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,19 +114,21 @@ 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
metadataClient := ec2metadata.New(session.New(cfg))
if metadataClient.Available() {
providers = append(providers, &ec2rolecreds.EC2RoleProvider{
Client: metadataClient,
})
log.Printf("[INFO] AWS EC2 instance detected via default metadata" +
" API endpoint, EC2RoleProvider added to the auth chain")
} else {
if usedEndpoint == "" {
usedEndpoint = "default location"
if c.SkipMetadataApiCheck == false {
metadataClient := ec2metadata.New(session.New(cfg))
if metadataClient.Available() {
providers = append(providers, &ec2rolecreds.EC2RoleProvider{
Client: metadataClient,
})
log.Printf("[INFO] AWS EC2 instance detected via default metadata" +
" API endpoint, EC2RoleProvider added to the auth chain")
} else {
if usedEndpoint == "" {
usedEndpoint = "default location"
}
log.Printf("[WARN] Ignoring AWS metadata API endpoint at %s "+
"as it doesn't return any instance-id", usedEndpoint)
}
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

@ -70,12 +70,16 @@ type Config struct {
AllowedAccountIds []interface{}
ForbiddenAccountIds []interface{}
DynamoDBEndpoint string
KinesisEndpoint string
Ec2Endpoint string
IamEndpoint string
ElbEndpoint string
Insecure bool
DynamoDBEndpoint string
KinesisEndpoint string
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,19 +203,24 @@ func (c *Config) Client() (interface{}, error) {
client.iamconn = iam.New(awsIamSess)
client.stsconn = sts.New(sess)
err = c.ValidateCredentials(client.stsconn)
if err != nil {
errs = append(errs, err)
return nil, &multierror.Error{Errors: errs}
}
accountId, err := GetAccountId(client.iamconn, client.stsconn, cp.ProviderName)
if err == nil {
client.accountid = accountId
if c.SkipIamCredsValidation == false {
err = c.ValidateCredentials(client.stsconn)
if err != nil {
errs = append(errs, err)
return nil, &multierror.Error{Errors: errs}
}
}
authErr := c.ValidateAccountId(client.accountid)
if authErr != nil {
errs = append(errs, authErr)
if c.SkipIamAccountId == false {
accountId, err := GetAccountId(client.iamconn, client.stsconn, cp.ProviderName)
if err == nil {
client.accountid = accountId
}
authErr := c.ValidateAccountId(client.accountid)
if authErr != nil {
errs = append(errs, authErr)
}
}
client.apigateway = apigateway.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,21 +354,33 @@ 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.",
}
}
func providerConfigure(d *schema.ResourceData) (interface{}, error) {
config := Config{
AccessKey: d.Get("access_key").(string),
SecretKey: d.Get("secret_key").(string),
Profile: d.Get("profile").(string),
CredsFilename: d.Get("shared_credentials_file").(string),
Token: d.Get("token").(string),
Region: d.Get("region").(string),
MaxRetries: d.Get("max_retries").(int),
DynamoDBEndpoint: d.Get("dynamodb_endpoint").(string),
KinesisEndpoint: d.Get("kinesis_endpoint").(string),
Insecure: d.Get("insecure").(bool),
AccessKey: d.Get("access_key").(string),
SecretKey: d.Get("secret_key").(string),
Profile: d.Get("profile").(string),
CredsFilename: d.Get("shared_credentials_file").(string),
Token: d.Get("token").(string),
Region: d.Get("region").(string),
MaxRetries: d.Get("max_retries").(int),
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()