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 9363894127..7da509b0e5 100644 --- a/pkg/repository/config/aws.go +++ b/pkg/repository/config/aws.go @@ -24,6 +24,7 @@ import ( goerr "errors" "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/credentials/stscreds" "github.com/aws/aws-sdk-go/aws/endpoints" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/s3/s3manager" @@ -33,8 +34,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 +57,48 @@ 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) != "" && os.Getenv(awsCredentialsFileEnvVar) == "" { 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.SharedConfigState = session.SharedConfigEnable } - creds := credentials.NewSharedCredentials(credentialsFile, "") - credValue, err := creds.Get() + sess, err := session.NewSessionWithOptions(opts) if err != nil { return nil, err } + if os.Getenv(awsRoleEnvVar) != "" { + sess.Config.WithCredentials(stscreds.NewCredentials(sess, os.Getenv(awsRoleEnvVar))) + } + + creds, err := sess.Config.Credentials.Get() - return &credValue, nil + 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)) }