Merge pull request #5764 from hashicorp/fix5760

"borrow" access config code from terraform.
This commit is contained in:
Matthew Hooker 2018-02-08 14:55:43 -08:00 committed by GitHub
commit 5b64f71702
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 184 additions and 50 deletions

View File

@ -1,6 +1,7 @@
package common
import (
"errors"
"fmt"
"log"
"os"
@ -8,7 +9,9 @@ import (
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"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"
@ -17,15 +20,16 @@ import (
// AccessConfig is for common configuration related to AWS access
type AccessConfig struct {
AccessKey string `mapstructure:"access_key"`
CustomEndpointEc2 string `mapstructure:"custom_endpoint_ec2"`
MFACode string `mapstructure:"mfa_code"`
ProfileName string `mapstructure:"profile"`
RawRegion string `mapstructure:"region"`
SecretKey string `mapstructure:"secret_key"`
SkipValidation bool `mapstructure:"skip_region_validation"`
Token string `mapstructure:"token"`
session *session.Session
AccessKey string `mapstructure:"access_key"`
CustomEndpointEc2 string `mapstructure:"custom_endpoint_ec2"`
MFACode string `mapstructure:"mfa_code"`
ProfileName string `mapstructure:"profile"`
RawRegion string `mapstructure:"region"`
SecretKey string `mapstructure:"secret_key"`
SkipValidation bool `mapstructure:"skip_region_validation"`
SkipMetadataApiCheck bool `mapstructure:"skip_metadata_api_check"`
Token string `mapstructure:"token"`
session *session.Session
}
// Config returns a valid aws.Config object for access to AWS services, or
@ -35,14 +39,78 @@ func (c *AccessConfig) Session() (*session.Session, error) {
return c.session, nil
}
config := aws.NewConfig().WithMaxRetries(11).WithCredentialsChainVerboseErrors(true)
// 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,
},
}
if c.ProfileName != "" {
if err := os.Setenv("AWS_PROFILE", c.ProfileName); err != nil {
return nil, fmt.Errorf("Set env error: %s", err)
// 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)
cp, err := creds.Get()
if err != nil {
if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NoCredentialProviders" {
return nil, errors.New("No valid credential sources found for AWS Builder. " +
"Please see https://www.packer.io/docs/builders/amazon.html#specifying-amazon-credentials " +
"for more information on providing credentials for the AWS Builder.")
}
return nil, fmt.Errorf("Error loading credentials for AWS Provider: %s", err)
}
log.Printf("[INFO] AWS Auth provider used: %q", cp.ProviderName)
config := aws.NewConfig().WithMaxRetries(11).WithCredentialsChainVerboseErrors(true)
config = config.WithCredentials(creds)
if c.RawRegion != "" {
config = config.WithRegion(c.RawRegion)
} else if region := c.metadataRegion(); region != "" {
@ -53,11 +121,6 @@ func (c *AccessConfig) Session() (*session.Session, error) {
config = config.WithEndpoint(c.CustomEndpointEc2)
}
if c.AccessKey != "" {
config = config.WithCredentials(
credentials.NewStaticCredentials(c.AccessKey, c.SecretKey, c.Token))
}
opts := session.Options{
SharedConfigState: session.SharedConfigEnable,
Config: *config,

View File

@ -246,6 +246,12 @@ each category, the available configuration keys are alphabetized.
of the `source_ami` unless `from_scratch` is `true`, in which case
this field must be defined.
- `skip_metadata_api_check` - (boolean) Skip the AWS Metadata API check.
Useful for AWS API implementations that do not have a metadata API
endpoint. Setting to `true` prevents Packer from authenticating via the
Metadata API. You may need to use other authentication methods like static
credentials, configuration variables, or environment variables.
- `skip_region_validation` (boolean) - Set to true if you want to skip
validation of the `ami_regions` configuration option. Default `false`.

View File

@ -247,6 +247,12 @@ builder.
in case Packer exits ungracefully. Possible values are "stop" and "terminate",
default is `stop`.
- `skip_metadata_api_check` - (boolean) Skip the AWS Metadata API check.
Useful for AWS API implementations that do not have a metadata API
endpoint. Setting to `true` prevents Packer from authenticating via the
Metadata API. You may need to use other authentication methods like static
credentials, configuration variables, or environment variables.
- `skip_region_validation` (boolean) - Set to true if you want to skip
validation of the region configuration option. Default `false`.

View File

@ -240,6 +240,12 @@ builder.
incase packer exits ungracefully. Possible values are "stop" and "terminate",
default is `stop`.
- `skip_metadata_api_check` - (boolean) Skip the AWS Metadata API check.
Useful for AWS API implementations that do not have a metadata API
endpoint. Setting to `true` prevents Packer from authenticating via the
Metadata API. You may need to use other authentication methods like static
credentials, configuration variables, or environment variables.
- `skip_region_validation` (boolean) - Set to true if you want to skip
validation of the region configuration option. Default `false`.

View File

@ -149,6 +149,12 @@ builder.
in case Packer exits ungracefully. Possible values are `stop` and `terminate`.
Defaults to `stop`.
- `skip_metadata_api_check` - (boolean) Skip the AWS Metadata API check.
Useful for AWS API implementations that do not have a metadata API
endpoint. Setting to `true` prevents Packer from authenticating via the
Metadata API. You may need to use other authentication methods like static
credentials, configuration variables, or environment variables.
- `skip_region_validation` (boolean) - Set to `true` if you want to skip
validation of the region configuration option. Defaults to `false`.

View File

@ -251,6 +251,12 @@ builder.
The default is `0.0.0.0/0` (ie, allow any IPv4 source). This is only used
when `security_group_id` or `security_group_ids` is not specified.
- `skip_metadata_api_check` - (boolean) Skip the AWS Metadata API check.
Useful for AWS API implementations that do not have a metadata API
endpoint. Setting to `true` prevents Packer from authenticating via the
Metadata API. You may need to use other authentication methods like static
credentials, configuration variables, or environment variables.
- `skip_region_validation` (boolean) - Set to true if you want to skip
validation of the region configuration option. Defaults to `false`.

View File

@ -49,49 +49,90 @@ filesystem and data.
<span id="specifying-amazon-credentials"></span>
## Specifying Amazon Credentials
## Authentication
When you use any of the amazon builders, you must provide credentials to the API
in the form of an access key id and secret. These look like:
The AWS provider offers a flexible means of providing credentials for
authentication. The following methods are supported, in this order, and
explained below:
access key id: AKIAIOSFODNN7EXAMPLE
secret access key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
- Static credentials
- Environment variables
- Shared credentials file
- EC2 Role
If you use other AWS tools you may already have these configured. If so, packer
will try to use them, *unless* they are specified in your packer template.
Credentials are resolved in the following order:
### Static Credentials
1. Values hard-coded in the packer template are always authoritative.
2. *Variables* in the packer template may be resolved from command-line flags
or from environment variables. Please read about [User
Variables](https://www.packer.io/docs/templates/user-variables.html)
for details.
3. If no credentials are found, packer falls back to automatic lookup.
Static credentials can be provided in the form of an access key id and secret.
These look like:
### Automatic Lookup
```json
{
"access_key": "AKIAIOSFODNN7EXAMPLE",
"secret_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
"region": "us-east-1",
"type": "amazon-ebs"
}
```
Packer depends on the [AWS
SDK](https://aws.amazon.com/documentation/sdk-for-go/) to perform automatic
lookup using *credential chains*. In short, the SDK looks for credentials in
the following order:
### Environment variables
1. Environment variables.
2. Shared credentials file.
3. If your application is running on an Amazon EC2 instance, IAM role for Amazon EC2.
You can provide your credentials via the `AWS_ACCESS_KEY_ID` and
`AWS_SECRET_ACCESS_KEY`, environment variables, representing your AWS Access
Key and AWS Secret Key, respectively. Note that setting your AWS credentials
using either these environment variables will override the use of
`AWS_SHARED_CREDENTIALS_FILE` and `AWS_PROFILE`. The `AWS_DEFAULT_REGION` and
`AWS_SESSION_TOKEN` environment variables are also used, if applicable:
Please refer to the SDK's documentation on [specifying
credentials](https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials)
for more information.
## Using an IAM Task or Instance Role
Usage:
If AWS keys are not specified in the template, a
[shared credentials file](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html#cli-config-files)
or through environment variables Packer will use credentials provided by
the task's or instance's IAM role, if it has one.
```
$ export AWS_ACCESS_KEY_ID="anaccesskey"
$ export AWS_SECRET_ACCESS_KEY="asecretkey"
$ export AWS_DEFAULT_REGION="us-west-2"
$ packer build packer.json
```
The following policy document provides the minimal set permissions necessary for
Packer to work:
### Shared Credentials file
You can use an AWS credentials file to specify your credentials. The default
location is &#36;HOME/.aws/credentials on Linux and OS X, or
"%USERPROFILE%.aws\credentials" for Windows users. If we fail to detect
credentials inline, or in the environment, Packer will check this location. You
can optionally specify a different location in the configuration by setting the
environment with the `AWS_SHARED_CREDENTIALS_FILE` variable.
You may also configure the profile to use by setting the `profile`
configuration option, or setting the `AWS_PROFILE` environment variable:
```json
{
"profile": "customprofile",
"region": "us-east-1",
"type": "amazon-ebs"
}
```
### IAM Task or Instance Role
Finally, Packer will use credentials provided by the task's or instance's IAM
role, if it has one.
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,
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
for Packer to work:
``` json
{