Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add more logging during configuration #386

Merged
merged 3 commits into from
Mar 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions aws_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,23 +40,29 @@ func GetAwsConfig(ctx context.Context, c *Config) (context.Context, aws.Config,
baseCtx, logger := logging.New(ctx, loggerName)
baseCtx = logging.RegisterLogger(baseCtx, logger)

logger.Trace(baseCtx, "Resolving AWS configuration")

if metadataUrl := os.Getenv("AWS_METADATA_URL"); metadataUrl != "" {
logger.Warn(baseCtx, `The environment variable "AWS_METADATA_URL" is deprecated. Use "AWS_EC2_METADATA_SERVICE_ENDPOINT" instead.`)
if ec2MetadataServiceEndpoint := os.Getenv("AWS_EC2_METADATA_SERVICE_ENDPOINT"); ec2MetadataServiceEndpoint != "" {
if ec2MetadataServiceEndpoint != metadataUrl {
logger.Warn(baseCtx, fmt.Sprintf(`[WARN] The environment variable "AWS_EC2_METADATA_SERVICE_ENDPOINT" is already set to %q. Ignoring "AWS_METADATA_URL".`, ec2MetadataServiceEndpoint))
logger.Warn(baseCtx, fmt.Sprintf(`The environment variable "AWS_EC2_METADATA_SERVICE_ENDPOINT" is already set to %q. Ignoring "AWS_METADATA_URL".`, ec2MetadataServiceEndpoint))
}
} else {
logger.Warn(baseCtx, fmt.Sprintf(`[WARN] Setting "AWS_EC2_METADATA_SERVICE_ENDPOINT" to %q.`, metadataUrl))
logger.Warn(baseCtx, fmt.Sprintf(`Setting "AWS_EC2_METADATA_SERVICE_ENDPOINT" to %q.`, metadataUrl))
os.Setenv("AWS_EC2_METADATA_SERVICE_ENDPOINT", metadataUrl)
}
}

logger.Debug(baseCtx, "Resolving credentials provider")
credentialsProvider, initialSource, err := getCredentialsProvider(baseCtx, c)
if err != nil {
return ctx, aws.Config{}, err
}
creds, _ := credentialsProvider.Retrieve(baseCtx)
creds, err := credentialsProvider.Retrieve(baseCtx)
if err != nil {
return ctx, aws.Config{}, fmt.Errorf("retrieving credentials: %w", err)
}
logger.Info(baseCtx, "Retrieved credentials", map[string]any{
"tf_aws.credentials_source": creds.Source,
})
Expand Down Expand Up @@ -87,6 +93,7 @@ func GetAwsConfig(ctx context.Context, c *Config) (context.Context, aws.Config,
)
}

logger.Debug(baseCtx, "Loading configuration")
awsConfig, err := config.LoadDefaultConfig(baseCtx, loadOptions...)
if err != nil {
return ctx, aws.Config{}, fmt.Errorf("loading configuration: %w", err)
Expand Down Expand Up @@ -191,11 +198,15 @@ func commonLoadOptions(ctx context.Context, c *Config) ([]func(*config.LoadOptio
var httpClient config.HTTPClient

if v := c.HTTPClient; v == nil {
logger.Trace(ctx, "Building default HTTP client")
httpClient, err = defaultHttpClient(c)
if err != nil {
return nil, err
}
} else {
logger.Debug(ctx, "Setting HTTP client", map[string]any{
"tf_aws.http_client.source": configSourceProviderConfig,
})
httpClient = v
}

Expand Down
63 changes: 48 additions & 15 deletions credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ import (
"github.com/hashicorp/aws-sdk-go-base/v2/logging"
)

const (
configSourceProviderConfig = "provider"
configSourceEnvironmentVariable = "envvar"
)

func getCredentialsProvider(ctx context.Context, c *Config) (aws.CredentialsProvider, string, error) {
logger := logging.RetrieveLogger(ctx)

Expand All @@ -29,6 +34,7 @@ func getCredentialsProvider(ctx context.Context, c *Config) (aws.CredentialsProv
// The endpoint resolver is added here instead of in commonLoadOptions() so that it
// is not included in the aws.Config returned to the caller
config.WithEndpointResolverWithOptions(credentialsEndpointResolver(ctx, c)),
config.WithLogConfigurationWarnings(true),
)

envConfig, err := config.NewEnvConfig()
Expand All @@ -44,11 +50,19 @@ func getCredentialsProvider(ctx context.Context, c *Config) (aws.CredentialsProv
// The default AWS SDK authentication flow silently ignores invalid Profiles. Pre-validate that the Profile exists
// https://github.com/aws/aws-sdk-go-v2/issues/1591
profile := c.Profile
if profile == "" {
var profileSource string
if profile != "" {
profileSource = configSourceProviderConfig
} else {
profile = envConfig.SharedConfigProfile
profileSource = configSourceEnvironmentVariable
}

if profile != "" {
logger.Debug(ctx, "Using profile", map[string]any{
"tf_aws.profile": profile,
"tf_aws.profile.source": profileSource,
})
sharedCredentialsFiles, err := c.ResolveSharedCredentialsFiles()
if err != nil {
return nil, "", err
Expand All @@ -58,14 +72,16 @@ func getCredentialsProvider(ctx context.Context, c *Config) (aws.CredentialsProv
for i, v := range sharedCredentialsFiles {
f[i] = fmt.Sprintf(`"%s"`, v)
}
logger.Debug(ctx, "Using shared credentials files from configuration", map[string]any{
"tf_aws.shared_credentials_files": f,
logger.Debug(ctx, "Using shared credentials files", map[string]any{
"tf_aws.shared_credentials_files": f,
"tf_aws.shared_credentials_files.source": configSourceProviderConfig,
})
} else {
if envConfig.SharedCredentialsFile != "" {
sharedCredentialsFiles = []string{envConfig.SharedCredentialsFile}
logger.Debug(ctx, "Using shared credentials file environment variables", map[string]any{
"tf_aws.shared_credentials_files": sharedCredentialsFiles,
logger.Debug(ctx, "Using shared credentials files", map[string]any{
"tf_aws.shared_credentials_files": sharedCredentialsFiles,
"tf_aws.shared_credentials_files.source": configSourceEnvironmentVariable,
})
}
}
Expand All @@ -79,18 +95,23 @@ func getCredentialsProvider(ctx context.Context, c *Config) (aws.CredentialsProv
for i, v := range sharedConfigFiles {
f[i] = fmt.Sprintf(`"%s"`, v)
}
logger.Debug(ctx, "Using shared configuration files from configuration", map[string]any{
"tf_aws.shared_config_files": f,
logger.Debug(ctx, "Using shared configuration files", map[string]any{
"tf_aws.shared_config_files": f,
"tf_aws.shared_config_files.source": configSourceProviderConfig,
})
} else {
if envConfig.SharedConfigFile != "" {
sharedConfigFiles = []string{envConfig.SharedConfigFile}
logger.Debug(ctx, "Using shared configuration file environment variables", map[string]any{
"tf_aws.shared_config_files": sharedConfigFiles,
logger.Debug(ctx, "Using shared configuration files", map[string]any{
"tf_aws.shared_config_files": sharedConfigFiles,
"tf_aws.shared_config_files.source": configSourceEnvironmentVariable,
})
}
}

logger.Debug(ctx, "Loading profile", map[string]any{
"tf_aws.profile": profile,
})
_, err = config.LoadSharedConfigProfile(ctx, profile, func(opts *config.LoadSharedConfigOptions) {
if len(sharedCredentialsFiles) != 0 {
opts.CredentialsFiles = sharedCredentialsFiles
Expand All @@ -106,8 +127,9 @@ func getCredentialsProvider(ctx context.Context, c *Config) (aws.CredentialsProv
// We need to validate both the configured and envvar named profiles for validity,
// but to use proper precedence, we only set the configured named profile
if c.Profile != "" {
logger.Debug(ctx, "Using profile from configuration", map[string]any{
"tf_aws.profile": c.Profile,
logger.Debug(ctx, "Setting profile", map[string]any{
"tf_aws.profile": profile,
"tf_aws.profile.source": configSourceProviderConfig,
})
loadOptions = append(
loadOptions,
Expand All @@ -126,8 +148,9 @@ func getCredentialsProvider(ctx context.Context, c *Config) (aws.CredentialsProv
if c.Token != "" {
params = append(params, "token")
}
logger.Debug(ctx, "Using authentication parameters from configuration", map[string]any{
"tf_aws.auth_fields": params,
logger.Debug(ctx, "Using authentication parameters", map[string]any{
"tf_aws.auth_fields": params,
"tf_aws.auth_fields.source": configSourceProviderConfig,
})
loadOptions = append(
loadOptions,
Expand All @@ -141,6 +164,7 @@ func getCredentialsProvider(ctx context.Context, c *Config) (aws.CredentialsProv
)
}

logger.Debug(ctx, "Loading configuration")
cfg, err := config.LoadDefaultConfig(ctx, loadOptions...)
if err != nil {
return nil, "", fmt.Errorf("loading configuration: %w", err)
Expand All @@ -162,12 +186,13 @@ func getCredentialsProvider(ctx context.Context, c *Config) (aws.CredentialsProv
cfg.Credentials = provider
}

logger.Debug(ctx, "Retrieving credentials")
creds, err := cfg.Credentials.Retrieve(ctx)
if err != nil {
if c.Profile != "" && os.Getenv("AWS_ACCESS_KEY_ID") != "" && os.Getenv("AWS_SECRET_ACCESS_KEY") != "" {
err = fmt.Errorf(`A Profile was specified along with the environment variables "AWS_ACCESS_KEY_ID" and "AWS_SECRET_ACCESS_KEY". The Profile is now used instead of the environment variable credentials.

Error: %w`, err)
AWS Error: %w`, err)
}
return nil, "", c.NewNoValidCredentialSourcesError(err)
}
Expand All @@ -185,7 +210,15 @@ Error: %w`, err)
}

func webIdentityCredentialsProvider(ctx context.Context, awsConfig aws.Config, c *Config) (aws.CredentialsProvider, error) {
logger := logging.RetrieveLogger(ctx)

ar := c.AssumeRoleWithWebIdentity

logger.Info(ctx, "Assuming IAM Role With Web Identity", map[string]any{
"tf_aws.assume_role_with_web_identity.role_arn": ar.RoleARN,
"tf_aws.assume_role_with_web_identity.session_name": ar.SessionName,
})

client := stsClient(ctx, awsConfig, c)

appCreds := stscreds.NewWebIdentityRoleProvider(client, ar.RoleARN, ar, func(opts *stscreds.WebIdentityRoleOptions) {
Expand Down Expand Up @@ -217,14 +250,14 @@ func assumeRoleCredentialsProvider(ctx context.Context, awsConfig aws.Config, c
return nil, errors.New("Assume Role: role ARN not set")
}

// When assuming a role, we need to first authenticate the base credentials above, then assume the desired role
logger.Info(ctx, "Assuming IAM Role", map[string]any{
"tf_aws.assume_role.role_arn": ar.RoleARN,
"tf_aws.assume_role.session_name": ar.SessionName,
"tf_aws.assume_role.external_id": ar.ExternalID,
"tf_aws.assume_role.source_identity": ar.SourceIdentity,
})

// When assuming a role, we need to first authenticate the base credentials above, then assume the desired role
client := stsClient(ctx, awsConfig, c)

appCreds := stscreds.NewAssumeRoleProvider(client, ar.RoleARN, func(opts *stscreds.AssumeRoleOptions) {
Expand Down
6 changes: 3 additions & 3 deletions internal/config/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ There are a number of possible causes of this - the most common are:
* The credentials do not have appropriate permission to assume the role
* The role ARN is not valid

Error: %s
AWS Error: %s
`, e.Config.AssumeRole.RoleARN, e.Err)
}

Expand Down Expand Up @@ -55,7 +55,7 @@ There are a number of possible causes of this - the most common are:
* The web identity token does not have appropriate permission to assume the role
* The role ARN is not valid

Error: %s
AWS Error: %s
`, e.Config.AssumeRoleWithWebIdentity.RoleARN, e.Err)
}

Expand Down Expand Up @@ -83,7 +83,7 @@ func (e NoValidCredentialSourcesError) Error() string {
Please see %[2]s
for more information about providing credentials.

Error: %[3]s
AWS Error: %[3]s
`, e.Config.CallerName, e.Config.CallerDocumentationURL, e.Err)
}

Expand Down
8 changes: 8 additions & 0 deletions logging/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ func (l TfLogger) Debug(ctx context.Context, msg string, fields ...map[string]an
}
}

func (l TfLogger) Trace(ctx context.Context, msg string, fields ...map[string]any) {
if l == "" {
tflog.Trace(ctx, msg, fields...)
} else {
tflog.SubsystemTrace(ctx, string(l), msg, fields...)
}
}

func (l TfLogger) SetField(ctx context.Context, key string, value any) context.Context {
if l == "" {
return tflog.SetField(ctx, key, value)
Expand Down