mirror of
https://github.com/mulgadc/spinifex.git
synced 2026-10-22 20:26:44 +00:00
31eb3bc672
ec2rolecreds implements aws.HandleFailRefreshCredentialsCacheStrategy, which aws.CredentialsCache prefers over returning the refresh error. When the cached credential is expired or within five minutes of expiring and the refresh fails, it hands back the same access key, secret and session token with Expires pushed a random five to fifteen minutes out, reported as a success. Every later refresh fails the same way, so ecs-agent signs with a credential the control plane has already rejected, indefinitely, and nothing surfaces it. The cache picks its strategies by asserting on the provider's dynamic type, so returning aws.CredentialsProvider from NewProvider does not disarm it. Wrap the provider in a type whose method set omits HandleFailToRefresh, and the cache falls through to returning the error. AdjustExpiresBy is forwarded on purpose: it only fires under an ExpiryWindow, which no caller sets today, but dropping it silently would change behaviour for whoever sets one. Fixed in imdscreds rather than at the one exposed call site, so a future caller that wraps this provider in a cache is safe by construction. ecr-credential-provider was never affected: it calls Retrieve uncached. Two comments claimed the hazard was ec2rolecreds capping Expires to an hour. That cap only ever shortens a too-far-future expiry and is unrelated.
42 lines
1.3 KiB
Go
42 lines
1.3 KiB
Go
package credentials
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
|
|
"github.com/aws/aws-sdk-go-v2/aws"
|
|
"github.com/mulgadc/spinifex/internal/imdscreds"
|
|
)
|
|
|
|
// IMDSProvider fetches instance-role credentials from IMDS via the AWS SDK's
|
|
// EC2 IMDS client and ec2rolecreds provider, cached with aws.CredentialsCache,
|
|
// which refetches once the real expiry passes. A failed refresh is an error
|
|
// here, not an extended expiry. Safe for concurrent use.
|
|
type IMDSProvider struct {
|
|
cache *aws.CredentialsCache
|
|
}
|
|
|
|
var _ CredentialsProvider = (*IMDSProvider)(nil)
|
|
|
|
// NewIMDSProvider builds a provider against the given IMDS base URL (e.g.
|
|
// "http://169.254.169.254/latest"). A nil client uses the SDK IMDS client's
|
|
// default HTTP client.
|
|
func NewIMDSProvider(client *http.Client, base string) *IMDSProvider {
|
|
provider := imdscreds.NewProvider(client, base)
|
|
return &IMDSProvider{cache: aws.NewCredentialsCache(provider)}
|
|
}
|
|
|
|
// Retrieve returns cached credentials when still valid, else fetches fresh ones.
|
|
func (p *IMDSProvider) Retrieve(ctx context.Context) (Credentials, error) {
|
|
creds, err := p.cache.Retrieve(ctx)
|
|
if err != nil {
|
|
return Credentials{}, err
|
|
}
|
|
return Credentials{
|
|
AccessKeyID: creds.AccessKeyID,
|
|
SecretAccessKey: creds.SecretAccessKey,
|
|
SessionToken: creds.SessionToken,
|
|
Expiration: creds.Expires,
|
|
}, nil
|
|
}
|