forked from TrueCloudLab/restic
Merge pull request #4474 from ekristen/aws-assume-role
Allow AWS Assume Role
This commit is contained in:
commit
77434c6e2b
4 changed files with 112 additions and 33 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
||||||
|
/.idea
|
||||||
/restic
|
/restic
|
||||||
/restic.exe
|
/restic.exe
|
||||||
/.vagrant
|
/.vagrant
|
||||||
|
|
18
changelog/unreleased/issue-4472
Normal file
18
changelog/unreleased/issue-4472
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
Enhancement: Allow AWS Assume Role to be used for S3 backend
|
||||||
|
|
||||||
|
Previously only credentials discovered via the Minio discovery methods
|
||||||
|
were used to authenticate.
|
||||||
|
|
||||||
|
However, there are many circumstances where the discovered credentials have
|
||||||
|
lower permissions and need to assume a specific role. This is now possible
|
||||||
|
using the following 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 (defaults to us-east-1)
|
||||||
|
- RESTIC_AWS_ASSUME_ROLE_POLICY
|
||||||
|
- RESTIC_AWS_ASSUME_ROLE_STS_ENDPOINT
|
||||||
|
|
||||||
|
https://github.com/restic/restic/issues/4472
|
||||||
|
https://github.com/restic/restic/pull/4474
|
|
@ -628,6 +628,12 @@ environment variables. The following lists these environment variables:
|
||||||
AWS_DEFAULT_REGION Amazon S3 default region
|
AWS_DEFAULT_REGION Amazon S3 default region
|
||||||
AWS_PROFILE Amazon credentials profile (alternative to specifying key and 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)
|
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_POLICY Inline Amazion IAM session policy
|
||||||
|
RESTIC_AWS_ASSUME_ROLE_REGION Region to use for IAM calls for the role assumption (default: us-east-1)
|
||||||
|
RESTIC_AWS_ASSUME_ROLE_STS_ENDPOINT URL to the STS endpoint (default is determined based on RESTIC_AWS_ASSUME_ROLE_REGION). You generally do not need to set this, advanced use only.
|
||||||
|
|
||||||
AZURE_ACCOUNT_NAME Account name for Azure
|
AZURE_ACCOUNT_NAME Account name for Azure
|
||||||
AZURE_ACCOUNT_KEY Account key 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)
|
minio.MaxRetry = int(cfg.MaxRetries)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chains all credential types, in the following order:
|
creds, err := getCredentials(cfg)
|
||||||
// - 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()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "creds.Get")
|
return nil, errors.Wrap(err, "s3.getCredentials")
|
||||||
}
|
|
||||||
|
|
||||||
if c.SignerType == credentials.SignatureAnonymous {
|
|
||||||
debug.Log("using anonymous access for %#v", cfg.Endpoint)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
options := &minio.Options{
|
options := &minio.Options{
|
||||||
|
@ -125,6 +94,91 @@ func open(ctx context.Context, cfg Config, rt http.RoundTripper) (*Backend, erro
|
||||||
return be, nil
|
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.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()
|
||||||
|
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 os.Getenv("RESTIC_AWS_ASSUME_ROLE_REGION") != "" {
|
||||||
|
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 awsRegion != "" {
|
||||||
|
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,
|
||||||
|
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
|
// Open opens the S3 backend at bucket and region. The bucket is created if it
|
||||||
// does not exist yet.
|
// does not exist yet.
|
||||||
func Open(ctx context.Context, cfg Config, rt http.RoundTripper) (backend.Backend, error) {
|
func Open(ctx context.Context, cfg Config, rt http.RoundTripper) (backend.Backend, error) {
|
||||||
|
|
Loading…
Reference in a new issue