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 package common
import ( import (
"errors"
"fmt" "fmt"
"log" "log"
"os" "os"
@ -8,7 +9,9 @@ import (
"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/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"
@ -17,15 +20,16 @@ import (
// AccessConfig is for common configuration related to AWS access // AccessConfig is for common configuration related to AWS access
type AccessConfig struct { type AccessConfig struct {
AccessKey string `mapstructure:"access_key"` AccessKey string `mapstructure:"access_key"`
CustomEndpointEc2 string `mapstructure:"custom_endpoint_ec2"` CustomEndpointEc2 string `mapstructure:"custom_endpoint_ec2"`
MFACode string `mapstructure:"mfa_code"` MFACode string `mapstructure:"mfa_code"`
ProfileName string `mapstructure:"profile"` ProfileName string `mapstructure:"profile"`
RawRegion string `mapstructure:"region"` RawRegion string `mapstructure:"region"`
SecretKey string `mapstructure:"secret_key"` SecretKey string `mapstructure:"secret_key"`
SkipValidation bool `mapstructure:"skip_region_validation"` SkipValidation bool `mapstructure:"skip_region_validation"`
Token string `mapstructure:"token"` SkipMetadataApiCheck bool `mapstructure:"skip_metadata_api_check"`
session *session.Session Token string `mapstructure:"token"`
session *session.Session
} }
// Config returns a valid aws.Config object for access to AWS services, or // 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 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 != "" { // Build isolated HTTP client to avoid issues with globally-shared settings
if err := os.Setenv("AWS_PROFILE", c.ProfileName); err != nil { client := cleanhttp.DefaultClient()
return nil, fmt.Errorf("Set env error: %s", err)
// 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 != "" { 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 != "" {
@ -53,11 +121,6 @@ func (c *AccessConfig) Session() (*session.Session, error) {
config = config.WithEndpoint(c.CustomEndpointEc2) config = config.WithEndpoint(c.CustomEndpointEc2)
} }
if c.AccessKey != "" {
config = config.WithCredentials(
credentials.NewStaticCredentials(c.AccessKey, c.SecretKey, c.Token))
}
opts := session.Options{ opts := session.Options{
SharedConfigState: session.SharedConfigEnable, SharedConfigState: session.SharedConfigEnable,
Config: *config, 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 of the `source_ami` unless `from_scratch` is `true`, in which case
this field must be defined. 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 - `skip_region_validation` (boolean) - Set to true if you want to skip
validation of the `ami_regions` configuration option. Default `false`. 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", in case Packer exits ungracefully. Possible values are "stop" and "terminate",
default is `stop`. 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 - `skip_region_validation` (boolean) - Set to true if you want to skip
validation of the region configuration option. Default `false`. 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", incase packer exits ungracefully. Possible values are "stop" and "terminate",
default is `stop`. 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 - `skip_region_validation` (boolean) - Set to true if you want to skip
validation of the region configuration option. Default `false`. 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`. in case Packer exits ungracefully. Possible values are `stop` and `terminate`.
Defaults to `stop`. 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 - `skip_region_validation` (boolean) - Set to `true` if you want to skip
validation of the region configuration option. Defaults to `false`. 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 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. 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 - `skip_region_validation` (boolean) - Set to true if you want to skip
validation of the region configuration option. Defaults to `false`. validation of the region configuration option. Defaults to `false`.

View File

@ -49,49 +49,90 @@ filesystem and data.
<span id="specifying-amazon-credentials"></span> <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 The AWS provider offers a flexible means of providing credentials for
in the form of an access key id and secret. These look like: authentication. The following methods are supported, in this order, and
explained below:
access key id: AKIAIOSFODNN7EXAMPLE - Static credentials
secret access key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY - Environment variables
- Shared credentials file
- EC2 Role
If you use other AWS tools you may already have these configured. If so, packer ### Static Credentials
will try to use them, *unless* they are specified in your packer template.
Credentials are resolved in the following order:
1. Values hard-coded in the packer template are always authoritative. Static credentials can be provided in the form of an access key id and secret.
2. *Variables* in the packer template may be resolved from command-line flags These look like:
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.
### Automatic Lookup ```json
{
"access_key": "AKIAIOSFODNN7EXAMPLE",
"secret_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
"region": "us-east-1",
"type": "amazon-ebs"
}
```
Packer depends on the [AWS ### Environment variables
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:
1. Environment variables. You can provide your credentials via the `AWS_ACCESS_KEY_ID` and
2. Shared credentials file. `AWS_SECRET_ACCESS_KEY`, environment variables, representing your AWS Access
3. If your application is running on an Amazon EC2 instance, IAM role for Amazon EC2. 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) $ export AWS_ACCESS_KEY_ID="anaccesskey"
or through environment variables Packer will use credentials provided by $ export AWS_SECRET_ACCESS_KEY="asecretkey"
the task's or instance's IAM role, if it has one. $ export AWS_DEFAULT_REGION="us-west-2"
$ packer build packer.json
```
The following policy document provides the minimal set permissions necessary for ### Shared Credentials file
Packer to work:
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 ``` json
{ {