From 56011c94435149dfd191fc69731d13ca82f99f19 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Tue, 3 May 2016 15:10:35 -0400 Subject: [PATCH] Refactor AWS credential code into a function that returns a static->env->instance chain --- builtin/logical/aws/client.go | 26 +++++++----- helper/awsutil/generate_providers.go | 62 ++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 10 deletions(-) create mode 100644 helper/awsutil/generate_providers.go diff --git a/builtin/logical/aws/client.go b/builtin/logical/aws/client.go index b0df673d5a..4ec18f806a 100644 --- a/builtin/logical/aws/client.go +++ b/builtin/logical/aws/client.go @@ -4,34 +4,40 @@ import ( "fmt" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/credentials" "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/go-cleanhttp" + "github.com/hashicorp/vault/helper/awsutil" "github.com/hashicorp/vault/logical" ) func getRootConfig(s logical.Storage) (*aws.Config, error) { + credConfig := &awsutil.AWSCredentialsConfig{} + entry, err := s.Get("config/root") if err != nil { return nil, err } - if entry == nil { - return nil, fmt.Errorf( - "root credentials haven't been configured. Please configure\n" + - "them at the 'config/root' endpoint") + if entry != nil { + var config rootConfig + if err := entry.DecodeJSON(&config); err != nil { + return nil, fmt.Errorf("error reading root configuration: %s", err) + } + + credConfig.AccessKey = config.AccessKey + credConfig.SecretKey = config.SecretKey + credConfig.Region = config.Region } - var config rootConfig - if err := entry.DecodeJSON(&config); err != nil { - return nil, fmt.Errorf("error reading root configuration: %s", err) + creds, err := awsutil.GenerateCredentialChain(credConfig) + if err != nil { + return nil, err } - creds := credentials.NewStaticCredentials(config.AccessKey, config.SecretKey, "") return &aws.Config{ Credentials: creds, - Region: aws.String(config.Region), + Region: aws.String(credConfig.Region), HTTPClient: cleanhttp.DefaultClient(), }, nil } diff --git a/helper/awsutil/generate_providers.go b/helper/awsutil/generate_providers.go new file mode 100644 index 0000000000..37c9051009 --- /dev/null +++ b/helper/awsutil/generate_providers.go @@ -0,0 +1,62 @@ +package awsutil + +import ( + "fmt" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds" + "github.com/aws/aws-sdk-go/aws/ec2metadata" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/hashicorp/go-cleanhttp" +) + +type AWSCredentialsConfig struct { + AccessKey string + SecretKey string + Region string +} + +func GenerateCredentialChain(config *AWSCredentialsConfig) (*credentials.Credentials, error) { + if config == nil { + return nil, fmt.Errorf("nil configuration provided") + } + + var providers []credentials.Provider + + switch { + case config.AccessKey != "" && config.SecretKey != "": + // Add the static credential provider + providers = append(providers, &credentials.StaticProvider{ + Value: credentials.Value{ + AccessKeyID: config.AccessKey, + SecretAccessKey: config.SecretKey, + }}) + case config.AccessKey == "" && config.AccessKey == "": + // Attempt to get credentials from the IAM instance role below + + default: // Have one or the other but not both and not neither + return nil, fmt.Errorf( + "static AWS client credentials haven't been properly configured (the access key or secret key were provided but not both)") + } + + // Add the environment credential provider + providers = append(providers, &credentials.EnvProvider{}) + + // Add the instance metadata role provider + // Create the credentials required to access the API. + providers = append(providers, &ec2rolecreds.EC2RoleProvider{ + Client: ec2metadata.New(session.New(&aws.Config{ + Region: aws.String(config.Region), + HTTPClient: cleanhttp.DefaultClient(), + })), + ExpiryWindow: 15, + }) + + creds := credentials.NewChainCredentials(providers) + if creds == nil { + return nil, fmt.Errorf("could not compile valid credential providers from static config, environemnt, or instance metadata") + } + + return creds, nil +}