Skip to content

Commit

Permalink
Support standard AWS config in the S3 remote backend.
Browse files Browse the repository at this point in the history
  • Loading branch information
johnrengelman committed Feb 23, 2016
1 parent 8677c1d commit 6deabf9
Showing 1 changed file with 72 additions and 20 deletions.
92 changes: 72 additions & 20 deletions state/remote/s3.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,21 @@ import (
"fmt"
"io"
"log"
"net/http"
"os"
"strconv"
"strings"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/credentials"
awsCredentials "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/aws/aws-sdk-go/service/s3"
"github.com/hashicorp/go-cleanhttp"
"github.com/hashicorp/go-multierror"
)

func s3Factory(conf map[string]string) (Client, error) {
Expand Down Expand Up @@ -60,29 +64,24 @@ func s3Factory(conf map[string]string) (Client, error) {
}
kmsKeyID := conf["kms_key_id"]

accessKeyId := conf["access_key"]
secretAccessKey := conf["secret_key"]

credentialsProvider := credentials.NewChainCredentials([]credentials.Provider{
&credentials.StaticProvider{Value: credentials.Value{
AccessKeyID: accessKeyId,
SecretAccessKey: secretAccessKey,
SessionToken: "",
}},
&credentials.EnvProvider{},
&credentials.SharedCredentialsProvider{Filename: "", Profile: ""},
&ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(session.New())},
})

// Make sure we got some sort of working credentials.
_, err := credentialsProvider.Get()
var errs []error
creds := getCreds(conf["access_key"], conf["secret_key"], conf["token"], conf["profile"], conf["shared_credentials_file"])
// Call Get to check for credential provider. If nothing found, we'll get an
// error, and we can present it nicely to the user
_, err := creds.Get()
if err != nil {
return nil, fmt.Errorf("Unable to determine AWS credentials. Set the AWS_ACCESS_KEY_ID and "+
"AWS_SECRET_ACCESS_KEY environment variables.\n(error was: %s)", err)
if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NoCredentialProviders" {
errs = append(errs, fmt.Errorf(`No valid credential sources found for AWS Provider.
Please see https://terraform.io/docs/providers/aws/index.html for more information on
providing credentials for the AWS Provider`))
} else {
errs = append(errs, fmt.Errorf("Error loading credentials for AWS Provider: %s", err))
}
return nil, &multierror.Error{Errors: errs}
}

awsConfig := &aws.Config{
Credentials: credentialsProvider,
Credentials: creds,
Endpoint: aws.String(endpoint),
Region: aws.String(regionName),
HTTPClient: cleanhttp.DefaultClient(),
Expand Down Expand Up @@ -188,3 +187,56 @@ func (c *S3Client) Delete() error {

return err
}

// This function is responsible for reading credentials from the
// environment in the case that they're not explicitly specified
// in the Terraform configuration.
func getCreds(key, secret, token, profile, credsfile string) *awsCredentials.Credentials {
// build a chain provider, lazy-evaulated by aws-sdk
providers := []awsCredentials.Provider{
&awsCredentials.StaticProvider{Value: awsCredentials.Value{
AccessKeyID: key,
SecretAccessKey: secret,
SessionToken: token,
}},
&awsCredentials.EnvProvider{},
&awsCredentials.SharedCredentialsProvider{
Filename: credsfile,
Profile: profile,
},
}

// We only look in the EC2 metadata API if we can connect
// to the metadata service within a reasonable amount of time
metadataURL := os.Getenv("AWS_METADATA_URL")
if metadataURL == "" {
metadataURL = "http://169.254.169.254:80/latest"
}
c := http.Client{
Timeout: 100 * time.Millisecond,
}

r, err := c.Get(metadataURL)
// Flag to determine if we should add the EC2Meta data provider. Default false
var useIAM bool
if err == nil {
// AWS will add a "Server: EC2ws" header value for the metadata request. We
// check the headers for this value to ensure something else didn't just
// happent to be listening on that IP:Port
if r.Header["Server"] != nil && strings.Contains(r.Header["Server"][0], "EC2") {
useIAM = true
}
}

if useIAM {
log.Printf("[DEBUG] EC2 Metadata service found, adding EC2 Role Credential Provider")
providers = append(providers, &ec2rolecreds.EC2RoleProvider{
Client: ec2metadata.New(session.New(&aws.Config{
Endpoint: aws.String(metadataURL),
})),
})
} else {
log.Printf("[DEBUG] EC2 Metadata service not found, not adding EC2 Role Credential Provider")
}
return awsCredentials.NewChainCredentials(providers)
}

0 comments on commit 6deabf9

Please sign in to comment.