From 30728c248ceff71773c2e92e31a92caf8ee8eb49 Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Tue, 30 Jan 2024 15:52:21 +0800 Subject: [PATCH] Respect the config in BSL when IRSA is configured This commit makes sure when kopia connects to the repository the crendentials file specified in BSL.spec.config has the higher priority over Pod Environment credentials when IRSA is configured. Signed-off-by: Daniel Jiang --- changelogs/unreleased/7374-reasonerjt | 1 + go.mod | 4 +- pkg/repository/config/aws.go | 72 +++++++++++++++++++++++-- pkg/repository/provider/unified_repo.go | 2 +- 4 files changed, 71 insertions(+), 8 deletions(-) create mode 100644 changelogs/unreleased/7374-reasonerjt diff --git a/changelogs/unreleased/7374-reasonerjt b/changelogs/unreleased/7374-reasonerjt new file mode 100644 index 000000000..ca09bda51 --- /dev/null +++ b/changelogs/unreleased/7374-reasonerjt @@ -0,0 +1 @@ +Respect and use `credentialsFile` specified in BSL.spec.config when IRSA is configured over Velero Pod Environment credentials \ No newline at end of file diff --git a/go.mod b/go.mod index 9e2f41bd6..33717f919 100644 --- a/go.mod +++ b/go.mod @@ -18,9 +18,11 @@ require ( github.com/Azure/go-autorest/autorest/to v0.3.0 github.com/aws/aws-sdk-go-v2 v1.24.1 github.com/aws/aws-sdk-go-v2/config v1.26.3 + github.com/aws/aws-sdk-go-v2/credentials v1.16.14 github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.11 github.com/aws/aws-sdk-go-v2/service/ec2 v1.143.0 github.com/aws/aws-sdk-go-v2/service/s3 v1.48.0 + github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 github.com/bombsimon/logrusr/v3 v3.0.0 github.com/evanphx/json-patch v5.6.0+incompatible github.com/fatih/color v1.15.0 @@ -83,7 +85,6 @@ require ( github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.16.14 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 // indirect @@ -95,7 +96,6 @@ require ( github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.10 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.18.6 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.6 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 // indirect github.com/aws/smithy-go v1.19.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect diff --git a/pkg/repository/config/aws.go b/pkg/repository/config/aws.go index 6cb87f0a6..567fec54c 100644 --- a/pkg/repository/config/aws.go +++ b/pkg/repository/config/aws.go @@ -19,7 +19,13 @@ package config import ( "context" + "fmt" "os" + "time" + + "github.com/aws/aws-sdk-go-v2/credentials" + "github.com/aws/aws-sdk-go-v2/credentials/stscreds" + "github.com/aws/aws-sdk-go-v2/service/sts" "github.com/aws/aws-sdk-go-v2/aws" awsconfig "github.com/aws/aws-sdk-go-v2/config" @@ -31,13 +37,13 @@ import ( const ( // AWS specific environment variable awsProfileEnvVar = "AWS_PROFILE" - awsRoleEnvVar = "AWS_ROLE_ARN" awsKeyIDEnvVar = "AWS_ACCESS_KEY_ID" awsSecretKeyEnvVar = "AWS_SECRET_ACCESS_KEY" awsSessTokenEnvVar = "AWS_SESSION_TOKEN" awsProfileKey = "profile" awsCredentialsFileEnvVar = "AWS_SHARED_CREDENTIALS_FILE" awsConfigFileEnvVar = "AWS_CONFIG_FILE" + awsDefaultProfile = "default" ) // GetS3ResticEnvVars gets the environment variables that restic @@ -72,10 +78,6 @@ func GetS3ResticEnvVars(config map[string]string) (map[string]string, error) { // GetS3Credentials gets the S3 credential values according to the information // of the provided config or the system's environment variables func GetS3Credentials(config map[string]string) (*aws.Credentials, error) { - if os.Getenv(awsRoleEnvVar) != "" { - return nil, nil - } - var opts []func(*awsconfig.LoadOptions) error credentialsFile := config[CredentialsFileKey] if credentialsFile == "" { @@ -93,6 +95,25 @@ func GetS3Credentials(config map[string]string) (*aws.Credentials, error) { if err != nil { return nil, err } + + if credentialsFile != "" && os.Getenv("AWS_WEB_IDENTITY_TOKEN_FILE") != "" && os.Getenv("AWS_ROLE_ARN") != "" { + // Reset the config to use the credentials from the credentials/config file + profile := config[awsProfileKey] + if profile == "" { + profile = awsDefaultProfile + } + sfp, err := awsconfig.LoadSharedConfigProfile(context.Background(), profile, func(o *awsconfig.LoadSharedConfigOptions) { + o.ConfigFiles = []string{credentialsFile} + o.CredentialsFiles = []string{credentialsFile} + }) + if err != nil { + return nil, fmt.Errorf("error loading config profile '%s': %v", profile, err) + } + if err := resolveCredsFromProfile(&cfg, &sfp); err != nil { + return nil, fmt.Errorf("error resolving creds from profile '%s': %v", profile, err) + } + } + creds, err := cfg.Credentials.Retrieve(context.Background()) return &creds, err @@ -115,3 +136,44 @@ func GetAWSBucketRegion(bucket string) (string, error) { } return region, nil } + +func resolveCredsFromProfile(cfg *aws.Config, sharedConfig *awsconfig.SharedConfig) error { + var err error + switch { + case sharedConfig.Source != nil: + // Assume IAM role with credentials source from a different profile. + err = resolveCredsFromProfile(cfg, sharedConfig.Source) + case sharedConfig.Credentials.HasKeys(): + // Static Credentials from Shared Config/Credentials file. + cfg.Credentials = credentials.StaticCredentialsProvider{ + Value: sharedConfig.Credentials, + } + } + if err != nil { + return err + } + if len(sharedConfig.RoleARN) > 0 { + credsFromAssumeRole(cfg, sharedConfig) + } + return nil +} + +func credsFromAssumeRole(cfg *aws.Config, sharedCfg *awsconfig.SharedConfig) { + optFns := []func(*stscreds.AssumeRoleOptions){ + func(options *stscreds.AssumeRoleOptions) { + options.RoleSessionName = sharedCfg.RoleSessionName + if sharedCfg.RoleDurationSeconds != nil { + if *sharedCfg.RoleDurationSeconds/time.Minute > 15 { + options.Duration = *sharedCfg.RoleDurationSeconds + } + } + if len(sharedCfg.ExternalID) > 0 { + options.ExternalID = aws.String(sharedCfg.ExternalID) + } + if len(sharedCfg.MFASerial) != 0 { + options.SerialNumber = aws.String(sharedCfg.MFASerial) + } + }, + } + cfg.Credentials = stscreds.NewAssumeRoleProvider(sts.NewFromConfig(*cfg), sharedCfg.RoleARN, optFns...) +} diff --git a/pkg/repository/provider/unified_repo.go b/pkg/repository/provider/unified_repo.go index 76ae36351..e3422e693 100644 --- a/pkg/repository/provider/unified_repo.go +++ b/pkg/repository/provider/unified_repo.go @@ -505,7 +505,7 @@ func getStorageVariables(backupLocation *velerov1api.BackupStorageLocation, repo } s3URL = url.Host - disableTLS = (url.Scheme == "http") + disableTLS = url.Scheme == "http" } result[udmrepo.StoreOptionS3Endpoint] = strings.Trim(s3URL, "/")