Merge pull request #5764 from hashicorp/fix5760
"borrow" access config code from terraform.
This commit is contained in:
commit
5b64f71702
|
@ -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,
|
||||||
|
|
|
@ -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`.
|
||||||
|
|
||||||
|
|
|
@ -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`.
|
||||||
|
|
||||||
|
|
|
@ -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`.
|
||||||
|
|
||||||
|
|
|
@ -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`.
|
||||||
|
|
||||||
|
|
|
@ -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`.
|
||||||
|
|
||||||
|
|
|
@ -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 $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
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue