Skip IAM/STS validation and metadata check (#7874)

* Skip IAM/STS validation and metadata check

* Skip IAM/STS identity validation - For environments or other api
  implementations where there are no IAM/STS endpoints available, this
  option lets you opt out from that provider initialization step.
* Skip metdata api check - For environments in which you know ahead of
  time there isn't going to be a metadta api endpoint, this option lets
  you opt out from that check to save time.

* Allow iam/sts initialization even if skipping account/cred validation

(#7874)

* Split out skip of IAM validation into credentials and account id

(#7874)
This commit is contained in:
Renier Morales 2016-08-10 10:10:34 -04:00 committed by Radek Simko
parent 6b477e888a
commit c2bcb5fbe5
5 changed files with 107 additions and 56 deletions

View File

@ -86,18 +86,18 @@ func parseAccountIdFromArn(arn string) (string, error) {
// This function is responsible for reading credentials from the
// environment in the case that they're not explicitly specified
// in the Terraform configuration.
func GetCredentials(key, secret, token, profile, credsfile string) *awsCredentials.Credentials {
func GetCredentials(c *Config) *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,
AccessKeyID: c.AccessKey,
SecretAccessKey: c.SecretKey,
SessionToken: c.Token,
}},
&awsCredentials.EnvProvider{},
&awsCredentials.SharedCredentialsProvider{
Filename: credsfile,
Profile: profile,
Filename: c.CredsFilename,
Profile: c.Profile,
},
}
@ -114,6 +114,7 @@ func GetCredentials(key, secret, token, profile, credsfile string) *awsCredentia
// 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
if c.SkipMetadataApiCheck == false {
metadataClient := ec2metadata.New(session.New(cfg))
if metadataClient.Available() {
providers = append(providers, &ec2rolecreds.EC2RoleProvider{
@ -128,6 +129,7 @@ func GetCredentials(key, secret, token, profile, credsfile string) *awsCredentia
log.Printf("[WARN] Ignoring AWS metadata API endpoint at %s "+
"as it doesn't return any instance-id", usedEndpoint)
}
}
return awsCredentials.NewChainCredentials(providers)
}

View File

@ -218,7 +218,7 @@ func TestAWSGetCredentials_shouldError(t *testing.T) {
defer resetEnv()
cfg := Config{}
c := GetCredentials(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename)
c := GetCredentials(&cfg)
_, err := c.Get()
if awsErr, ok := err.(awserr.Error); ok {
if awsErr.Code() != "NoCredentialProviders" {
@ -251,7 +251,7 @@ func TestAWSGetCredentials_shouldBeStatic(t *testing.T) {
Token: c.Token,
}
creds := GetCredentials(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename)
creds := GetCredentials(&cfg)
if creds == nil {
t.Fatalf("Expected a static creds provider to be returned")
}
@ -286,7 +286,7 @@ func TestAWSGetCredentials_shouldIAM(t *testing.T) {
// An empty config, no key supplied
cfg := Config{}
creds := GetCredentials(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename)
creds := GetCredentials(&cfg)
if creds == nil {
t.Fatalf("Expected a static creds provider to be returned")
}
@ -335,7 +335,7 @@ func TestAWSGetCredentials_shouldIgnoreIAM(t *testing.T) {
Token: c.Token,
}
creds := GetCredentials(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename)
creds := GetCredentials(&cfg)
if creds == nil {
t.Fatalf("Expected a static creds provider to be returned")
}
@ -362,7 +362,7 @@ func TestAWSGetCredentials_shouldErrorWithInvalidEndpoint(t *testing.T) {
ts := invalidAwsEnv(t)
defer ts()
creds := GetCredentials("", "", "", "", "")
creds := GetCredentials(&Config{})
v, err := creds.Get()
if err == nil {
t.Fatal("Expected error returned when getting creds w/ invalid EC2 endpoint")
@ -380,7 +380,7 @@ func TestAWSGetCredentials_shouldIgnoreInvalidEndpoint(t *testing.T) {
ts := invalidAwsEnv(t)
defer ts()
creds := GetCredentials("accessKey", "secretKey", "", "", "")
creds := GetCredentials(&Config{AccessKey: "accessKey", SecretKey: "secretKey"})
v, err := creds.Get()
if err != nil {
t.Fatalf("Getting static credentials w/ invalid EC2 endpoint failed: %s", err)
@ -406,7 +406,7 @@ func TestAWSGetCredentials_shouldCatchEC2RoleProvider(t *testing.T) {
ts := awsEnv(t)
defer ts()
creds := GetCredentials("", "", "", "", "")
creds := GetCredentials(&Config{})
if creds == nil {
t.Fatalf("Expected an EC2Role creds provider to be returned")
}
@ -452,7 +452,7 @@ func TestAWSGetCredentials_shouldBeShared(t *testing.T) {
t.Fatalf("Error resetting env var AWS_SHARED_CREDENTIALS_FILE: %s", err)
}
creds := GetCredentials("", "", "", "myprofile", file.Name())
creds := GetCredentials(&Config{Profile: "myprofile", CredsFilename: file.Name()})
if creds == nil {
t.Fatalf("Expected a provider chain to be returned")
}
@ -479,7 +479,7 @@ func TestAWSGetCredentials_shouldBeENV(t *testing.T) {
defer resetEnv()
cfg := Config{}
creds := GetCredentials(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename)
creds := GetCredentials(&cfg)
if creds == nil {
t.Fatalf("Expected a static creds provider to be returned")
}

View File

@ -75,7 +75,11 @@ type Config struct {
Ec2Endpoint string
IamEndpoint string
ElbEndpoint string
S3Endpoint string
Insecure bool
SkipIamCredsValidation bool
SkipIamAccountId bool
SkipMetadataApiCheck bool
}
type AWSClient struct {
@ -141,7 +145,7 @@ func (c *Config) Client() (interface{}, error) {
client.region = c.Region
log.Println("[INFO] Building AWS auth structure")
creds := GetCredentials(c.AccessKey, c.SecretKey, c.Token, c.Profile, c.CredsFilename)
creds := GetCredentials(c)
// Call Get to check for credential provider. If nothing found, we'll get an
// error, and we can present it nicely to the user
cp, err := creds.Get()
@ -199,11 +203,15 @@ func (c *Config) Client() (interface{}, error) {
client.iamconn = iam.New(awsIamSess)
client.stsconn = sts.New(sess)
if c.SkipIamCredsValidation == false {
err = c.ValidateCredentials(client.stsconn)
if err != nil {
errs = append(errs, err)
return nil, &multierror.Error{Errors: errs}
}
}
if c.SkipIamAccountId == false {
accountId, err := GetAccountId(client.iamconn, client.stsconn, cp.ProviderName)
if err == nil {
client.accountid = accountId
@ -213,6 +221,7 @@ func (c *Config) Client() (interface{}, error) {
if authErr != nil {
errs = append(errs, authErr)
}
}
client.apigateway = apigateway.New(sess)
client.appautoscalingconn = applicationautoscaling.New(sess)

View File

@ -100,6 +100,7 @@ func Provider() terraform.ResourceProvider {
Default: "",
Description: descriptions["kinesis_endpoint"],
},
"endpoints": endpointsSchema(),
"insecure": &schema.Schema{
@ -108,6 +109,27 @@ func Provider() terraform.ResourceProvider {
Default: false,
Description: descriptions["insecure"],
},
"skip_iam_creds_validation": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: descriptions["skip_iam_creds_validation"],
},
"skip_iam_account_id": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: descriptions["skip_iam_account_id"],
},
"skip_metadata_api_check": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: descriptions["skip_metadata_api_check"],
},
},
DataSourcesMap: map[string]*schema.Resource{
@ -332,6 +354,15 @@ func init() {
"insecure": "Explicitly allow the provider to perform \"insecure\" SSL requests. If omitted," +
"default value is `false`",
"skip_iam_creds_validation": "Skip the IAM/STS credentials validation. " +
"Used for AWS API implementations that do not use IAM.",
"skip_iam_account_id": "Skip the request of account id to IAM/STS. " +
"Used for AWS API implementations that do not use IAM.",
"skip_medatadata_api_check": "Skip the AWS Metadata API check. " +
"Used for AWS API implementations that do not have a metadata api endpoint.",
}
}
@ -347,6 +378,9 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) {
DynamoDBEndpoint: d.Get("dynamodb_endpoint").(string),
KinesisEndpoint: d.Get("kinesis_endpoint").(string),
Insecure: d.Get("insecure").(bool),
SkipIamCredsValidation: d.Get("skip_iam_creds_validation").(bool),
SkipIamAccountId: d.Get("skip_iam_account_id").(bool),
SkipMetadataApiCheck: d.Get("skip_metadata_api_check").(bool),
}
endpointsSet := d.Get("endpoints").(*schema.Set)

View File

@ -60,7 +60,13 @@ func s3Factory(conf map[string]string) (Client, error) {
kmsKeyID := conf["kms_key_id"]
var errs []error
creds := terraformAws.GetCredentials(conf["access_key"], conf["secret_key"], conf["token"], conf["profile"], conf["shared_credentials_file"])
creds := terraformAws.GetCredentials(&terraformAws.Config{
AccessKey: conf["access_key"],
SecretKey: conf["secret_key"],
Token: conf["token"],
Profile: conf["profile"],
CredsFilename: 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()