pkg/curatedpackages/regional_registry.go (134 lines of code) (raw):

package curatedpackages import ( "context" "fmt" "io" "net/http" "os" "strings" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/credentials" "github.com/aws/aws-sdk-go-v2/service/ecr" ) // RegistryAccessTestParams struct for testing resigtry access. type RegistryAccessTestParams struct { AccessKey string Secret string SessionToken string Region string AwsConfig string Registry string } // RegistryAccessTester test if AWS credentials has valid permission to access an ECR registry. type RegistryAccessTester interface { Test(ctx context.Context, params RegistryAccessTestParams) error } // DefaultRegistryAccessTester the default implementation of RegistryAccessTester. type DefaultRegistryAccessTester struct{} // Test if the AWS static credential or sharedConfig has valid permission to access an ECR registry. func (r *DefaultRegistryAccessTester) Test(ctx context.Context, params RegistryAccessTestParams) (err error) { authTokenProvider := &DefaultRegistryAuthTokenProvider{} var authToken string if len(params.AwsConfig) > 0 { authToken, err = authTokenProvider.GetTokenByAWSConfig(ctx, params.AwsConfig) } else { authToken, err = authTokenProvider.GetTokenByAWSKeySecret(ctx, params.AccessKey, params.Secret, params.SessionToken, params.Region) } if err != nil { return err } return TestRegistryWithAuthToken(authToken, params.Registry, http.DefaultClient.Do) } // TestRegistryWithAuthToken test if the registry can be acccessed with auth token. func TestRegistryWithAuthToken(authToken, registry string, do Do) error { manifestPath := "/v2/eks-anywhere-packages/manifests/latest" req, err := http.NewRequest("GET", "https://"+registry+manifestPath, nil) if err != nil { return err } req.Header.Add("Authorization", "Basic "+authToken) resp2, err := do(req) if err != nil { return err } bodyBytes, err := io.ReadAll(resp2.Body) // 404 means the IAM policy is good, so 404 is good here if resp2.StatusCode != 200 && resp2.StatusCode != 404 { return fmt.Errorf("%s\n, %v", string(bodyBytes), err) } return nil } // GetRegionalRegistry get the regional registry corresponding to defaultRegistry in a specific region. func GetRegionalRegistry(defaultRegistry, region string) string { if strings.Contains(defaultRegistry, devRegionalPublicRegistryAlias) { return devRegionalPrivateRegistryURI } if strings.Contains(defaultRegistry, stagingPublicRegistryAlias) { return stagingRegionalPrivateRegistryURI } return prodRegionalPrivateRegistryURIByRegion[region] } // RegistryAuthTokenProvider provides auth token for registry access. type RegistryAuthTokenProvider interface { GetTokenByAWSConfig(ctx context.Context, awsConfig string) (string, error) GetTokenByAWSKeySecret(ctx context.Context, key, secret, region string) (string, error) } // DefaultRegistryAuthTokenProvider provides auth token for AWS ECR registry access. type DefaultRegistryAuthTokenProvider struct{} // GetTokenByAWSConfig get auth token by AWS config. func (d *DefaultRegistryAuthTokenProvider) GetTokenByAWSConfig(ctx context.Context, awsConfig string) (string, error) { cfg, err := ParseAWSConfig(ctx, awsConfig) if err != nil { return "", err } return getAuthorizationToken(*cfg) } // ParseAWSConfig parse AWS config from string. func ParseAWSConfig(ctx context.Context, awsConfig string) (*aws.Config, error) { file, err := os.CreateTemp("", "eksa-temp-aws-config-*") if err != nil { return nil, err } if _, err := file.Write([]byte(awsConfig)); err != nil { return nil, err } defer os.Remove(file.Name()) cfg, err := config.LoadDefaultConfig(ctx, config.WithSharedConfigFiles([]string{file.Name()}), ) if err != nil { return nil, err } return &cfg, nil } // GetAWSConfigFromKeySecret get AWS config from key, secret and region. func GetAWSConfigFromKeySecret(ctx context.Context, key, secret, sessionToken, region string) (*aws.Config, error) { cfg, err := config.LoadDefaultConfig(ctx, config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(key, secret, sessionToken)), config.WithRegion(region), ) if err != nil { return nil, err } return &cfg, nil } // GetTokenByAWSKeySecret get auth token by AWS key and secret. func (d *DefaultRegistryAuthTokenProvider) GetTokenByAWSKeySecret(ctx context.Context, key, secret, sessionToken, region string) (string, error) { cfg, err := GetAWSConfigFromKeySecret(ctx, key, secret, sessionToken, region) if err != nil { return "", err } return getAuthorizationToken(*cfg) } func getAuthorizationToken(cfg aws.Config) (string, error) { ecrClient := ecr.NewFromConfig(cfg) out, err := ecrClient.GetAuthorizationToken(context.Background(), &ecr.GetAuthorizationTokenInput{}) if err != nil { return "", fmt.Errorf("ecrClient cannot get authorization token: %w", err) } authToken := out.AuthorizationData[0].AuthorizationToken return *authToken, nil } // Do is a function type that takes a http request and returns a http response. type Do func(req *http.Request) (*http.Response, error) // TestRegistryAccessWithAWSConfig test if the AWS config has valid permission to access container registry. func TestRegistryAccessWithAWSConfig(ctx context.Context, awsConfig, registry string, tokenProvider RegistryAuthTokenProvider, do Do) error { token, err := tokenProvider.GetTokenByAWSConfig(ctx, awsConfig) if err != nil { return err } return TestRegistryWithAuthToken(token, registry, do) } // TestRegistryAccessWithAWSKeySecret test if the AWS key and secret has valid permission to access container registry. func TestRegistryAccessWithAWSKeySecret(ctx context.Context, key, secret, region, registry string, tokenProvider RegistryAuthTokenProvider, do Do) error { token, err := tokenProvider.GetTokenByAWSKeySecret(ctx, key, secret, region) if err != nil { return err } return TestRegistryWithAuthToken(token, registry, do) }