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:
parent
6b477e888a
commit
c2bcb5fbe5
|
@ -86,18 +86,18 @@ func parseAccountIdFromArn(arn string) (string, error) {
|
||||||
// This function is responsible for reading credentials from the
|
// This function is responsible for reading credentials from the
|
||||||
// environment in the case that they're not explicitly specified
|
// environment in the case that they're not explicitly specified
|
||||||
// in the Terraform configuration.
|
// 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
|
// build a chain provider, lazy-evaulated by aws-sdk
|
||||||
providers := []awsCredentials.Provider{
|
providers := []awsCredentials.Provider{
|
||||||
&awsCredentials.StaticProvider{Value: awsCredentials.Value{
|
&awsCredentials.StaticProvider{Value: awsCredentials.Value{
|
||||||
AccessKeyID: key,
|
AccessKeyID: c.AccessKey,
|
||||||
SecretAccessKey: secret,
|
SecretAccessKey: c.SecretKey,
|
||||||
SessionToken: token,
|
SessionToken: c.Token,
|
||||||
}},
|
}},
|
||||||
&awsCredentials.EnvProvider{},
|
&awsCredentials.EnvProvider{},
|
||||||
&awsCredentials.SharedCredentialsProvider{
|
&awsCredentials.SharedCredentialsProvider{
|
||||||
Filename: credsfile,
|
Filename: c.CredsFilename,
|
||||||
Profile: profile,
|
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.
|
// Real AWS should reply to a simple metadata request.
|
||||||
// We check it actually does to ensure something else didn't just
|
// We check it actually does to ensure something else didn't just
|
||||||
// happen to be listening on the same IP:Port
|
// happen to be listening on the same IP:Port
|
||||||
metadataClient := ec2metadata.New(session.New(cfg))
|
if c.SkipMetadataApiCheck == false {
|
||||||
if metadataClient.Available() {
|
metadataClient := ec2metadata.New(session.New(cfg))
|
||||||
providers = append(providers, &ec2rolecreds.EC2RoleProvider{
|
if metadataClient.Available() {
|
||||||
Client: metadataClient,
|
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")
|
log.Printf("[INFO] AWS EC2 instance detected via default metadata" +
|
||||||
} else {
|
" API endpoint, EC2RoleProvider added to the auth chain")
|
||||||
if usedEndpoint == "" {
|
} else {
|
||||||
usedEndpoint = "default location"
|
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)
|
return awsCredentials.NewChainCredentials(providers)
|
||||||
|
|
|
@ -218,7 +218,7 @@ func TestAWSGetCredentials_shouldError(t *testing.T) {
|
||||||
defer resetEnv()
|
defer resetEnv()
|
||||||
cfg := Config{}
|
cfg := Config{}
|
||||||
|
|
||||||
c := GetCredentials(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename)
|
c := GetCredentials(&cfg)
|
||||||
_, err := c.Get()
|
_, err := c.Get()
|
||||||
if awsErr, ok := err.(awserr.Error); ok {
|
if awsErr, ok := err.(awserr.Error); ok {
|
||||||
if awsErr.Code() != "NoCredentialProviders" {
|
if awsErr.Code() != "NoCredentialProviders" {
|
||||||
|
@ -251,7 +251,7 @@ func TestAWSGetCredentials_shouldBeStatic(t *testing.T) {
|
||||||
Token: c.Token,
|
Token: c.Token,
|
||||||
}
|
}
|
||||||
|
|
||||||
creds := GetCredentials(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename)
|
creds := GetCredentials(&cfg)
|
||||||
if creds == nil {
|
if creds == nil {
|
||||||
t.Fatalf("Expected a static creds provider to be returned")
|
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
|
// An empty config, no key supplied
|
||||||
cfg := Config{}
|
cfg := Config{}
|
||||||
|
|
||||||
creds := GetCredentials(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename)
|
creds := GetCredentials(&cfg)
|
||||||
if creds == nil {
|
if creds == nil {
|
||||||
t.Fatalf("Expected a static creds provider to be returned")
|
t.Fatalf("Expected a static creds provider to be returned")
|
||||||
}
|
}
|
||||||
|
@ -335,7 +335,7 @@ func TestAWSGetCredentials_shouldIgnoreIAM(t *testing.T) {
|
||||||
Token: c.Token,
|
Token: c.Token,
|
||||||
}
|
}
|
||||||
|
|
||||||
creds := GetCredentials(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename)
|
creds := GetCredentials(&cfg)
|
||||||
if creds == nil {
|
if creds == nil {
|
||||||
t.Fatalf("Expected a static creds provider to be returned")
|
t.Fatalf("Expected a static creds provider to be returned")
|
||||||
}
|
}
|
||||||
|
@ -362,7 +362,7 @@ func TestAWSGetCredentials_shouldErrorWithInvalidEndpoint(t *testing.T) {
|
||||||
ts := invalidAwsEnv(t)
|
ts := invalidAwsEnv(t)
|
||||||
defer ts()
|
defer ts()
|
||||||
|
|
||||||
creds := GetCredentials("", "", "", "", "")
|
creds := GetCredentials(&Config{})
|
||||||
v, err := creds.Get()
|
v, err := creds.Get()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected error returned when getting creds w/ invalid EC2 endpoint")
|
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)
|
ts := invalidAwsEnv(t)
|
||||||
defer ts()
|
defer ts()
|
||||||
|
|
||||||
creds := GetCredentials("accessKey", "secretKey", "", "", "")
|
creds := GetCredentials(&Config{AccessKey: "accessKey", SecretKey: "secretKey"})
|
||||||
v, err := creds.Get()
|
v, err := creds.Get()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Getting static credentials w/ invalid EC2 endpoint failed: %s", err)
|
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)
|
ts := awsEnv(t)
|
||||||
defer ts()
|
defer ts()
|
||||||
|
|
||||||
creds := GetCredentials("", "", "", "", "")
|
creds := GetCredentials(&Config{})
|
||||||
if creds == nil {
|
if creds == nil {
|
||||||
t.Fatalf("Expected an EC2Role creds provider to be returned")
|
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)
|
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 {
|
if creds == nil {
|
||||||
t.Fatalf("Expected a provider chain to be returned")
|
t.Fatalf("Expected a provider chain to be returned")
|
||||||
}
|
}
|
||||||
|
@ -479,7 +479,7 @@ func TestAWSGetCredentials_shouldBeENV(t *testing.T) {
|
||||||
defer resetEnv()
|
defer resetEnv()
|
||||||
|
|
||||||
cfg := Config{}
|
cfg := Config{}
|
||||||
creds := GetCredentials(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename)
|
creds := GetCredentials(&cfg)
|
||||||
if creds == nil {
|
if creds == nil {
|
||||||
t.Fatalf("Expected a static creds provider to be returned")
|
t.Fatalf("Expected a static creds provider to be returned")
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,12 +70,16 @@ type Config struct {
|
||||||
AllowedAccountIds []interface{}
|
AllowedAccountIds []interface{}
|
||||||
ForbiddenAccountIds []interface{}
|
ForbiddenAccountIds []interface{}
|
||||||
|
|
||||||
DynamoDBEndpoint string
|
DynamoDBEndpoint string
|
||||||
KinesisEndpoint string
|
KinesisEndpoint string
|
||||||
Ec2Endpoint string
|
Ec2Endpoint string
|
||||||
IamEndpoint string
|
IamEndpoint string
|
||||||
ElbEndpoint string
|
ElbEndpoint string
|
||||||
Insecure bool
|
S3Endpoint string
|
||||||
|
Insecure bool
|
||||||
|
SkipIamCredsValidation bool
|
||||||
|
SkipIamAccountId bool
|
||||||
|
SkipMetadataApiCheck bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type AWSClient struct {
|
type AWSClient struct {
|
||||||
|
@ -141,7 +145,7 @@ func (c *Config) Client() (interface{}, error) {
|
||||||
client.region = c.Region
|
client.region = c.Region
|
||||||
|
|
||||||
log.Println("[INFO] Building AWS auth structure")
|
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
|
// Call Get to check for credential provider. If nothing found, we'll get an
|
||||||
// error, and we can present it nicely to the user
|
// error, and we can present it nicely to the user
|
||||||
cp, err := creds.Get()
|
cp, err := creds.Get()
|
||||||
|
@ -199,19 +203,24 @@ func (c *Config) Client() (interface{}, error) {
|
||||||
client.iamconn = iam.New(awsIamSess)
|
client.iamconn = iam.New(awsIamSess)
|
||||||
client.stsconn = sts.New(sess)
|
client.stsconn = sts.New(sess)
|
||||||
|
|
||||||
err = c.ValidateCredentials(client.stsconn)
|
if c.SkipIamCredsValidation == false {
|
||||||
if err != nil {
|
err = c.ValidateCredentials(client.stsconn)
|
||||||
errs = append(errs, err)
|
if err != nil {
|
||||||
return nil, &multierror.Error{Errors: errs}
|
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
authErr := c.ValidateAccountId(client.accountid)
|
if c.SkipIamAccountId == false {
|
||||||
if authErr != nil {
|
accountId, err := GetAccountId(client.iamconn, client.stsconn, cp.ProviderName)
|
||||||
errs = append(errs, authErr)
|
if err == nil {
|
||||||
|
client.accountid = accountId
|
||||||
|
}
|
||||||
|
|
||||||
|
authErr := c.ValidateAccountId(client.accountid)
|
||||||
|
if authErr != nil {
|
||||||
|
errs = append(errs, authErr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
client.apigateway = apigateway.New(sess)
|
client.apigateway = apigateway.New(sess)
|
||||||
|
|
|
@ -100,6 +100,7 @@ func Provider() terraform.ResourceProvider {
|
||||||
Default: "",
|
Default: "",
|
||||||
Description: descriptions["kinesis_endpoint"],
|
Description: descriptions["kinesis_endpoint"],
|
||||||
},
|
},
|
||||||
|
|
||||||
"endpoints": endpointsSchema(),
|
"endpoints": endpointsSchema(),
|
||||||
|
|
||||||
"insecure": &schema.Schema{
|
"insecure": &schema.Schema{
|
||||||
|
@ -108,6 +109,27 @@ func Provider() terraform.ResourceProvider {
|
||||||
Default: false,
|
Default: false,
|
||||||
Description: descriptions["insecure"],
|
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{
|
DataSourcesMap: map[string]*schema.Resource{
|
||||||
|
@ -332,21 +354,33 @@ func init() {
|
||||||
|
|
||||||
"insecure": "Explicitly allow the provider to perform \"insecure\" SSL requests. If omitted," +
|
"insecure": "Explicitly allow the provider to perform \"insecure\" SSL requests. If omitted," +
|
||||||
"default value is `false`",
|
"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) {
|
func providerConfigure(d *schema.ResourceData) (interface{}, error) {
|
||||||
config := Config{
|
config := Config{
|
||||||
AccessKey: d.Get("access_key").(string),
|
AccessKey: d.Get("access_key").(string),
|
||||||
SecretKey: d.Get("secret_key").(string),
|
SecretKey: d.Get("secret_key").(string),
|
||||||
Profile: d.Get("profile").(string),
|
Profile: d.Get("profile").(string),
|
||||||
CredsFilename: d.Get("shared_credentials_file").(string),
|
CredsFilename: d.Get("shared_credentials_file").(string),
|
||||||
Token: d.Get("token").(string),
|
Token: d.Get("token").(string),
|
||||||
Region: d.Get("region").(string),
|
Region: d.Get("region").(string),
|
||||||
MaxRetries: d.Get("max_retries").(int),
|
MaxRetries: d.Get("max_retries").(int),
|
||||||
DynamoDBEndpoint: d.Get("dynamodb_endpoint").(string),
|
DynamoDBEndpoint: d.Get("dynamodb_endpoint").(string),
|
||||||
KinesisEndpoint: d.Get("kinesis_endpoint").(string),
|
KinesisEndpoint: d.Get("kinesis_endpoint").(string),
|
||||||
Insecure: d.Get("insecure").(bool),
|
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)
|
endpointsSet := d.Get("endpoints").(*schema.Set)
|
||||||
|
|
|
@ -60,7 +60,13 @@ func s3Factory(conf map[string]string) (Client, error) {
|
||||||
kmsKeyID := conf["kms_key_id"]
|
kmsKeyID := conf["kms_key_id"]
|
||||||
|
|
||||||
var errs []error
|
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
|
// Call Get to check for credential provider. If nothing found, we'll get an
|
||||||
// error, and we can present it nicely to the user
|
// error, and we can present it nicely to the user
|
||||||
_, err := creds.Get()
|
_, err := creds.Get()
|
||||||
|
|
Loading…
Reference in New Issue