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 STS Regional Endpoint flag for the SDK to resolve the endpoint
132	// for a service.
133	//
134	// AWS_STS_REGIONAL_ENDPOINTS=regional
135	// This can take value as `regional` or `legacy`
136	STSRegionalEndpoint endpoints.STSRegionalEndpoint
137
138	// Specifies the S3 Regional Endpoint flag for the SDK to resolve the
139	// endpoint for a service.
140	//
141	// AWS_S3_US_EAST_1_REGIONAL_ENDPOINT=regional
142	// This can take value as `regional` or `legacy`
143	S3UsEast1RegionalEndpoint endpoints.S3UsEast1RegionalEndpoint
144}
145
146var (
147	csmEnabledEnvKey = []string{
148		"AWS_CSM_ENABLED",
149	}
150	csmHostEnvKey = []string{
151		"AWS_CSM_HOST",
152	}
153	csmPortEnvKey = []string{
154		"AWS_CSM_PORT",
155	}
156	csmClientIDEnvKey = []string{
157		"AWS_CSM_CLIENT_ID",
158	}
159	credAccessEnvKey = []string{
160		"AWS_ACCESS_KEY_ID",
161		"AWS_ACCESS_KEY",
162	}
163	credSecretEnvKey = []string{
164		"AWS_SECRET_ACCESS_KEY",
165		"AWS_SECRET_KEY",
166	}
167	credSessionEnvKey = []string{
168		"AWS_SESSION_TOKEN",
169	}
170
171	enableEndpointDiscoveryEnvKey = []string{
172		"AWS_ENABLE_ENDPOINT_DISCOVERY",
173	}
174
175	regionEnvKeys = []string{
176		"AWS_REGION",
177		"AWS_DEFAULT_REGION", // Only read if AWS_SDK_LOAD_CONFIG is also set
178	}
179	profileEnvKeys = []string{
180		"AWS_PROFILE",
181		"AWS_DEFAULT_PROFILE", // Only read if AWS_SDK_LOAD_CONFIG is also set
182	}
183	sharedCredsFileEnvKey = []string{
184		"AWS_SHARED_CREDENTIALS_FILE",
185	}
186	sharedConfigFileEnvKey = []string{
187		"AWS_CONFIG_FILE",
188	}
189	webIdentityTokenFilePathEnvKey = []string{
190		"AWS_WEB_IDENTITY_TOKEN_FILE",
191	}
192	roleARNEnvKey = []string{
193		"AWS_ROLE_ARN",
194	}
195	roleSessionNameEnvKey = []string{
196		"AWS_ROLE_SESSION_NAME",
197	}
198	stsRegionalEndpointKey = []string{
199		"AWS_STS_REGIONAL_ENDPOINTS",
200	}
201	s3UsEast1RegionalEndpoint = []string{
202		"AWS_S3_US_EAST_1_REGIONAL_ENDPOINT",
203	}
204)
205
206// loadEnvConfig retrieves the SDK's environment configuration.
207// See `envConfig` for the values that will be retrieved.
208//
209// If the environment variable `AWS_SDK_LOAD_CONFIG` is set to a truthy value
210// the shared SDK config will be loaded in addition to the SDK's specific
211// configuration values.
212func loadEnvConfig() (envConfig, error) {
213	enableSharedConfig, _ := strconv.ParseBool(os.Getenv("AWS_SDK_LOAD_CONFIG"))
214	return envConfigLoad(enableSharedConfig)
215}
216
217// loadEnvSharedConfig retrieves the SDK's environment configuration, and the
218// SDK shared config. See `envConfig` for the values that will be retrieved.
219//
220// Loads the shared configuration in addition to the SDK's specific configuration.
221// This will load the same values as `loadEnvConfig` if the `AWS_SDK_LOAD_CONFIG`
222// environment variable is set.
223func loadSharedEnvConfig() (envConfig, error) {
224	return envConfigLoad(true)
225}
226
227func envConfigLoad(enableSharedConfig bool) (envConfig, error) {
228	cfg := envConfig{}
229
230	cfg.EnableSharedConfig = enableSharedConfig
231
232	// Static environment credentials
233	var creds credentials.Value
234	setFromEnvVal(&creds.AccessKeyID, credAccessEnvKey)
235	setFromEnvVal(&creds.SecretAccessKey, credSecretEnvKey)
236	setFromEnvVal(&creds.SessionToken, credSessionEnvKey)
237	if creds.HasKeys() {
238		// Require logical grouping of credentials
239		creds.ProviderName = EnvProviderName
240		cfg.Creds = creds
241	}
242
243	// Role Metadata
244	setFromEnvVal(&cfg.RoleARN, roleARNEnvKey)
245	setFromEnvVal(&cfg.RoleSessionName, roleSessionNameEnvKey)
246
247	// Web identity environment variables
248	setFromEnvVal(&cfg.WebIdentityTokenFilePath, webIdentityTokenFilePathEnvKey)
249
250	// CSM environment variables
251	setFromEnvVal(&cfg.csmEnabled, csmEnabledEnvKey)
252	setFromEnvVal(&cfg.CSMHost, csmHostEnvKey)
253	setFromEnvVal(&cfg.CSMPort, csmPortEnvKey)
254	setFromEnvVal(&cfg.CSMClientID, csmClientIDEnvKey)
255
256	if len(cfg.csmEnabled) != 0 {
257		v, _ := strconv.ParseBool(cfg.csmEnabled)
258		cfg.CSMEnabled = &v
259	}
260
261	regionKeys := regionEnvKeys
262	profileKeys := profileEnvKeys
263	if !cfg.EnableSharedConfig {
264		regionKeys = regionKeys[:1]
265		profileKeys = profileKeys[:1]
266	}
267
268	setFromEnvVal(&cfg.Region, regionKeys)
269	setFromEnvVal(&cfg.Profile, profileKeys)
270
271	// endpoint discovery is in reference to it being enabled.
272	setFromEnvVal(&cfg.enableEndpointDiscovery, enableEndpointDiscoveryEnvKey)
273	if len(cfg.enableEndpointDiscovery) > 0 {
274		cfg.EnableEndpointDiscovery = aws.Bool(cfg.enableEndpointDiscovery != "false")
275	}
276
277	setFromEnvVal(&cfg.SharedCredentialsFile, sharedCredsFileEnvKey)
278	setFromEnvVal(&cfg.SharedConfigFile, sharedConfigFileEnvKey)
279
280	if len(cfg.SharedCredentialsFile) == 0 {
281		cfg.SharedCredentialsFile = defaults.SharedCredentialsFilename()
282	}
283	if len(cfg.SharedConfigFile) == 0 {
284		cfg.SharedConfigFile = defaults.SharedConfigFilename()
285	}
286
287	cfg.CustomCABundle = os.Getenv("AWS_CA_BUNDLE")
288
289	var err error
290	// STS Regional Endpoint variable
291	for _, k := range stsRegionalEndpointKey {
292		if v := os.Getenv(k); len(v) != 0 {
293			cfg.STSRegionalEndpoint, err = endpoints.GetSTSRegionalEndpoint(v)
294			if err != nil {
295				return cfg, fmt.Errorf("failed to load, %v from env config, %v", k, err)
296			}
297		}
298	}
299
300	// S3 Regional Endpoint variable
301	for _, k := range s3UsEast1RegionalEndpoint {
302		if v := os.Getenv(k); len(v) != 0 {
303			cfg.S3UsEast1RegionalEndpoint, err = endpoints.GetS3UsEast1RegionalEndpoint(v)
304			if err != nil {
305				return cfg, fmt.Errorf("failed to load, %v from env config, %v", k, err)
306			}
307		}
308	}
309
310	return cfg, nil
311}
312
313func setFromEnvVal(dst *string, keys []string) {
314	for _, k := range keys {
315		if v := os.Getenv(k); len(v) != 0 {
316			*dst = v
317			break
318		}
319	}
320}
321