forked from TrueCloudLab/restic
feat: support AWS assume role
This commit is contained in:
parent
b2b7669ca0
commit
5ffb536aae
4 changed files with 108 additions and 33 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
|||
/.idea
|
||||
/restic
|
||||
/restic.exe
|
||||
/.vagrant
|
||||
|
|
14
changelog/unreleased/issue-4472
Normal file
14
changelog/unreleased/issue-4472
Normal file
|
@ -0,0 +1,14 @@
|
|||
Enhancement: Allow AWS Assume Role to be used for S3 backend
|
||||
|
||||
Previously only credentials discovered via the Minio Click discovery methods
|
||||
would be used to authenticate. However there are many circumstances where the
|
||||
discovered credentials have lower permissions and need to assume a specific role.
|
||||
|
||||
New Environment Variables:
|
||||
|
||||
- RESTIC_AWS_ASSUME_ROLE_ARN
|
||||
- RESTIC_AWS_ASSUME_ROLE_SESSION_NAME
|
||||
- RESTIC_AWS_ASSUME_ROLE_EXTERNAL_ID
|
||||
- RESTIC_AWS_ASSUME_ROLE_REGION (if need to override from us-east-1)
|
||||
- RESTIC_AWS_ASSUME_ROLE_POLICY
|
||||
- RESTIC_AWS_ASSUME_ROLE_STS_ENDPOINT
|
|
@ -628,6 +628,10 @@ environment variables. The following lists these environment variables:
|
|||
AWS_DEFAULT_REGION Amazon S3 default region
|
||||
AWS_PROFILE Amazon credentials profile (alternative to specifying key and region)
|
||||
AWS_SHARED_CREDENTIALS_FILE Location of the AWS CLI shared credentials file (default: ~/.aws/credentials)
|
||||
RESTIC_AWS_ASSUME_ROLE_ARN Amazon IAM Role ARN to assume using discovered credentials
|
||||
RESTIC_AWS_ASSUME_ROLE_SESSION_NAME Session Name to use with the role assumption
|
||||
RESTIC_AWS_ASSUME_ROLE_EXTERNAL_ID External ID to use with the role assumption
|
||||
RESTIC_AWS_ASSUME_ROLE_REGION Region to use for IAM calls for the role assumption
|
||||
|
||||
AZURE_ACCOUNT_NAME Account name for Azure
|
||||
AZURE_ACCOUNT_KEY Account key for Azure
|
||||
|
|
|
@ -51,40 +51,9 @@ func open(ctx context.Context, cfg Config, rt http.RoundTripper) (*Backend, erro
|
|||
minio.MaxRetry = int(cfg.MaxRetries)
|
||||
}
|
||||
|
||||
// Chains all credential types, in the following order:
|
||||
// - Static credentials provided by user
|
||||
// - AWS env vars (i.e. AWS_ACCESS_KEY_ID)
|
||||
// - Minio env vars (i.e. MINIO_ACCESS_KEY)
|
||||
// - AWS creds file (i.e. AWS_SHARED_CREDENTIALS_FILE or ~/.aws/credentials)
|
||||
// - Minio creds file (i.e. MINIO_SHARED_CREDENTIALS_FILE or ~/.mc/config.json)
|
||||
// - IAM profile based credentials. (performs an HTTP
|
||||
// call to a pre-defined endpoint, only valid inside
|
||||
// configured ec2 instances)
|
||||
creds := credentials.NewChainCredentials([]credentials.Provider{
|
||||
&credentials.EnvAWS{},
|
||||
&credentials.Static{
|
||||
Value: credentials.Value{
|
||||
AccessKeyID: cfg.KeyID,
|
||||
SecretAccessKey: cfg.Secret.Unwrap(),
|
||||
},
|
||||
},
|
||||
&credentials.EnvMinio{},
|
||||
&credentials.FileAWSCredentials{},
|
||||
&credentials.FileMinioClient{},
|
||||
&credentials.IAM{
|
||||
Client: &http.Client{
|
||||
Transport: http.DefaultTransport,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
c, err := creds.Get()
|
||||
creds, err := getCredentials(cfg)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "creds.Get")
|
||||
}
|
||||
|
||||
if c.SignerType == credentials.SignatureAnonymous {
|
||||
debug.Log("using anonymous access for %#v", cfg.Endpoint)
|
||||
return nil, errors.Wrap(err, "s3.getCredentials")
|
||||
}
|
||||
|
||||
options := &minio.Options{
|
||||
|
@ -125,6 +94,93 @@ func open(ctx context.Context, cfg Config, rt http.RoundTripper) (*Backend, erro
|
|||
return be, nil
|
||||
}
|
||||
|
||||
// getCredentials -- runs through the various credential types and returns the first one that works.
|
||||
// additionally if the user has specified a role to assume, it will do that as well.
|
||||
func getCredentials(cfg Config) (*credentials.Credentials, error) {
|
||||
// Chains all credential types, in the following order:
|
||||
// - Static credentials provided by user
|
||||
// - AWS env vars (i.e. AWS_ACCESS_KEY_ID)
|
||||
// - Minio env vars (i.e. MINIO_ACCESS_KEY)
|
||||
// - AWS creds file (i.e. AWS_SHARED_CREDENTIALS_FILE or ~/.aws/credentials)
|
||||
// - Minio creds file (i.e. MINIO_SHARED_CREDENTIALS_FILE or ~/.mc/config.json)
|
||||
// - IAM profile based credentials. (performs an HTTP
|
||||
// call to a pre-defined endpoint, only valid inside
|
||||
// configured ec2 instances)
|
||||
creds := credentials.NewChainCredentials([]credentials.Provider{
|
||||
&credentials.Static{
|
||||
Value: credentials.Value{
|
||||
AccessKeyID: cfg.KeyID,
|
||||
SecretAccessKey: cfg.Secret.Unwrap(),
|
||||
},
|
||||
},
|
||||
&credentials.EnvAWS{},
|
||||
&credentials.EnvMinio{},
|
||||
&credentials.FileAWSCredentials{},
|
||||
&credentials.FileMinioClient{},
|
||||
&credentials.IAM{
|
||||
Client: &http.Client{
|
||||
Transport: http.DefaultTransport,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
c, err := creds.Get()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "creds.Get")
|
||||
}
|
||||
|
||||
if c.SignerType == credentials.SignatureAnonymous {
|
||||
debug.Log("using anonymous access for %#v", cfg.Endpoint)
|
||||
}
|
||||
|
||||
roleArn := os.Getenv("RESTIC_AWS_ASSUME_ROLE_ARN")
|
||||
if roleArn != "" {
|
||||
// use the region provided by the configuration by default
|
||||
awsRegion := cfg.Region
|
||||
// allow the region to be overridden if for some reason it is required
|
||||
if len(os.Getenv("RESTIC_AWS_ASSUME_ROLE_REGION")) > 0 {
|
||||
awsRegion = os.Getenv("RESTIC_AWS_ASSUME_ROLE_REGION")
|
||||
}
|
||||
|
||||
sessionName := os.Getenv("RESTIC_AWS_ASSUME_ROLE_SESSION_NAME")
|
||||
externalID := os.Getenv("RESTIC_AWS_ASSUME_ROLE_EXTERNAL_ID")
|
||||
policy := os.Getenv("RESTIC_AWS_ASSUME_ROLE_POLICY")
|
||||
stsEndpoint := os.Getenv("RESTIC_AWS_ASSUME_ROLE_STS_ENDPOINT")
|
||||
|
||||
if stsEndpoint == "" {
|
||||
if len(awsRegion) > 0 {
|
||||
if strings.HasPrefix(awsRegion, "cn-") {
|
||||
stsEndpoint = "https://sts." + awsRegion + ".amazonaws.com.cn"
|
||||
} else {
|
||||
stsEndpoint = "https://sts." + awsRegion + ".amazonaws.com"
|
||||
}
|
||||
} else {
|
||||
stsEndpoint = "https://sts.amazonaws.com"
|
||||
}
|
||||
}
|
||||
|
||||
opts := credentials.STSAssumeRoleOptions{
|
||||
RoleARN: roleArn,
|
||||
AccessKey: c.AccessKeyID,
|
||||
SecretKey: c.SecretAccessKey,
|
||||
SessionToken: c.SessionToken,
|
||||
RoleSessionName: sessionName,
|
||||
ExternalID: externalID,
|
||||
Policy: policy,
|
||||
}
|
||||
if len(awsRegion) > 0 {
|
||||
opts.Location = awsRegion
|
||||
}
|
||||
|
||||
creds, err = credentials.NewSTSAssumeRole(stsEndpoint, opts)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "creds.AssumeRole")
|
||||
}
|
||||
}
|
||||
|
||||
return creds, nil
|
||||
}
|
||||
|
||||
// Open opens the S3 backend at bucket and region. The bucket is created if it
|
||||
// does not exist yet.
|
||||
func Open(ctx context.Context, cfg Config, rt http.RoundTripper) (backend.Backend, error) {
|
||||
|
|
Loading…
Reference in a new issue