builder/amazon: Use sdk default cred providers

I think we were overcomplicating things. The SDK provides the correct
credential chain by default, so let's use that. This patch does a quick
check for static credentials and uses those if found, then defaults to
the default credential provider chain.

This patch also removes the metadata timeout argument. Current versions
of the SDK have short timeouts by default, so I don't believe this is
needed.
This commit is contained in:
Matthew Hooker 2018-03-15 09:49:34 -07:00
parent e483087574
commit b16f2ec64b
No known key found for this signature in database
GPG Key ID: 7B5F933D9CE8C6A1
2 changed files with 8 additions and 70 deletions

View File

@ -3,14 +3,12 @@ package common
import ( import (
"fmt" "fmt"
"log" "log"
"os"
"strings" "strings"
"time" "time"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/credentials" "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/ec2metadata"
"github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/aws/session"
"github.com/hashicorp/go-cleanhttp" "github.com/hashicorp/go-cleanhttp"
@ -38,66 +36,13 @@ func (c *AccessConfig) Session() (*session.Session, error) {
return c.session, nil return c.session, nil
} }
// build a chain provider, lazy-evaluated by aws-sdk
providers := []credentials.Provider{
&credentials.StaticProvider{Value: credentials.Value{
AccessKeyID: c.AccessKey,
SecretAccessKey: c.SecretKey,
SessionToken: c.Token,
}},
&credentials.EnvProvider{},
&credentials.SharedCredentialsProvider{
Filename: "",
Profile: c.ProfileName,
},
}
// Build isolated HTTP client to avoid issues with globally-shared settings
client := cleanhttp.DefaultClient()
// Keep the default timeout (100ms) low as we don't want to wait in non-EC2 environments
client.Timeout = 100 * time.Millisecond
const userTimeoutEnvVar = "AWS_METADATA_TIMEOUT"
userTimeout := os.Getenv(userTimeoutEnvVar)
if userTimeout != "" {
newTimeout, err := time.ParseDuration(userTimeout)
if err == nil {
if newTimeout.Nanoseconds() > 0 {
client.Timeout = newTimeout
} else {
log.Printf("[WARN] Non-positive value of %s (%s) is meaningless, ignoring", userTimeoutEnvVar, newTimeout.String())
}
} else {
log.Printf("[WARN] Error converting %s to time.Duration: %s", userTimeoutEnvVar, err)
}
}
log.Printf("[INFO] Setting AWS metadata API timeout to %s", client.Timeout.String())
cfg := &aws.Config{
HTTPClient: client,
}
if !c.SkipMetadataApiCheck {
// Real AWS should reply to a simple metadata request.
// We check it actually does to ensure something else didn't just
// happen to be listening on the same IP:Port
metadataClient := ec2metadata.New(session.New(cfg))
if metadataClient.Available() {
providers = append(providers, &ec2rolecreds.EC2RoleProvider{
Client: metadataClient,
})
log.Print("[INFO] AWS EC2 instance detected via default metadata" +
" API endpoint, EC2RoleProvider added to the auth chain")
} else {
log.Printf("[INFO] Ignoring AWS metadata API endpoint " +
"as it doesn't return any instance-id")
}
}
creds := credentials.NewChainCredentials(providers)
config := aws.NewConfig().WithCredentialsChainVerboseErrors(true) config := aws.NewConfig().WithCredentialsChainVerboseErrors(true)
staticCreds := credentials.NewStaticCredentials(c.AccessKey, c.SecretKey, c.Token)
if _, err := staticCreds.Get(); err != credentials.ErrStaticCredentialsEmpty {
config.WithCredentials(staticCreds)
}
if c.RawRegion != "" { if c.RawRegion != "" {
config = config.WithRegion(c.RawRegion) config = config.WithRegion(c.RawRegion)
} else if region := c.metadataRegion(); region != "" { } else if region := c.metadataRegion(); region != "" {
@ -123,8 +68,6 @@ func (c *AccessConfig) Session() (*session.Session, error) {
} }
} }
config = config.WithCredentials(creds)
if sess, err := session.NewSessionWithOptions(opts); err != nil { if sess, err := session.NewSessionWithOptions(opts); err != nil {
return nil, err return nil, err
} else if *sess.Config.Region == "" { } else if *sess.Config.Region == "" {
@ -185,6 +128,9 @@ func (c *AccessConfig) metadataRegion() string {
func (c *AccessConfig) Prepare(ctx *interpolate.Context) []error { func (c *AccessConfig) Prepare(ctx *interpolate.Context) []error {
var errs []error var errs []error
if c.SkipMetadataApiCheck {
log.Println("(WARN) skip_metadata_api_check ignored.")
}
// Either both access and secret key must be set or neither of them should // Either both access and secret key must be set or neither of them should
// be. // be.
if (len(c.AccessKey) > 0) != (len(c.SecretKey) > 0) { if (len(c.AccessKey) > 0) != (len(c.SecretKey) > 0) {

View File

@ -123,14 +123,6 @@ This is a preferred approach over any other when running in EC2 as you can
avoid hard coding credentials. Instead these are leased on-the-fly by Packer, avoid hard coding credentials. Instead these are leased on-the-fly by Packer,
which reduces the chance of leakage. which reduces the chance of leakage.
The default deadline for the EC2 metadata API endpoint is 100 milliseconds,
which can be overidden by setting the `AWS_METADATA_TIMEOUT` environment
variable. The variable expects a positive golang Time.Duration string, which is
a sequence of decimal numbers and a unit suffix; valid suffixes are `ns`
(nanoseconds), `us` (microseconds), `ms` (milliseconds), `s` (seconds), `m`
(minutes), and `h` (hours). Examples of valid inputs: `100ms`, `250ms`, `1s`,
`2.5s`, `2.5m`, `1m30s`.
The following policy document provides the minimal set permissions necessary The following policy document provides the minimal set permissions necessary
for Packer to work: for Packer to work: