From 28c5dc9fdabbdbe12c49e4effc640dede776adf4 Mon Sep 17 00:00:00 2001 From: Luis Davim Date: Thu, 3 Aug 2023 08:58:05 +0100 Subject: [PATCH] fix: load AWS config and assume role Signed-off-by: Luis Davim --- Dockerfile | 2 +- changelogs/unreleased/6598-aws_creds | 1 + pkg/repository/config/aws.go | 33 ++++++++++++++++++++++------ pkg/restic/common.go | 22 ++++++++++++++++++- 4 files changed, 49 insertions(+), 9 deletions(-) create mode 100644 changelogs/unreleased/6598-aws_creds diff --git a/Dockerfile b/Dockerfile index 2edd111ba8..587a0e0988 100644 --- a/Dockerfile +++ b/Dockerfile @@ -54,7 +54,7 @@ ARG TARGETARCH ARG TARGETVARIANT ARG RESTIC_VERSION -env CGO_ENABLED=0 \ +ENV CGO_ENABLED=0 \ GO111MODULE=on \ GOPROXY=${GOPROXY} \ GOOS=${TARGETOS} \ diff --git a/changelogs/unreleased/6598-aws_creds b/changelogs/unreleased/6598-aws_creds new file mode 100644 index 0000000000..74aee6ee1d --- /dev/null +++ b/changelogs/unreleased/6598-aws_creds @@ -0,0 +1 @@ +Fix how the AWS credentials are obtained from configuration diff --git a/pkg/repository/config/aws.go b/pkg/repository/config/aws.go index 2acbbb5025..c3a3f4dd8b 100644 --- a/pkg/repository/config/aws.go +++ b/pkg/repository/config/aws.go @@ -33,8 +33,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" ) // GetS3ResticEnvVars gets the environment variables that restic @@ -51,32 +56,46 @@ func GetS3ResticEnvVars(config map[string]string) (map[string]string, error) { result[awsProfileEnvVar] = profile } + // GetS3ResticEnvVars reads the AWS config, from files and envs + // if needed assumes the role and returns the session credentials + // setting these variables emulates what would happen for example when using kube2iam + if creds, err := GetS3Credentials(config); err == nil && creds != nil { + result[awsKeyIDEnvVar] = creds.AccessKeyID + result[awsSecretKeyEnvVar] = creds.SecretAccessKey + result[awsSessTokenEnvVar] = creds.SessionToken + result[awsCredentialsFileEnvVar] = "" + result[awsProfileEnvVar] = "" + result[awsConfigFileEnvVar] = "" + } + return result, nil } // 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) (*credentials.Value, error) { - if len(os.Getenv("AWS_ROLE_ARN")) > 0 { + if os.Getenv(awsRoleEnvVar) != "" { return nil, nil } + opts := session.Options{} credentialsFile := config[CredentialsFileKey] if credentialsFile == "" { credentialsFile = os.Getenv("AWS_SHARED_CREDENTIALS_FILE") } - - if credentialsFile == "" { - return nil, errors.New("missing credential file") + if credentialsFile != "" { + opts.SharedConfigFiles = append(opts.SharedConfigFiles, credentialsFile) + opts.SharedConfigState = session.SharedConfigEnable } - creds := credentials.NewSharedCredentials(credentialsFile, config[awsProfileKey]) - credValue, err := creds.Get() + sess, err := session.NewSessionWithOptions(opts) if err != nil { return nil, err } - return &credValue, nil + creds, err := sess.Config.Credentials.Get() + + return &creds, err } // GetAWSBucketRegion returns the AWS region that a bucket is in, or an error diff --git a/pkg/restic/common.go b/pkg/restic/common.go index 26eb2ef27a..a5bf05c447 100644 --- a/pkg/restic/common.go +++ b/pkg/restic/common.go @@ -20,6 +20,7 @@ import ( "fmt" "os" "strconv" + "strings" "time" "github.com/pkg/errors" @@ -71,11 +72,26 @@ func TempCACertFile(caCert []byte, bsl string, fs filesystem.Interface) (string, return name, nil } +// environ is a slice of strings representing the environment, in the form "key=value". +type environ []string + +// Unset a single environment variable. +func (e *environ) Unset(key string) { + for i := range *e { + if strings.HasPrefix((*e)[i], key+"=") { + (*e)[i] = (*e)[len(*e)-1] + *e = (*e)[:len(*e)-1] + break + } + } +} + // CmdEnv returns a list of environment variables (in the format var=val) that // should be used when running a restic command for a particular backend provider. // This list is the current environment, plus any provider-specific variables restic needs. func CmdEnv(backupLocation *velerov1api.BackupStorageLocation, credentialFileStore credentials.FileStore) ([]string, error) { - env := os.Environ() + var env environ + env = os.Environ() customEnv := map[string]string{} var err error @@ -113,6 +129,10 @@ func CmdEnv(backupLocation *velerov1api.BackupStorageLocation, credentialFileSto } for k, v := range customEnv { + env.Unset(k) + if v == "" { + continue + } env = append(env, fmt.Sprintf("%s=%s", k, v)) }