5e6f8904d8
* Add AWS Secret Engine Root Credential Rotation This allows the AWS Secret Engine to rotate its credentials used to access AWS. This will only work when the AWS Secret Engine has been provided explicit IAM credentials via the config/root endpoint, and further, when the IAM credentials provided are the only access key on the IAM user associated wtih the access key (because AWS allows a maximum of 2 access keys per user). Fixes #4385 * Add test for AWS root credential rotation Also fix a typo in the root credential rotation code * Add docs for AWS root rotation * Add locks around reading and writing config/root And wire the backend up in a bunch of places so the config can get the lock * Respond to PR feedback * Fix casing in error messages * Fix merge errors * Fix locking bugs
98 lines
2.5 KiB
Go
98 lines
2.5 KiB
Go
package aws
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/aws/aws-sdk-go/aws"
|
|
"github.com/aws/aws-sdk-go/aws/session"
|
|
"github.com/aws/aws-sdk-go/service/iam"
|
|
"github.com/aws/aws-sdk-go/service/sts"
|
|
"github.com/hashicorp/errwrap"
|
|
"github.com/hashicorp/go-cleanhttp"
|
|
"github.com/hashicorp/vault/helper/awsutil"
|
|
"github.com/hashicorp/vault/logical"
|
|
)
|
|
|
|
// NOTE: The caller is required to ensure that b.clientMutex is at least read locked
|
|
func getRootConfig(ctx context.Context, s logical.Storage, clientType string) (*aws.Config, error) {
|
|
credsConfig := &awsutil.CredentialsConfig{}
|
|
var endpoint string
|
|
var maxRetries int = aws.UseServiceDefaultRetries
|
|
|
|
entry, err := s.Get(ctx, "config/root")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if entry != nil {
|
|
var config rootConfig
|
|
if err := entry.DecodeJSON(&config); err != nil {
|
|
return nil, errwrap.Wrapf("error reading root configuration: {{err}}", err)
|
|
}
|
|
|
|
credsConfig.AccessKey = config.AccessKey
|
|
credsConfig.SecretKey = config.SecretKey
|
|
credsConfig.Region = config.Region
|
|
maxRetries = config.MaxRetries
|
|
switch {
|
|
case clientType == "iam" && config.IAMEndpoint != "":
|
|
endpoint = *aws.String(config.IAMEndpoint)
|
|
case clientType == "sts" && config.STSEndpoint != "":
|
|
endpoint = *aws.String(config.STSEndpoint)
|
|
}
|
|
}
|
|
|
|
if credsConfig.Region == "" {
|
|
credsConfig.Region = os.Getenv("AWS_REGION")
|
|
if credsConfig.Region == "" {
|
|
credsConfig.Region = os.Getenv("AWS_DEFAULT_REGION")
|
|
if credsConfig.Region == "" {
|
|
credsConfig.Region = "us-east-1"
|
|
}
|
|
}
|
|
}
|
|
|
|
credsConfig.HTTPClient = cleanhttp.DefaultClient()
|
|
|
|
creds, err := credsConfig.GenerateCredentialChain()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &aws.Config{
|
|
Credentials: creds,
|
|
Region: aws.String(credsConfig.Region),
|
|
Endpoint: &endpoint,
|
|
HTTPClient: cleanhttp.DefaultClient(),
|
|
MaxRetries: aws.Int(maxRetries),
|
|
}, nil
|
|
}
|
|
|
|
func nonCachedClientIAM(ctx context.Context, s logical.Storage) (*iam.IAM, error) {
|
|
awsConfig, err := getRootConfig(ctx, s, "iam")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
client := iam.New(session.New(awsConfig))
|
|
|
|
if client == nil {
|
|
return nil, fmt.Errorf("could not obtain iam client")
|
|
}
|
|
return client, nil
|
|
}
|
|
|
|
func nonCachedClientSTS(ctx context.Context, s logical.Storage) (*sts.STS, error) {
|
|
awsConfig, err := getRootConfig(ctx, s, "sts")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
client := sts.New(session.New(awsConfig))
|
|
|
|
if client == nil {
|
|
return nil, fmt.Errorf("could not obtain sts client")
|
|
}
|
|
return client, nil
|
|
}
|