diff --git a/changelogs/unreleased/7374-reasonerjt b/changelogs/unreleased/7374-reasonerjt new file mode 100644 index 0000000000..ca09bda51f --- /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 9e2f41bd64..33717f9195 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 6cb87f0a62..567fec54c3 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 76ae36351f..e3422e693b 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, "/")