1package session
2
3import (
4	"fmt"
5	"os"
6	"strconv"
7
8	"github.com/aws/aws-sdk-go/aws"
9	"github.com/aws/aws-sdk-go/aws/credentials"
10	"github.com/aws/aws-sdk-go/aws/defaults"
11	"github.com/aws/aws-sdk-go/aws/endpoints"
12)
13
14// EnvProviderName provides a name of the provider when config is loaded from environment.
15const EnvProviderName = "EnvConfigCredentials"
16
17// envConfig is a collection of environment values the SDK will read
18// setup config from. All environment values are optional. But some values
19// such as credentials require multiple values to be complete or the values
20// will be ignored.
21type envConfig struct {
22	// Environment configuration values. If set both Access Key ID and Secret Access
23	// Key must be provided. Session Token and optionally also be provided, but is
24	// not required.
25	//
26	//	# Access Key ID
27	//	AWS_ACCESS_KEY_ID=AKID
28	//	AWS_ACCESS_KEY=AKID # only read if AWS_ACCESS_KEY_ID is not set.
29	//
30	//	# Secret Access Key
31	//	AWS_SECRET_ACCESS_KEY=SECRET
32	//	AWS_SECRET_KEY=SECRET=SECRET # only read if AWS_SECRET_ACCESS_KEY is not set.
33	//
34	//	# Session Token
35	//	AWS_SESSION_TOKEN=TOKEN
36	Creds credentials.Value
37
38	// Region value will instruct the SDK where to make service API requests to. If is
39	// not provided in the environment the region must be provided before a service
40	// client request is made.
41	//
42	//	AWS_REGION=us-east-1
43	//
44	//	# AWS_DEFAULT_REGION is only read if AWS_SDK_LOAD_CONFIG is also set,
45	//	# and AWS_REGION is not also set.
46	//	AWS_DEFAULT_REGION=us-east-1
47	Region string
48
49	// Profile name the SDK should load use when loading shared configuration from the
50	// shared configuration files. If not provided "default" will be used as the
51	// profile name.
52	//
53	//	AWS_PROFILE=my_profile
54	//
55	//	# AWS_DEFAULT_PROFILE is only read if AWS_SDK_LOAD_CONFIG is also set,
56	//	# and AWS_PROFILE is not also set.
57	//	AWS_DEFAULT_PROFILE=my_profile
58	Profile string
59
60	// SDK load config instructs the SDK to load the shared config in addition to
61	// shared credentials. This also expands the configuration loaded from the shared
62	// credentials to have parity with the shared config file. This also enables
63	// Region and Profile support for the AWS_DEFAULT_REGION and AWS_DEFAULT_PROFILE
64	// env values as well.
65	//
66	//	AWS_SDK_LOAD_CONFIG=1
67	EnableSharedConfig bool
68
69	// Shared credentials file path can be set to instruct the SDK to use an alternate
70	// file for the shared credentials. If not set the file will be loaded from
71	// $HOME/.aws/credentials on Linux/Unix based systems, and
72	// %USERPROFILE%\.aws\credentials on Windows.
73	//
74	//	AWS_SHARED_CREDENTIALS_FILE=$HOME/my_shared_credentials
75	SharedCredentialsFile string
76
77	// Shared config file path can be set to instruct the SDK to use an alternate
78	// file for the shared config. If not set the file will be loaded from
79	// $HOME/.aws/config on Linux/Unix based systems, and
80	// %USERPROFILE%\.aws\config on Windows.
81	//
82	//	AWS_CONFIG_FILE=$HOME/my_shared_config
83	SharedConfigFile string
84
85	// Sets the path to a custom Credentials Authority (CA) Bundle PEM file
86	// that the SDK will use instead of the system's root CA bundle.
87	// Only use this if you want to configure the SDK to use a custom set
88	// of CAs.
89	//
90	// Enabling this option will attempt to merge the Transport
91	// into the SDK's HTTP client. If the client's Transport is
92	// not a http.Transport an error will be returned. If the
93	// Transport's TLS config is set this option will cause the
94	// SDK to overwrite the Transport's TLS config's  RootCAs value.
95	//
96	// Setting a custom HTTPClient in the aws.Config options will override this setting.
97	// To use this option and custom HTTP client, the HTTP client needs to be provided
98	// when creating the session. Not the service client.
99	//
100	//  AWS_CA_BUNDLE=$HOME/my_custom_ca_bundle
101	CustomCABundle string
102
103	csmEnabled  string
104	CSMEnabled  *bool
105	CSMPort     string
106	CSMHost     string
107	CSMClientID string
108
109	// Enables endpoint discovery via environment variables.
110	//
111	//	AWS_ENABLE_ENDPOINT_DISCOVERY=true
112	EnableEndpointDiscovery *bool
113	enableEndpointDiscovery string
114
115	// Specifies the WebIdentity token the SDK should use to assume a role
116	// with.
117	//
118	//  AWS_WEB_IDENTITY_TOKEN_FILE=file_path
119	WebIdentityTokenFilePath string
120
121	// Specifies the IAM role arn to use when assuming an role.
122	//
123	//  AWS_ROLE_ARN=role_arn
124	RoleARN string
125
126	// Specifies the IAM role session name to use when assuming a role.
127	//
128	//  AWS_ROLE_SESSION_NAME=session_name
129	RoleSessionName string
130
131	// Specifies the Regional Endpoint flag for the sdk to resolve the endpoint for a service
132	//
133	// AWS_STS_REGIONAL_ENDPOINTS =sts_regional_endpoint
134	// This can take value as `regional` or `legacy`
135	STSRegionalEndpoint endpoints.STSRegionalEndpoint
136}
137
138var (
139	csmEnabledEnvKey = []string{
140		"AWS_CSM_ENABLED",
141	}
142	csmHostEnvKey = []string{
143		"AWS_CSM_HOST",
144	}
145	csmPortEnvKey = []string{
146		"AWS_CSM_PORT",
147	}
148	csmClientIDEnvKey = []string{
149		"AWS_CSM_CLIENT_ID",
150	}
151	credAccessEnvKey = []string{
152		"AWS_ACCESS_KEY_ID",
153		"AWS_ACCESS_KEY",
154	}
155	credSecretEnvKey = []string{
156		"AWS_SECRET_ACCESS_KEY",
157		"AWS_SECRET_KEY",
158	}
159	credSessionEnvKey = []string{
160		"AWS_SESSION_TOKEN",
161	}
162
163	enableEndpointDiscoveryEnvKey = []string{
164		"AWS_ENABLE_ENDPOINT_DISCOVERY",
165	}
166
167	regionEnvKeys = []string{
168		"AWS_REGION",
169		"AWS_DEFAULT_REGION", // Only read if AWS_SDK_LOAD_CONFIG is also set
170	}
171	profileEnvKeys = []string{
172		"AWS_PROFILE",
173		"AWS_DEFAULT_PROFILE", // Only read if AWS_SDK_LOAD_CONFIG is also set
174	}
175	sharedCredsFileEnvKey = []string{
176		"AWS_SHARED_CREDENTIALS_FILE",
177	}
178	sharedConfigFileEnvKey = []string{
179		"AWS_CONFIG_FILE",
180	}
181	webIdentityTokenFilePathEnvKey = []string{
182		"AWS_WEB_IDENTITY_TOKEN_FILE",
183	}
184	roleARNEnvKey = []string{
185		"AWS_ROLE_ARN",
186	}
187	roleSessionNameEnvKey = []string{
188		"AWS_ROLE_SESSION_NAME",
189	}
190	stsRegionalEndpointKey = []string{
191		"AWS_STS_REGIONAL_ENDPOINTS",
192	}
193)
194
195// loadEnvConfig retrieves the SDK's environment configuration.
196// See `envConfig` for the values that will be retrieved.
197//
198// If the environment variable `AWS_SDK_LOAD_CONFIG` is set to a truthy value
199// the shared SDK config will be loaded in addition to the SDK's specific
200// configuration values.
201func loadEnvConfig() (envConfig, error) {
202	enableSharedConfig, _ := strconv.ParseBool(os.Getenv("AWS_SDK_LOAD_CONFIG"))
203	return envConfigLoad(enableSharedConfig)
204}
205
206// loadEnvSharedConfig retrieves the SDK's environment configuration, and the
207// SDK shared config. See `envConfig` for the values that will be retrieved.
208//
209// Loads the shared configuration in addition to the SDK's specific configuration.
210// This will load the same values as `loadEnvConfig` if the `AWS_SDK_LOAD_CONFIG`
211// environment variable is set.
212func loadSharedEnvConfig() (envConfig, error) {
213	return envConfigLoad(true)
214}
215
216func envConfigLoad(enableSharedConfig bool) (envConfig, error) {
217	cfg := envConfig{}
218
219	cfg.EnableSharedConfig = enableSharedConfig
220
221	// Static environment credentials
222	var creds credentials.Value
223	setFromEnvVal(&creds.AccessKeyID, credAccessEnvKey)
224	setFromEnvVal(&creds.SecretAccessKey, credSecretEnvKey)
225	setFromEnvVal(&creds.SessionToken, credSessionEnvKey)
226	if creds.HasKeys() {
227		// Require logical grouping of credentials
228		creds.ProviderName = EnvProviderName
229		cfg.Creds = creds
230	}
231
232	// Role Metadata
233	setFromEnvVal(&cfg.RoleARN, roleARNEnvKey)
234	setFromEnvVal(&cfg.RoleSessionName, roleSessionNameEnvKey)
235
236	// Web identity environment variables
237	setFromEnvVal(&cfg.WebIdentityTokenFilePath, webIdentityTokenFilePathEnvKey)
238
239	// CSM environment variables
240	setFromEnvVal(&cfg.csmEnabled, csmEnabledEnvKey)
241	setFromEnvVal(&cfg.CSMHost, csmHostEnvKey)
242	setFromEnvVal(&cfg.CSMPort, csmPortEnvKey)
243	setFromEnvVal(&cfg.CSMClientID, csmClientIDEnvKey)
244
245	if len(cfg.csmEnabled) != 0 {
246		v, _ := strconv.ParseBool(cfg.csmEnabled)
247		cfg.CSMEnabled = &v
248	}
249
250	regionKeys := regionEnvKeys
251	profileKeys := profileEnvKeys
252	if !cfg.EnableSharedConfig {
253		regionKeys = regionKeys[:1]
254		profileKeys = profileKeys[:1]
255	}
256
257	setFromEnvVal(&cfg.Region, regionKeys)
258	setFromEnvVal(&cfg.Profile, profileKeys)
259
260	// endpoint discovery is in reference to it being enabled.
261	setFromEnvVal(&cfg.enableEndpointDiscovery, enableEndpointDiscoveryEnvKey)
262	if len(cfg.enableEndpointDiscovery) > 0 {
263		cfg.EnableEndpointDiscovery = aws.Bool(cfg.enableEndpointDiscovery != "false")
264	}
265
266	setFromEnvVal(&cfg.SharedCredentialsFile, sharedCredsFileEnvKey)
267	setFromEnvVal(&cfg.SharedConfigFile, sharedConfigFileEnvKey)
268
269	if len(cfg.SharedCredentialsFile) == 0 {
270		cfg.SharedCredentialsFile = defaults.SharedCredentialsFilename()
271	}
272	if len(cfg.SharedConfigFile) == 0 {
273		cfg.SharedConfigFile = defaults.SharedConfigFilename()
274	}
275
276	cfg.CustomCABundle = os.Getenv("AWS_CA_BUNDLE")
277
278	// STS Regional Endpoint variable
279	for _, k := range stsRegionalEndpointKey {
280		if v := os.Getenv(k); len(v) != 0 {
281			STSRegionalEndpoint, err := endpoints.GetSTSRegionalEndpoint(v)
282			if err != nil {
283				return cfg, fmt.Errorf("failed to load, %v from env config, %v", k, err)
284			}
285			cfg.STSRegionalEndpoint = STSRegionalEndpoint
286		}
287	}
288
289	return cfg, nil
290}
291
292func setFromEnvVal(dst *string, keys []string) {
293	for _, k := range keys {
294		if v := os.Getenv(k); len(v) != 0 {
295			*dst = v
296			break
297		}
298	}
299}
300