From 8b84c162690bf656e15450a01b2645fd54178a03 Mon Sep 17 00:00:00 2001 From: Jon Johnson Date: Mon, 31 Jul 2023 13:38:45 -0700 Subject: [PATCH] Add client-side rate limiting for rekor ops (#54) We are hitting 429s with rekor, which only allows 500 requests per minute. Signed-off-by: Jon Johnson --- go.mod | 2 ++ go.sum | 3 +++ internal/provider/provider.go | 7 +++++++ internal/provider/resource_attest.go | 3 +++ internal/provider/resource_sign.go | 3 +++ 5 files changed, 18 insertions(+) diff --git a/go.mod b/go.mod index 41960b31..fd4837ef 100644 --- a/go.mod +++ b/go.mod @@ -26,6 +26,7 @@ require ( github.com/sigstore/rekor v1.2.2 github.com/sigstore/sigstore v1.7.1 github.com/transparency-dev/merkle v0.0.2 + go.uber.org/ratelimit v0.3.0 golang.org/x/oauth2 v0.10.0 ) @@ -92,6 +93,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/sts v1.19.2 // indirect github.com/aws/smithy-go v1.13.5 // indirect github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20221027043306-dc425bc05c64 // indirect + github.com/benbjohnson/clock v1.3.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.0 // indirect github.com/blang/semver v3.5.1+incompatible // indirect diff --git a/go.sum b/go.sum index 33f9c2b1..572a0db9 100644 --- a/go.sum +++ b/go.sum @@ -222,6 +222,7 @@ github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20221027043306-dc425bc05c64 h1:J+6PUCOmCU9A2iZDGsTGxdycxybJMp+fbFEMWWsQUgg= github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20221027043306-dc425bc05c64/go.mod h1:oqbjAk8VeItfKctyahGuAyU61z4d0Fi1gHmlWjHWsMM= github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= +github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -1040,6 +1041,8 @@ go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/ratelimit v0.3.0 h1:IdZd9wqvFXnvLvSEBo0KPcGfkoBGNkpTHlrE3Rcjkjw= +go.uber.org/ratelimit v0.3.0/go.mod h1:So5LG7CV1zWpY1sHe+DXTJqQvOx+FFPFaAs2SnoyBaI= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 1699778b..2fe7973f 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -4,6 +4,7 @@ import ( "context" "net/url" "sync" + "time" "github.com/chainguard-dev/terraform-provider-cosign/internal/secant/fulcio" "github.com/google/go-containerregistry/pkg/authn" @@ -16,6 +17,7 @@ import ( "github.com/sigstore/fulcio/pkg/api" rclient "github.com/sigstore/rekor/pkg/client" "github.com/sigstore/rekor/pkg/generated/client" + "go.uber.org/ratelimit" ) // Ensure Provider satisfies various provider interfaces. @@ -42,6 +44,9 @@ type ProviderOpts struct { // Keyed off rekor URL. rekorClients map[string]*client.Rekor + + // Client-side rate limiting to avoid rekor 429s. + limiter ratelimit.Limiter } func (p *ProviderOpts) rekorClient(rekorUrl string) (*client.Rekor, error) { @@ -129,6 +134,8 @@ func (p *Provider) Configure(ctx context.Context, req provider.ConfigureRequest, oidc: &oidcProvider{}, signers: map[string]*fulcio.SignerVerifier{}, rekorClients: map[string]*client.Rekor{}, + // A little bird told me that rekor allows 500 requests per minute. + limiter: ratelimit.New(500, ratelimit.Per(time.Minute), ratelimit.WithoutSlack), } // Make provider opts available to resources and data sources. diff --git a/internal/provider/resource_attest.go b/internal/provider/resource_attest.go index 5156b1fa..d75362d0 100644 --- a/internal/provider/resource_attest.go +++ b/internal/provider/resource_attest.go @@ -236,6 +236,9 @@ func (r *AttestResource) doAttest(ctx context.Context, data *AttestResourceModel return "", nil, fmt.Errorf("creating rekor client: %w", err) } + // Avoid hitting rekor rate limits. + r.popts.limiter.Take() + ctx, cancel := context.WithTimeout(ctx, options.DefaultTimeout) defer cancel() diff --git a/internal/provider/resource_sign.go b/internal/provider/resource_sign.go index a38d17fa..90146c98 100644 --- a/internal/provider/resource_sign.go +++ b/internal/provider/resource_sign.go @@ -121,6 +121,9 @@ func (r *SignResource) doSign(ctx context.Context, data *SignResourceModel) (str return "", nil, fmt.Errorf("creating rekor client: %w", err) } + // Avoid hitting rekor rate limits. + r.popts.limiter.Take() + ctx, cancel := context.WithTimeout(ctx, options.DefaultTimeout) defer cancel()