mirror of
https://github.com/aptly-dev/aptly.git
synced 2026-07-27 13:57:46 +00:00
Conver to regular Go vendor + dep tool
This commit is contained in:
+319
@@ -0,0 +1,319 @@
|
||||
package session
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func createTLSServer(cert, key []byte, done <-chan struct{}) (*httptest.Server, error) {
|
||||
c, err := tls.X509KeyPair(cert, key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
s.TLS = &tls.Config{
|
||||
Certificates: []tls.Certificate{c},
|
||||
}
|
||||
s.TLS.BuildNameToCertificate()
|
||||
s.StartTLS()
|
||||
|
||||
go func() {
|
||||
<-done
|
||||
s.Close()
|
||||
}()
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func setupTestCAFile(b []byte) (string, error) {
|
||||
bundleFile, err := ioutil.TempFile(os.TempDir(), "aws-sdk-go-session-test")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
_, err = bundleFile.Write(b)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
defer bundleFile.Close()
|
||||
return bundleFile.Name(), nil
|
||||
}
|
||||
|
||||
func TestNewSession_WithCustomCABundle_Env(t *testing.T) {
|
||||
oldEnv := initSessionTestEnv()
|
||||
defer popEnv(oldEnv)
|
||||
|
||||
done := make(chan struct{})
|
||||
server, err := createTLSServer(testTLSBundleCert, testTLSBundleKey, done)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Write bundle to file
|
||||
caFilename, err := setupTestCAFile(testTLSBundleCA)
|
||||
defer func() {
|
||||
os.Remove(caFilename)
|
||||
}()
|
||||
assert.NoError(t, err)
|
||||
|
||||
os.Setenv("AWS_CA_BUNDLE", caFilename)
|
||||
|
||||
s, err := NewSession(&aws.Config{
|
||||
HTTPClient: &http.Client{},
|
||||
Endpoint: aws.String(server.URL),
|
||||
Region: aws.String("mock-region"),
|
||||
Credentials: credentials.AnonymousCredentials,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, s)
|
||||
|
||||
req, _ := http.NewRequest("GET", *s.Config.Endpoint, nil)
|
||||
resp, err := s.Config.HTTPClient.Do(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
}
|
||||
|
||||
func TestNewSession_WithCustomCABundle_EnvNotExists(t *testing.T) {
|
||||
oldEnv := initSessionTestEnv()
|
||||
defer popEnv(oldEnv)
|
||||
|
||||
os.Setenv("AWS_CA_BUNDLE", "file-not-exists")
|
||||
|
||||
s, err := NewSession()
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, "LoadCustomCABundleError", err.(awserr.Error).Code())
|
||||
assert.Nil(t, s)
|
||||
}
|
||||
|
||||
func TestNewSession_WithCustomCABundle_Option(t *testing.T) {
|
||||
oldEnv := initSessionTestEnv()
|
||||
defer popEnv(oldEnv)
|
||||
|
||||
done := make(chan struct{})
|
||||
server, err := createTLSServer(testTLSBundleCert, testTLSBundleKey, done)
|
||||
assert.NoError(t, err)
|
||||
|
||||
s, err := NewSessionWithOptions(Options{
|
||||
Config: aws.Config{
|
||||
HTTPClient: &http.Client{},
|
||||
Endpoint: aws.String(server.URL),
|
||||
Region: aws.String("mock-region"),
|
||||
Credentials: credentials.AnonymousCredentials,
|
||||
},
|
||||
CustomCABundle: bytes.NewReader(testTLSBundleCA),
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, s)
|
||||
|
||||
req, _ := http.NewRequest("GET", *s.Config.Endpoint, nil)
|
||||
resp, err := s.Config.HTTPClient.Do(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
}
|
||||
|
||||
func TestNewSession_WithCustomCABundle_OptionPriority(t *testing.T) {
|
||||
oldEnv := initSessionTestEnv()
|
||||
defer popEnv(oldEnv)
|
||||
|
||||
done := make(chan struct{})
|
||||
server, err := createTLSServer(testTLSBundleCert, testTLSBundleKey, done)
|
||||
assert.NoError(t, err)
|
||||
|
||||
os.Setenv("AWS_CA_BUNDLE", "file-not-exists")
|
||||
|
||||
s, err := NewSessionWithOptions(Options{
|
||||
Config: aws.Config{
|
||||
HTTPClient: &http.Client{},
|
||||
Endpoint: aws.String(server.URL),
|
||||
Region: aws.String("mock-region"),
|
||||
Credentials: credentials.AnonymousCredentials,
|
||||
},
|
||||
CustomCABundle: bytes.NewReader(testTLSBundleCA),
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, s)
|
||||
|
||||
req, _ := http.NewRequest("GET", *s.Config.Endpoint, nil)
|
||||
resp, err := s.Config.HTTPClient.Do(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
}
|
||||
|
||||
type mockRoundTripper struct{}
|
||||
|
||||
func (m *mockRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func TestNewSession_WithCustomCABundle_UnsupportedTransport(t *testing.T) {
|
||||
oldEnv := initSessionTestEnv()
|
||||
defer popEnv(oldEnv)
|
||||
|
||||
s, err := NewSessionWithOptions(Options{
|
||||
Config: aws.Config{
|
||||
HTTPClient: &http.Client{
|
||||
Transport: &mockRoundTripper{},
|
||||
},
|
||||
},
|
||||
CustomCABundle: bytes.NewReader(testTLSBundleCA),
|
||||
})
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, "LoadCustomCABundleError", err.(awserr.Error).Code())
|
||||
assert.Contains(t, err.(awserr.Error).Message(), "transport unsupported type")
|
||||
assert.Nil(t, s)
|
||||
}
|
||||
|
||||
func TestNewSession_WithCustomCABundle_TransportSet(t *testing.T) {
|
||||
oldEnv := initSessionTestEnv()
|
||||
defer popEnv(oldEnv)
|
||||
|
||||
done := make(chan struct{})
|
||||
server, err := createTLSServer(testTLSBundleCert, testTLSBundleKey, done)
|
||||
assert.NoError(t, err)
|
||||
|
||||
s, err := NewSessionWithOptions(Options{
|
||||
Config: aws.Config{
|
||||
Endpoint: aws.String(server.URL),
|
||||
Region: aws.String("mock-region"),
|
||||
Credentials: credentials.AnonymousCredentials,
|
||||
HTTPClient: &http.Client{
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
DualStack: true,
|
||||
}).Dial,
|
||||
TLSHandshakeTimeout: 2 * time.Second,
|
||||
},
|
||||
},
|
||||
},
|
||||
CustomCABundle: bytes.NewReader(testTLSBundleCA),
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, s)
|
||||
|
||||
req, _ := http.NewRequest("GET", *s.Config.Endpoint, nil)
|
||||
resp, err := s.Config.HTTPClient.Do(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
}
|
||||
|
||||
/* Cert generation steps
|
||||
# Create the CA key
|
||||
openssl genrsa -des3 -out ca.key 1024
|
||||
|
||||
# Create the CA Cert
|
||||
openssl req -new -sha256 -x509 -days 3650 \
|
||||
-subj "/C=GO/ST=Gopher/O=Testing ROOT CA" \
|
||||
-key ca.key -out ca.crt
|
||||
|
||||
# Create config
|
||||
cat > csr_details.txt <<-EOF
|
||||
|
||||
[req]
|
||||
default_bits = 1024
|
||||
prompt = no
|
||||
default_md = sha256
|
||||
req_extensions = SAN
|
||||
distinguished_name = dn
|
||||
|
||||
[ dn ]
|
||||
C=GO
|
||||
ST=Gopher
|
||||
O=Testing Certificate
|
||||
OU=Testing IP
|
||||
|
||||
[SAN]
|
||||
subjectAltName = IP:127.0.0.1
|
||||
EOF
|
||||
|
||||
# Create certificate signing request
|
||||
openssl req -new -sha256 -nodes -newkey rsa:1024 \
|
||||
-config <( cat csr_details.txt ) \
|
||||
-keyout ia.key -out ia.csr
|
||||
|
||||
# Create a signed certificate
|
||||
openssl x509 -req -days 3650 \
|
||||
-CAcreateserial \
|
||||
-extfile <( cat csr_details.txt ) \
|
||||
-extensions SAN \
|
||||
-CA ca.crt -CAkey ca.key -in ia.csr -out ia.crt
|
||||
|
||||
# Verify
|
||||
openssl req -noout -text -in ia.csr
|
||||
openssl x509 -noout -text -in ia.crt
|
||||
*/
|
||||
var (
|
||||
// ca.crt
|
||||
testTLSBundleCA = []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIICiTCCAfKgAwIBAgIJAJ5X1olt05XjMA0GCSqGSIb3DQEBCwUAMDgxCzAJBgNV
|
||||
BAYTAkdPMQ8wDQYDVQQIEwZHb3BoZXIxGDAWBgNVBAoTD1Rlc3RpbmcgUk9PVCBD
|
||||
QTAeFw0xNzAzMDkwMDAyMDZaFw0yNzAzMDcwMDAyMDZaMDgxCzAJBgNVBAYTAkdP
|
||||
MQ8wDQYDVQQIEwZHb3BoZXIxGDAWBgNVBAoTD1Rlc3RpbmcgUk9PVCBDQTCBnzAN
|
||||
BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAw/8DN+t9XQR60jx42rsQ2WE2Dx85rb3n
|
||||
GQxnKZZLNddsT8rDyxJNP18aFalbRbFlyln5fxWxZIblu9Xkm/HRhOpbSimSqo1y
|
||||
uDx21NVZ1YsOvXpHby71jx3gPrrhSc/t/zikhi++6D/C6m1CiIGuiJ0GBiJxtrub
|
||||
UBMXT0QtI2ECAwEAAaOBmjCBlzAdBgNVHQ4EFgQU8XG3X/YHBA6T04kdEkq6+4GV
|
||||
YykwaAYDVR0jBGEwX4AU8XG3X/YHBA6T04kdEkq6+4GVYymhPKQ6MDgxCzAJBgNV
|
||||
BAYTAkdPMQ8wDQYDVQQIEwZHb3BoZXIxGDAWBgNVBAoTD1Rlc3RpbmcgUk9PVCBD
|
||||
QYIJAJ5X1olt05XjMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADgYEAeILv
|
||||
z49+uxmPcfOZzonuOloRcpdvyjiXblYxbzz6ch8GsE7Q886FTZbvwbgLhzdwSVgG
|
||||
G8WHkodDUsymVepdqAamS3f8PdCUk8xIk9mop8LgaB9Ns0/TssxDvMr3sOD2Grb3
|
||||
xyWymTWMcj6uCiEBKtnUp4rPiefcvCRYZ17/hLE=
|
||||
-----END CERTIFICATE-----
|
||||
`)
|
||||
|
||||
// ai.crt
|
||||
testTLSBundleCert = []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIICGjCCAYOgAwIBAgIJAIIu+NOoxxM0MA0GCSqGSIb3DQEBBQUAMDgxCzAJBgNV
|
||||
BAYTAkdPMQ8wDQYDVQQIEwZHb3BoZXIxGDAWBgNVBAoTD1Rlc3RpbmcgUk9PVCBD
|
||||
QTAeFw0xNzAzMDkwMDAzMTRaFw0yNzAzMDcwMDAzMTRaMFExCzAJBgNVBAYTAkdP
|
||||
MQ8wDQYDVQQIDAZHb3BoZXIxHDAaBgNVBAoME1Rlc3RpbmcgQ2VydGlmaWNhdGUx
|
||||
EzARBgNVBAsMClRlc3RpbmcgSVAwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGB
|
||||
AN1hWHeioo/nASvbrjwCQzXCiWiEzGkw353NxsAB54/NqDL3LXNATtiSJu8kJBrm
|
||||
Ah12IFLtWLGXjGjjYlHbQWnOR6awveeXnQZukJyRWh7m/Qlt9Ho0CgZE1U+832ac
|
||||
5GWVldNxW1Lz4I+W9/ehzqe8I80RS6eLEKfUFXGiW+9RAgMBAAGjEzARMA8GA1Ud
|
||||
EQQIMAaHBH8AAAEwDQYJKoZIhvcNAQEFBQADgYEAdF4WQHfVdPCbgv9sxgJjcR1H
|
||||
Hgw9rZ47gO1IiIhzglnLXQ6QuemRiHeYFg4kjcYBk1DJguxzDTGnUwhUXOibAB+S
|
||||
zssmrkdYYvn9aUhjc3XK3tjAoDpsPpeBeTBamuUKDHoH/dNRXxerZ8vu6uPR3Pgs
|
||||
5v/KCV6IAEcvNyOXMPo=
|
||||
-----END CERTIFICATE-----
|
||||
`)
|
||||
|
||||
// ai.key
|
||||
testTLSBundleKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICXAIBAAKBgQDdYVh3oqKP5wEr2648AkM1wolohMxpMN+dzcbAAeePzagy9y1z
|
||||
QE7YkibvJCQa5gIddiBS7Vixl4xo42JR20FpzkemsL3nl50GbpCckVoe5v0JbfR6
|
||||
NAoGRNVPvN9mnORllZXTcVtS8+CPlvf3oc6nvCPNEUunixCn1BVxolvvUQIDAQAB
|
||||
AoGBAMISrcirddGrlLZLLrKC1ULS2T0cdkqdQtwHYn4+7S5+/z42vMx1iumHLsSk
|
||||
rVY7X41OWkX4trFxhvEIrc/O48bo2zw78P7flTxHy14uxXnllU8cLThE29SlUU7j
|
||||
AVBNxJZMsXMlS/DowwD4CjFe+x4Pu9wZcReF2Z9ntzMpySABAkEA+iWoJCPE2JpS
|
||||
y78q3HYYgpNY3gF3JqQ0SI/zTNkb3YyEIUffEYq0Y9pK13HjKtdsSuX4osTIhQkS
|
||||
+UgRp6tCAQJBAOKPYTfQ2FX8ijgUpHZRuEAVaxASAS0UATiLgzXxLvOh/VC2at5x
|
||||
wjOX6sD65pPz/0D8Qj52Cq6Q1TQ+377SDVECQAIy0od+yPweXxvrUjUd1JlRMjbB
|
||||
TIrKZqs8mKbUQapw0bh5KTy+O1elU4MRPS3jNtBxtP25PQnuSnxmZcFTgAECQFzg
|
||||
DiiFcsn9FuRagfkHExMiNJuH5feGxeFaP9WzI144v9GAllrOI6Bm3JNzx2ZLlg4b
|
||||
20Qju8lIEj6yr6JYFaECQHM1VSojGRKpOl9Ox/R4yYSA9RV5Gyn00/aJNxVYyPD5
|
||||
i3acL2joQm2kLD/LO8paJ4+iQdRXCOMMIpjxSNjGQjQ=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
`)
|
||||
)
|
||||
+274
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
Package session provides configuration for the SDK's service clients.
|
||||
|
||||
Sessions can be shared across all service clients that share the same base
|
||||
configuration. The Session is built from the SDK's default configuration and
|
||||
request handlers.
|
||||
|
||||
Sessions should be cached when possible, because creating a new Session will
|
||||
load all configuration values from the environment, and config files each time
|
||||
the Session is created. Sharing the Session value across all of your service
|
||||
clients will ensure the configuration is loaded the fewest number of times possible.
|
||||
|
||||
Concurrency
|
||||
|
||||
Sessions are safe to use concurrently as long as the Session is not being
|
||||
modified. The SDK will not modify the Session once the Session has been created.
|
||||
Creating service clients concurrently from a shared Session is safe.
|
||||
|
||||
Sessions from Shared Config
|
||||
|
||||
Sessions can be created using the method above that will only load the
|
||||
additional config if the AWS_SDK_LOAD_CONFIG environment variable is set.
|
||||
Alternatively you can explicitly create a Session with shared config enabled.
|
||||
To do this you can use NewSessionWithOptions to configure how the Session will
|
||||
be created. Using the NewSessionWithOptions with SharedConfigState set to
|
||||
SharedConfigEnabled will create the session as if the AWS_SDK_LOAD_CONFIG
|
||||
environment variable was set.
|
||||
|
||||
Creating Sessions
|
||||
|
||||
When creating Sessions optional aws.Config values can be passed in that will
|
||||
override the default, or loaded config values the Session is being created
|
||||
with. This allows you to provide additional, or case based, configuration
|
||||
as needed.
|
||||
|
||||
By default NewSession will only load credentials from the shared credentials
|
||||
file (~/.aws/credentials). If the AWS_SDK_LOAD_CONFIG environment variable is
|
||||
set to a truthy value the Session will be created from the configuration
|
||||
values from the shared config (~/.aws/config) and shared credentials
|
||||
(~/.aws/credentials) files. See the section Sessions from Shared Config for
|
||||
more information.
|
||||
|
||||
Create a Session with the default config and request handlers. With credentials
|
||||
region, and profile loaded from the environment and shared config automatically.
|
||||
Requires the AWS_PROFILE to be set, or "default" is used.
|
||||
|
||||
// Create Session
|
||||
sess := session.Must(session.NewSession())
|
||||
|
||||
// Create a Session with a custom region
|
||||
sess := session.Must(session.NewSession(&aws.Config{
|
||||
Region: aws.String("us-east-1"),
|
||||
}))
|
||||
|
||||
// Create a S3 client instance from a session
|
||||
sess := session.Must(session.NewSession())
|
||||
|
||||
svc := s3.New(sess)
|
||||
|
||||
Create Session With Option Overrides
|
||||
|
||||
In addition to NewSession, Sessions can be created using NewSessionWithOptions.
|
||||
This func allows you to control and override how the Session will be created
|
||||
through code instead of being driven by environment variables only.
|
||||
|
||||
Use NewSessionWithOptions when you want to provide the config profile, or
|
||||
override the shared config state (AWS_SDK_LOAD_CONFIG).
|
||||
|
||||
// Equivalent to session.NewSession()
|
||||
sess := session.Must(session.NewSessionWithOptions(session.Options{
|
||||
// Options
|
||||
}))
|
||||
|
||||
// Specify profile to load for the session's config
|
||||
sess := session.Must(session.NewSessionWithOptions(session.Options{
|
||||
Profile: "profile_name",
|
||||
}))
|
||||
|
||||
// Specify profile for config and region for requests
|
||||
sess := session.Must(session.NewSessionWithOptions(session.Options{
|
||||
Config: aws.Config{Region: aws.String("us-east-1")},
|
||||
Profile: "profile_name",
|
||||
}))
|
||||
|
||||
// Force enable Shared Config support
|
||||
sess := session.Must(session.NewSessionWithOptions(session.Options{
|
||||
SharedConfigState: SharedConfigEnable,
|
||||
}))
|
||||
|
||||
Adding Handlers
|
||||
|
||||
You can add handlers to a session for processing HTTP requests. All service
|
||||
clients that use the session inherit the handlers. For example, the following
|
||||
handler logs every request and its payload made by a service client:
|
||||
|
||||
// Create a session, and add additional handlers for all service
|
||||
// clients created with the Session to inherit. Adds logging handler.
|
||||
sess := session.Must(session.NewSession())
|
||||
|
||||
sess.Handlers.Send.PushFront(func(r *request.Request) {
|
||||
// Log every request made and its payload
|
||||
logger.Println("Request: %s/%s, Payload: %s",
|
||||
r.ClientInfo.ServiceName, r.Operation, r.Params)
|
||||
})
|
||||
|
||||
Deprecated "New" function
|
||||
|
||||
The New session function has been deprecated because it does not provide good
|
||||
way to return errors that occur when loading the configuration files and values.
|
||||
Because of this, NewSession was created so errors can be retrieved when
|
||||
creating a session fails.
|
||||
|
||||
Shared Config Fields
|
||||
|
||||
By default the SDK will only load the shared credentials file's (~/.aws/credentials)
|
||||
credentials values, and all other config is provided by the environment variables,
|
||||
SDK defaults, and user provided aws.Config values.
|
||||
|
||||
If the AWS_SDK_LOAD_CONFIG environment variable is set, or SharedConfigEnable
|
||||
option is used to create the Session the full shared config values will be
|
||||
loaded. This includes credentials, region, and support for assume role. In
|
||||
addition the Session will load its configuration from both the shared config
|
||||
file (~/.aws/config) and shared credentials file (~/.aws/credentials). Both
|
||||
files have the same format.
|
||||
|
||||
If both config files are present the configuration from both files will be
|
||||
read. The Session will be created from configuration values from the shared
|
||||
credentials file (~/.aws/credentials) over those in the shared credentials
|
||||
file (~/.aws/config).
|
||||
|
||||
Credentials are the values the SDK should use for authenticating requests with
|
||||
AWS Services. They arfrom a configuration file will need to include both
|
||||
aws_access_key_id and aws_secret_access_key must be provided together in the
|
||||
same file to be considered valid. The values will be ignored if not a complete
|
||||
group. aws_session_token is an optional field that can be provided if both of
|
||||
the other two fields are also provided.
|
||||
|
||||
aws_access_key_id = AKID
|
||||
aws_secret_access_key = SECRET
|
||||
aws_session_token = TOKEN
|
||||
|
||||
Assume Role values allow you to configure the SDK to assume an IAM role using
|
||||
a set of credentials provided in a config file via the source_profile field.
|
||||
Both "role_arn" and "source_profile" are required. The SDK supports assuming
|
||||
a role with MFA token if the session option AssumeRoleTokenProvider
|
||||
is set.
|
||||
|
||||
role_arn = arn:aws:iam::<account_number>:role/<role_name>
|
||||
source_profile = profile_with_creds
|
||||
external_id = 1234
|
||||
mfa_serial = <serial or mfa arn>
|
||||
role_session_name = session_name
|
||||
|
||||
Region is the region the SDK should use for looking up AWS service endpoints
|
||||
and signing requests.
|
||||
|
||||
region = us-east-1
|
||||
|
||||
Assume Role with MFA token
|
||||
|
||||
To create a session with support for assuming an IAM role with MFA set the
|
||||
session option AssumeRoleTokenProvider to a function that will prompt for the
|
||||
MFA token code when the SDK assumes the role and refreshes the role's credentials.
|
||||
This allows you to configure the SDK via the shared config to assumea role
|
||||
with MFA tokens.
|
||||
|
||||
In order for the SDK to assume a role with MFA the SharedConfigState
|
||||
session option must be set to SharedConfigEnable, or AWS_SDK_LOAD_CONFIG
|
||||
environment variable set.
|
||||
|
||||
The shared configuration instructs the SDK to assume an IAM role with MFA
|
||||
when the mfa_serial configuration field is set in the shared config
|
||||
(~/.aws/config) or shared credentials (~/.aws/credentials) file.
|
||||
|
||||
If mfa_serial is set in the configuration, the SDK will assume the role, and
|
||||
the AssumeRoleTokenProvider session option is not set an an error will
|
||||
be returned when creating the session.
|
||||
|
||||
sess := session.Must(session.NewSessionWithOptions(session.Options{
|
||||
AssumeRoleTokenProvider: stscreds.StdinTokenProvider,
|
||||
}))
|
||||
|
||||
// Create service client value configured for credentials
|
||||
// from assumed role.
|
||||
svc := s3.New(sess)
|
||||
|
||||
To setup assume role outside of a session see the stscrds.AssumeRoleProvider
|
||||
documentation.
|
||||
|
||||
Environment Variables
|
||||
|
||||
When a Session is created several environment variables can be set to adjust
|
||||
how the SDK functions, and what configuration data it loads when creating
|
||||
Sessions. All environment values are optional, but some values like credentials
|
||||
require multiple of the values to set or the partial values will be ignored.
|
||||
All environment variable values are strings unless otherwise noted.
|
||||
|
||||
Environment configuration values. If set both Access Key ID and Secret Access
|
||||
Key must be provided. Session Token and optionally also be provided, but is
|
||||
not required.
|
||||
|
||||
# Access Key ID
|
||||
AWS_ACCESS_KEY_ID=AKID
|
||||
AWS_ACCESS_KEY=AKID # only read if AWS_ACCESS_KEY_ID is not set.
|
||||
|
||||
# Secret Access Key
|
||||
AWS_SECRET_ACCESS_KEY=SECRET
|
||||
AWS_SECRET_KEY=SECRET=SECRET # only read if AWS_SECRET_ACCESS_KEY is not set.
|
||||
|
||||
# Session Token
|
||||
AWS_SESSION_TOKEN=TOKEN
|
||||
|
||||
Region value will instruct the SDK where to make service API requests to. If is
|
||||
not provided in the environment the region must be provided before a service
|
||||
client request is made.
|
||||
|
||||
AWS_REGION=us-east-1
|
||||
|
||||
# AWS_DEFAULT_REGION is only read if AWS_SDK_LOAD_CONFIG is also set,
|
||||
# and AWS_REGION is not also set.
|
||||
AWS_DEFAULT_REGION=us-east-1
|
||||
|
||||
Profile name the SDK should load use when loading shared config from the
|
||||
configuration files. If not provided "default" will be used as the profile name.
|
||||
|
||||
AWS_PROFILE=my_profile
|
||||
|
||||
# AWS_DEFAULT_PROFILE is only read if AWS_SDK_LOAD_CONFIG is also set,
|
||||
# and AWS_PROFILE is not also set.
|
||||
AWS_DEFAULT_PROFILE=my_profile
|
||||
|
||||
SDK load config instructs the SDK to load the shared config in addition to
|
||||
shared credentials. This also expands the configuration loaded so the shared
|
||||
credentials will have parity with the shared config file. This also enables
|
||||
Region and Profile support for the AWS_DEFAULT_REGION and AWS_DEFAULT_PROFILE
|
||||
env values as well.
|
||||
|
||||
AWS_SDK_LOAD_CONFIG=1
|
||||
|
||||
Shared credentials file path can be set to instruct the SDK to use an alternative
|
||||
file for the shared credentials. If not set the file will be loaded from
|
||||
$HOME/.aws/credentials on Linux/Unix based systems, and
|
||||
%USERPROFILE%\.aws\credentials on Windows.
|
||||
|
||||
AWS_SHARED_CREDENTIALS_FILE=$HOME/my_shared_credentials
|
||||
|
||||
Shared config file path can be set to instruct the SDK to use an alternative
|
||||
file for the shared config. If not set the file will be loaded from
|
||||
$HOME/.aws/config on Linux/Unix based systems, and
|
||||
%USERPROFILE%\.aws\config on Windows.
|
||||
|
||||
AWS_CONFIG_FILE=$HOME/my_shared_config
|
||||
|
||||
Path to a custom Credentials Authority (CA) bundle PEM file that the SDK
|
||||
will use instead of the default system's root CA bundle. Use this only
|
||||
if you want to replace the CA bundle the SDK uses for TLS requests.
|
||||
|
||||
AWS_CA_BUNDLE=$HOME/my_custom_ca_bundle
|
||||
|
||||
Enabling this option will attempt to merge the Transport into the SDK's HTTP
|
||||
client. If the client's Transport is not a http.Transport an error will be
|
||||
returned. If the Transport's TLS config is set this option will cause the SDK
|
||||
to overwrite the Transport's TLS config's RootCAs value. If the CA bundle file
|
||||
contains multiple certificates all of them will be loaded.
|
||||
|
||||
The Session option CustomCABundle is also available when creating sessions
|
||||
to also enable this feature. CustomCABundle session option field has priority
|
||||
over the AWS_CA_BUNDLE environment variable, and will be used if both are set.
|
||||
|
||||
Setting a custom HTTPClient in the aws.Config options will override this setting.
|
||||
To use this option and custom HTTP client, the HTTP client needs to be provided
|
||||
when creating the session. Not the service client.
|
||||
*/
|
||||
package session
|
||||
+208
@@ -0,0 +1,208 @@
|
||||
package session
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
)
|
||||
|
||||
// envConfig is a collection of environment values the SDK will read
|
||||
// setup config from. All environment values are optional. But some values
|
||||
// such as credentials require multiple values to be complete or the values
|
||||
// will be ignored.
|
||||
type envConfig struct {
|
||||
// Environment configuration values. If set both Access Key ID and Secret Access
|
||||
// Key must be provided. Session Token and optionally also be provided, but is
|
||||
// not required.
|
||||
//
|
||||
// # Access Key ID
|
||||
// AWS_ACCESS_KEY_ID=AKID
|
||||
// AWS_ACCESS_KEY=AKID # only read if AWS_ACCESS_KEY_ID is not set.
|
||||
//
|
||||
// # Secret Access Key
|
||||
// AWS_SECRET_ACCESS_KEY=SECRET
|
||||
// AWS_SECRET_KEY=SECRET=SECRET # only read if AWS_SECRET_ACCESS_KEY is not set.
|
||||
//
|
||||
// # Session Token
|
||||
// AWS_SESSION_TOKEN=TOKEN
|
||||
Creds credentials.Value
|
||||
|
||||
// Region value will instruct the SDK where to make service API requests to. If is
|
||||
// not provided in the environment the region must be provided before a service
|
||||
// client request is made.
|
||||
//
|
||||
// AWS_REGION=us-east-1
|
||||
//
|
||||
// # AWS_DEFAULT_REGION is only read if AWS_SDK_LOAD_CONFIG is also set,
|
||||
// # and AWS_REGION is not also set.
|
||||
// AWS_DEFAULT_REGION=us-east-1
|
||||
Region string
|
||||
|
||||
// Profile name the SDK should load use when loading shared configuration from the
|
||||
// shared configuration files. If not provided "default" will be used as the
|
||||
// profile name.
|
||||
//
|
||||
// AWS_PROFILE=my_profile
|
||||
//
|
||||
// # AWS_DEFAULT_PROFILE is only read if AWS_SDK_LOAD_CONFIG is also set,
|
||||
// # and AWS_PROFILE is not also set.
|
||||
// AWS_DEFAULT_PROFILE=my_profile
|
||||
Profile string
|
||||
|
||||
// SDK load config instructs the SDK to load the shared config in addition to
|
||||
// shared credentials. This also expands the configuration loaded from the shared
|
||||
// credentials to have parity with the shared config file. This also enables
|
||||
// Region and Profile support for the AWS_DEFAULT_REGION and AWS_DEFAULT_PROFILE
|
||||
// env values as well.
|
||||
//
|
||||
// AWS_SDK_LOAD_CONFIG=1
|
||||
EnableSharedConfig bool
|
||||
|
||||
// Shared credentials file path can be set to instruct the SDK to use an alternate
|
||||
// file for the shared credentials. If not set the file will be loaded from
|
||||
// $HOME/.aws/credentials on Linux/Unix based systems, and
|
||||
// %USERPROFILE%\.aws\credentials on Windows.
|
||||
//
|
||||
// AWS_SHARED_CREDENTIALS_FILE=$HOME/my_shared_credentials
|
||||
SharedCredentialsFile string
|
||||
|
||||
// Shared config file path can be set to instruct the SDK to use an alternate
|
||||
// file for the shared config. If not set the file will be loaded from
|
||||
// $HOME/.aws/config on Linux/Unix based systems, and
|
||||
// %USERPROFILE%\.aws\config on Windows.
|
||||
//
|
||||
// AWS_CONFIG_FILE=$HOME/my_shared_config
|
||||
SharedConfigFile string
|
||||
|
||||
// Sets the path to a custom Credentials Authroity (CA) Bundle PEM file
|
||||
// that the SDK will use instead of the the system's root CA bundle.
|
||||
// Only use this if you want to configure the SDK to use a custom set
|
||||
// of CAs.
|
||||
//
|
||||
// Enabling this option will attempt to merge the Transport
|
||||
// into the SDK's HTTP client. If the client's Transport is
|
||||
// not a http.Transport an error will be returned. If the
|
||||
// Transport's TLS config is set this option will cause the
|
||||
// SDK to overwrite the Transport's TLS config's RootCAs value.
|
||||
//
|
||||
// Setting a custom HTTPClient in the aws.Config options will override this setting.
|
||||
// To use this option and custom HTTP client, the HTTP client needs to be provided
|
||||
// when creating the session. Not the service client.
|
||||
//
|
||||
// AWS_CA_BUNDLE=$HOME/my_custom_ca_bundle
|
||||
CustomCABundle string
|
||||
}
|
||||
|
||||
var (
|
||||
credAccessEnvKey = []string{
|
||||
"AWS_ACCESS_KEY_ID",
|
||||
"AWS_ACCESS_KEY",
|
||||
}
|
||||
credSecretEnvKey = []string{
|
||||
"AWS_SECRET_ACCESS_KEY",
|
||||
"AWS_SECRET_KEY",
|
||||
}
|
||||
credSessionEnvKey = []string{
|
||||
"AWS_SESSION_TOKEN",
|
||||
}
|
||||
|
||||
regionEnvKeys = []string{
|
||||
"AWS_REGION",
|
||||
"AWS_DEFAULT_REGION", // Only read if AWS_SDK_LOAD_CONFIG is also set
|
||||
}
|
||||
profileEnvKeys = []string{
|
||||
"AWS_PROFILE",
|
||||
"AWS_DEFAULT_PROFILE", // Only read if AWS_SDK_LOAD_CONFIG is also set
|
||||
}
|
||||
)
|
||||
|
||||
// loadEnvConfig retrieves the SDK's environment configuration.
|
||||
// See `envConfig` for the values that will be retrieved.
|
||||
//
|
||||
// If the environment variable `AWS_SDK_LOAD_CONFIG` is set to a truthy value
|
||||
// the shared SDK config will be loaded in addition to the SDK's specific
|
||||
// configuration values.
|
||||
func loadEnvConfig() envConfig {
|
||||
enableSharedConfig, _ := strconv.ParseBool(os.Getenv("AWS_SDK_LOAD_CONFIG"))
|
||||
return envConfigLoad(enableSharedConfig)
|
||||
}
|
||||
|
||||
// loadEnvSharedConfig retrieves the SDK's environment configuration, and the
|
||||
// SDK shared config. See `envConfig` for the values that will be retrieved.
|
||||
//
|
||||
// Loads the shared configuration in addition to the SDK's specific configuration.
|
||||
// This will load the same values as `loadEnvConfig` if the `AWS_SDK_LOAD_CONFIG`
|
||||
// environment variable is set.
|
||||
func loadSharedEnvConfig() envConfig {
|
||||
return envConfigLoad(true)
|
||||
}
|
||||
|
||||
func envConfigLoad(enableSharedConfig bool) envConfig {
|
||||
cfg := envConfig{}
|
||||
|
||||
cfg.EnableSharedConfig = enableSharedConfig
|
||||
|
||||
setFromEnvVal(&cfg.Creds.AccessKeyID, credAccessEnvKey)
|
||||
setFromEnvVal(&cfg.Creds.SecretAccessKey, credSecretEnvKey)
|
||||
setFromEnvVal(&cfg.Creds.SessionToken, credSessionEnvKey)
|
||||
|
||||
// Require logical grouping of credentials
|
||||
if len(cfg.Creds.AccessKeyID) == 0 || len(cfg.Creds.SecretAccessKey) == 0 {
|
||||
cfg.Creds = credentials.Value{}
|
||||
} else {
|
||||
cfg.Creds.ProviderName = "EnvConfigCredentials"
|
||||
}
|
||||
|
||||
regionKeys := regionEnvKeys
|
||||
profileKeys := profileEnvKeys
|
||||
if !cfg.EnableSharedConfig {
|
||||
regionKeys = regionKeys[:1]
|
||||
profileKeys = profileKeys[:1]
|
||||
}
|
||||
|
||||
setFromEnvVal(&cfg.Region, regionKeys)
|
||||
setFromEnvVal(&cfg.Profile, profileKeys)
|
||||
|
||||
cfg.SharedCredentialsFile = sharedCredentialsFilename()
|
||||
cfg.SharedConfigFile = sharedConfigFilename()
|
||||
|
||||
cfg.CustomCABundle = os.Getenv("AWS_CA_BUNDLE")
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
func setFromEnvVal(dst *string, keys []string) {
|
||||
for _, k := range keys {
|
||||
if v := os.Getenv(k); len(v) > 0 {
|
||||
*dst = v
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sharedCredentialsFilename() string {
|
||||
if name := os.Getenv("AWS_SHARED_CREDENTIALS_FILE"); len(name) > 0 {
|
||||
return name
|
||||
}
|
||||
|
||||
return filepath.Join(userHomeDir(), ".aws", "credentials")
|
||||
}
|
||||
|
||||
func sharedConfigFilename() string {
|
||||
if name := os.Getenv("AWS_CONFIG_FILE"); len(name) > 0 {
|
||||
return name
|
||||
}
|
||||
|
||||
return filepath.Join(userHomeDir(), ".aws", "config")
|
||||
}
|
||||
|
||||
func userHomeDir() string {
|
||||
homeDir := os.Getenv("HOME") // *nix
|
||||
if len(homeDir) == 0 { // windows
|
||||
homeDir = os.Getenv("USERPROFILE")
|
||||
}
|
||||
|
||||
return homeDir
|
||||
}
|
||||
+291
@@ -0,0 +1,291 @@
|
||||
package session
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestLoadEnvConfig_Creds(t *testing.T) {
|
||||
env := stashEnv()
|
||||
defer popEnv(env)
|
||||
|
||||
cases := []struct {
|
||||
Env map[string]string
|
||||
Val credentials.Value
|
||||
}{
|
||||
{
|
||||
Env: map[string]string{
|
||||
"AWS_ACCESS_KEY": "AKID",
|
||||
},
|
||||
Val: credentials.Value{},
|
||||
},
|
||||
{
|
||||
Env: map[string]string{
|
||||
"AWS_ACCESS_KEY_ID": "AKID",
|
||||
},
|
||||
Val: credentials.Value{},
|
||||
},
|
||||
{
|
||||
Env: map[string]string{
|
||||
"AWS_SECRET_KEY": "SECRET",
|
||||
},
|
||||
Val: credentials.Value{},
|
||||
},
|
||||
{
|
||||
Env: map[string]string{
|
||||
"AWS_SECRET_ACCESS_KEY": "SECRET",
|
||||
},
|
||||
Val: credentials.Value{},
|
||||
},
|
||||
{
|
||||
Env: map[string]string{
|
||||
"AWS_ACCESS_KEY_ID": "AKID",
|
||||
"AWS_SECRET_ACCESS_KEY": "SECRET",
|
||||
},
|
||||
Val: credentials.Value{
|
||||
AccessKeyID: "AKID", SecretAccessKey: "SECRET",
|
||||
ProviderName: "EnvConfigCredentials",
|
||||
},
|
||||
},
|
||||
{
|
||||
Env: map[string]string{
|
||||
"AWS_ACCESS_KEY": "AKID",
|
||||
"AWS_SECRET_KEY": "SECRET",
|
||||
},
|
||||
Val: credentials.Value{
|
||||
AccessKeyID: "AKID", SecretAccessKey: "SECRET",
|
||||
ProviderName: "EnvConfigCredentials",
|
||||
},
|
||||
},
|
||||
{
|
||||
Env: map[string]string{
|
||||
"AWS_ACCESS_KEY": "AKID",
|
||||
"AWS_SECRET_KEY": "SECRET",
|
||||
"AWS_SESSION_TOKEN": "TOKEN",
|
||||
},
|
||||
Val: credentials.Value{
|
||||
AccessKeyID: "AKID", SecretAccessKey: "SECRET", SessionToken: "TOKEN",
|
||||
ProviderName: "EnvConfigCredentials",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
os.Clearenv()
|
||||
|
||||
for k, v := range c.Env {
|
||||
os.Setenv(k, v)
|
||||
}
|
||||
|
||||
cfg := loadEnvConfig()
|
||||
assert.Equal(t, c.Val, cfg.Creds)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadEnvConfig(t *testing.T) {
|
||||
env := stashEnv()
|
||||
defer popEnv(env)
|
||||
|
||||
cases := []struct {
|
||||
Env map[string]string
|
||||
Region, Profile string
|
||||
CustomCABundle string
|
||||
UseSharedConfigCall bool
|
||||
}{
|
||||
{
|
||||
Env: map[string]string{
|
||||
"AWS_REGION": "region",
|
||||
"AWS_PROFILE": "profile",
|
||||
},
|
||||
Region: "region", Profile: "profile",
|
||||
},
|
||||
{
|
||||
Env: map[string]string{
|
||||
"AWS_REGION": "region",
|
||||
"AWS_DEFAULT_REGION": "default_region",
|
||||
"AWS_PROFILE": "profile",
|
||||
"AWS_DEFAULT_PROFILE": "default_profile",
|
||||
},
|
||||
Region: "region", Profile: "profile",
|
||||
},
|
||||
{
|
||||
Env: map[string]string{
|
||||
"AWS_REGION": "region",
|
||||
"AWS_DEFAULT_REGION": "default_region",
|
||||
"AWS_PROFILE": "profile",
|
||||
"AWS_DEFAULT_PROFILE": "default_profile",
|
||||
"AWS_SDK_LOAD_CONFIG": "1",
|
||||
},
|
||||
Region: "region", Profile: "profile",
|
||||
},
|
||||
{
|
||||
Env: map[string]string{
|
||||
"AWS_DEFAULT_REGION": "default_region",
|
||||
"AWS_DEFAULT_PROFILE": "default_profile",
|
||||
},
|
||||
},
|
||||
{
|
||||
Env: map[string]string{
|
||||
"AWS_DEFAULT_REGION": "default_region",
|
||||
"AWS_DEFAULT_PROFILE": "default_profile",
|
||||
"AWS_SDK_LOAD_CONFIG": "1",
|
||||
},
|
||||
Region: "default_region", Profile: "default_profile",
|
||||
},
|
||||
{
|
||||
Env: map[string]string{
|
||||
"AWS_REGION": "region",
|
||||
"AWS_PROFILE": "profile",
|
||||
},
|
||||
Region: "region", Profile: "profile",
|
||||
UseSharedConfigCall: true,
|
||||
},
|
||||
{
|
||||
Env: map[string]string{
|
||||
"AWS_REGION": "region",
|
||||
"AWS_DEFAULT_REGION": "default_region",
|
||||
"AWS_PROFILE": "profile",
|
||||
"AWS_DEFAULT_PROFILE": "default_profile",
|
||||
},
|
||||
Region: "region", Profile: "profile",
|
||||
UseSharedConfigCall: true,
|
||||
},
|
||||
{
|
||||
Env: map[string]string{
|
||||
"AWS_REGION": "region",
|
||||
"AWS_DEFAULT_REGION": "default_region",
|
||||
"AWS_PROFILE": "profile",
|
||||
"AWS_DEFAULT_PROFILE": "default_profile",
|
||||
"AWS_SDK_LOAD_CONFIG": "1",
|
||||
},
|
||||
Region: "region", Profile: "profile",
|
||||
UseSharedConfigCall: true,
|
||||
},
|
||||
{
|
||||
Env: map[string]string{
|
||||
"AWS_DEFAULT_REGION": "default_region",
|
||||
"AWS_DEFAULT_PROFILE": "default_profile",
|
||||
},
|
||||
Region: "default_region", Profile: "default_profile",
|
||||
UseSharedConfigCall: true,
|
||||
},
|
||||
{
|
||||
Env: map[string]string{
|
||||
"AWS_DEFAULT_REGION": "default_region",
|
||||
"AWS_DEFAULT_PROFILE": "default_profile",
|
||||
"AWS_SDK_LOAD_CONFIG": "1",
|
||||
},
|
||||
Region: "default_region", Profile: "default_profile",
|
||||
UseSharedConfigCall: true,
|
||||
},
|
||||
{
|
||||
Env: map[string]string{
|
||||
"AWS_CA_BUNDLE": "custom_ca_bundle",
|
||||
},
|
||||
CustomCABundle: "custom_ca_bundle",
|
||||
},
|
||||
{
|
||||
Env: map[string]string{
|
||||
"AWS_CA_BUNDLE": "custom_ca_bundle",
|
||||
},
|
||||
CustomCABundle: "custom_ca_bundle",
|
||||
UseSharedConfigCall: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
os.Clearenv()
|
||||
|
||||
for k, v := range c.Env {
|
||||
os.Setenv(k, v)
|
||||
}
|
||||
|
||||
var cfg envConfig
|
||||
if c.UseSharedConfigCall {
|
||||
cfg = loadSharedEnvConfig()
|
||||
} else {
|
||||
cfg = loadEnvConfig()
|
||||
}
|
||||
|
||||
assert.Equal(t, c.Region, cfg.Region)
|
||||
assert.Equal(t, c.Profile, cfg.Profile)
|
||||
assert.Equal(t, c.CustomCABundle, cfg.CustomCABundle)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSharedCredsFilename(t *testing.T) {
|
||||
env := stashEnv()
|
||||
defer popEnv(env)
|
||||
|
||||
os.Setenv("USERPROFILE", "profile_dir")
|
||||
expect := filepath.Join("profile_dir", ".aws", "credentials")
|
||||
name := sharedCredentialsFilename()
|
||||
assert.Equal(t, expect, name)
|
||||
|
||||
os.Setenv("HOME", "home_dir")
|
||||
expect = filepath.Join("home_dir", ".aws", "credentials")
|
||||
name = sharedCredentialsFilename()
|
||||
assert.Equal(t, expect, name)
|
||||
|
||||
expect = filepath.Join("path/to/credentials/file")
|
||||
os.Setenv("AWS_SHARED_CREDENTIALS_FILE", expect)
|
||||
name = sharedCredentialsFilename()
|
||||
assert.Equal(t, expect, name)
|
||||
}
|
||||
|
||||
func TestSharedConfigFilename(t *testing.T) {
|
||||
env := stashEnv()
|
||||
defer popEnv(env)
|
||||
|
||||
os.Setenv("USERPROFILE", "profile_dir")
|
||||
expect := filepath.Join("profile_dir", ".aws", "config")
|
||||
name := sharedConfigFilename()
|
||||
assert.Equal(t, expect, name)
|
||||
|
||||
os.Setenv("HOME", "home_dir")
|
||||
expect = filepath.Join("home_dir", ".aws", "config")
|
||||
name = sharedConfigFilename()
|
||||
assert.Equal(t, expect, name)
|
||||
|
||||
expect = filepath.Join("path/to/config/file")
|
||||
os.Setenv("AWS_CONFIG_FILE", expect)
|
||||
name = sharedConfigFilename()
|
||||
assert.Equal(t, expect, name)
|
||||
}
|
||||
|
||||
func TestSetEnvValue(t *testing.T) {
|
||||
env := stashEnv()
|
||||
defer popEnv(env)
|
||||
|
||||
os.Setenv("empty_key", "")
|
||||
os.Setenv("second_key", "2")
|
||||
os.Setenv("third_key", "3")
|
||||
|
||||
var dst string
|
||||
setFromEnvVal(&dst, []string{
|
||||
"empty_key", "first_key", "second_key", "third_key",
|
||||
})
|
||||
|
||||
assert.Equal(t, "2", dst)
|
||||
}
|
||||
|
||||
func stashEnv() []string {
|
||||
env := os.Environ()
|
||||
os.Clearenv()
|
||||
|
||||
return env
|
||||
}
|
||||
|
||||
func popEnv(env []string) {
|
||||
os.Clearenv()
|
||||
|
||||
for _, e := range env {
|
||||
p := strings.SplitN(e, "=", 2)
|
||||
os.Setenv(p[0], p[1])
|
||||
}
|
||||
}
|
||||
+590
@@ -0,0 +1,590 @@
|
||||
package session
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/aws/client"
|
||||
"github.com/aws/aws-sdk-go/aws/corehandlers"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
|
||||
"github.com/aws/aws-sdk-go/aws/defaults"
|
||||
"github.com/aws/aws-sdk-go/aws/endpoints"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
)
|
||||
|
||||
// A Session provides a central location to create service clients from and
|
||||
// store configurations and request handlers for those services.
|
||||
//
|
||||
// Sessions are safe to create service clients concurrently, but it is not safe
|
||||
// to mutate the Session concurrently.
|
||||
//
|
||||
// The Session satisfies the service client's client.ClientConfigProvider.
|
||||
type Session struct {
|
||||
Config *aws.Config
|
||||
Handlers request.Handlers
|
||||
}
|
||||
|
||||
// New creates a new instance of the handlers merging in the provided configs
|
||||
// on top of the SDK's default configurations. Once the Session is created it
|
||||
// can be mutated to modify the Config or Handlers. The Session is safe to be
|
||||
// read concurrently, but it should not be written to concurrently.
|
||||
//
|
||||
// If the AWS_SDK_LOAD_CONFIG environment is set to a truthy value, the New
|
||||
// method could now encounter an error when loading the configuration. When
|
||||
// The environment variable is set, and an error occurs, New will return a
|
||||
// session that will fail all requests reporting the error that occurred while
|
||||
// loading the session. Use NewSession to get the error when creating the
|
||||
// session.
|
||||
//
|
||||
// If the AWS_SDK_LOAD_CONFIG environment variable is set to a truthy value
|
||||
// the shared config file (~/.aws/config) will also be loaded, in addition to
|
||||
// the shared credentials file (~/.aws/credentials). Values set in both the
|
||||
// shared config, and shared credentials will be taken from the shared
|
||||
// credentials file.
|
||||
//
|
||||
// Deprecated: Use NewSession functions to create sessions instead. NewSession
|
||||
// has the same functionality as New except an error can be returned when the
|
||||
// func is called instead of waiting to receive an error until a request is made.
|
||||
func New(cfgs ...*aws.Config) *Session {
|
||||
// load initial config from environment
|
||||
envCfg := loadEnvConfig()
|
||||
|
||||
if envCfg.EnableSharedConfig {
|
||||
s, err := newSession(Options{}, envCfg, cfgs...)
|
||||
if err != nil {
|
||||
// Old session.New expected all errors to be discovered when
|
||||
// a request is made, and would report the errors then. This
|
||||
// needs to be replicated if an error occurs while creating
|
||||
// the session.
|
||||
msg := "failed to create session with AWS_SDK_LOAD_CONFIG enabled. " +
|
||||
"Use session.NewSession to handle errors occurring during session creation."
|
||||
|
||||
// Session creation failed, need to report the error and prevent
|
||||
// any requests from succeeding.
|
||||
s = &Session{Config: defaults.Config()}
|
||||
s.Config.MergeIn(cfgs...)
|
||||
s.Config.Logger.Log("ERROR:", msg, "Error:", err)
|
||||
s.Handlers.Validate.PushBack(func(r *request.Request) {
|
||||
r.Error = err
|
||||
})
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
return deprecatedNewSession(cfgs...)
|
||||
}
|
||||
|
||||
// NewSession returns a new Session created from SDK defaults, config files,
|
||||
// environment, and user provided config files. Once the Session is created
|
||||
// it can be mutated to modify the Config or Handlers. The Session is safe to
|
||||
// be read concurrently, but it should not be written to concurrently.
|
||||
//
|
||||
// If the AWS_SDK_LOAD_CONFIG environment variable is set to a truthy value
|
||||
// the shared config file (~/.aws/config) will also be loaded in addition to
|
||||
// the shared credentials file (~/.aws/credentials). Values set in both the
|
||||
// shared config, and shared credentials will be taken from the shared
|
||||
// credentials file. Enabling the Shared Config will also allow the Session
|
||||
// to be built with retrieving credentials with AssumeRole set in the config.
|
||||
//
|
||||
// See the NewSessionWithOptions func for information on how to override or
|
||||
// control through code how the Session will be created. Such as specifying the
|
||||
// config profile, and controlling if shared config is enabled or not.
|
||||
func NewSession(cfgs ...*aws.Config) (*Session, error) {
|
||||
opts := Options{}
|
||||
opts.Config.MergeIn(cfgs...)
|
||||
|
||||
return NewSessionWithOptions(opts)
|
||||
}
|
||||
|
||||
// SharedConfigState provides the ability to optionally override the state
|
||||
// of the session's creation based on the shared config being enabled or
|
||||
// disabled.
|
||||
type SharedConfigState int
|
||||
|
||||
const (
|
||||
// SharedConfigStateFromEnv does not override any state of the
|
||||
// AWS_SDK_LOAD_CONFIG env var. It is the default value of the
|
||||
// SharedConfigState type.
|
||||
SharedConfigStateFromEnv SharedConfigState = iota
|
||||
|
||||
// SharedConfigDisable overrides the AWS_SDK_LOAD_CONFIG env var value
|
||||
// and disables the shared config functionality.
|
||||
SharedConfigDisable
|
||||
|
||||
// SharedConfigEnable overrides the AWS_SDK_LOAD_CONFIG env var value
|
||||
// and enables the shared config functionality.
|
||||
SharedConfigEnable
|
||||
)
|
||||
|
||||
// Options provides the means to control how a Session is created and what
|
||||
// configuration values will be loaded.
|
||||
//
|
||||
type Options struct {
|
||||
// Provides config values for the SDK to use when creating service clients
|
||||
// and making API requests to services. Any value set in with this field
|
||||
// will override the associated value provided by the SDK defaults,
|
||||
// environment or config files where relevant.
|
||||
//
|
||||
// If not set, configuration values from from SDK defaults, environment,
|
||||
// config will be used.
|
||||
Config aws.Config
|
||||
|
||||
// Overrides the config profile the Session should be created from. If not
|
||||
// set the value of the environment variable will be loaded (AWS_PROFILE,
|
||||
// or AWS_DEFAULT_PROFILE if the Shared Config is enabled).
|
||||
//
|
||||
// If not set and environment variables are not set the "default"
|
||||
// (DefaultSharedConfigProfile) will be used as the profile to load the
|
||||
// session config from.
|
||||
Profile string
|
||||
|
||||
// Instructs how the Session will be created based on the AWS_SDK_LOAD_CONFIG
|
||||
// environment variable. By default a Session will be created using the
|
||||
// value provided by the AWS_SDK_LOAD_CONFIG environment variable.
|
||||
//
|
||||
// Setting this value to SharedConfigEnable or SharedConfigDisable
|
||||
// will allow you to override the AWS_SDK_LOAD_CONFIG environment variable
|
||||
// and enable or disable the shared config functionality.
|
||||
SharedConfigState SharedConfigState
|
||||
|
||||
// When the SDK's shared config is configured to assume a role with MFA
|
||||
// this option is required in order to provide the mechanism that will
|
||||
// retrieve the MFA token. There is no default value for this field. If
|
||||
// it is not set an error will be returned when creating the session.
|
||||
//
|
||||
// This token provider will be called when ever the assumed role's
|
||||
// credentials need to be refreshed. Within the context of service clients
|
||||
// all sharing the same session the SDK will ensure calls to the token
|
||||
// provider are atomic. When sharing a token provider across multiple
|
||||
// sessions additional synchronization logic is needed to ensure the
|
||||
// token providers do not introduce race conditions. It is recommend to
|
||||
// share the session where possible.
|
||||
//
|
||||
// stscreds.StdinTokenProvider is a basic implementation that will prompt
|
||||
// from stdin for the MFA token code.
|
||||
//
|
||||
// This field is only used if the shared configuration is enabled, and
|
||||
// the config enables assume role wit MFA via the mfa_serial field.
|
||||
AssumeRoleTokenProvider func() (string, error)
|
||||
|
||||
// Reader for a custom Credentials Authority (CA) bundle in PEM format that
|
||||
// the SDK will use instead of the default system's root CA bundle. Use this
|
||||
// only if you want to replace the CA bundle the SDK uses for TLS requests.
|
||||
//
|
||||
// Enabling this option will attempt to merge the Transport into the SDK's HTTP
|
||||
// client. If the client's Transport is not a http.Transport an error will be
|
||||
// returned. If the Transport's TLS config is set this option will cause the SDK
|
||||
// to overwrite the Transport's TLS config's RootCAs value. If the CA
|
||||
// bundle reader contains multiple certificates all of them will be loaded.
|
||||
//
|
||||
// The Session option CustomCABundle is also available when creating sessions
|
||||
// to also enable this feature. CustomCABundle session option field has priority
|
||||
// over the AWS_CA_BUNDLE environment variable, and will be used if both are set.
|
||||
CustomCABundle io.Reader
|
||||
}
|
||||
|
||||
// NewSessionWithOptions returns a new Session created from SDK defaults, config files,
|
||||
// environment, and user provided config files. This func uses the Options
|
||||
// values to configure how the Session is created.
|
||||
//
|
||||
// If the AWS_SDK_LOAD_CONFIG environment variable is set to a truthy value
|
||||
// the shared config file (~/.aws/config) will also be loaded in addition to
|
||||
// the shared credentials file (~/.aws/credentials). Values set in both the
|
||||
// shared config, and shared credentials will be taken from the shared
|
||||
// credentials file. Enabling the Shared Config will also allow the Session
|
||||
// to be built with retrieving credentials with AssumeRole set in the config.
|
||||
//
|
||||
// // Equivalent to session.New
|
||||
// sess := session.Must(session.NewSessionWithOptions(session.Options{}))
|
||||
//
|
||||
// // Specify profile to load for the session's config
|
||||
// sess := session.Must(session.NewSessionWithOptions(session.Options{
|
||||
// Profile: "profile_name",
|
||||
// }))
|
||||
//
|
||||
// // Specify profile for config and region for requests
|
||||
// sess := session.Must(session.NewSessionWithOptions(session.Options{
|
||||
// Config: aws.Config{Region: aws.String("us-east-1")},
|
||||
// Profile: "profile_name",
|
||||
// }))
|
||||
//
|
||||
// // Force enable Shared Config support
|
||||
// sess := session.Must(session.NewSessionWithOptions(session.Options{
|
||||
// SharedConfigState: SharedConfigEnable,
|
||||
// }))
|
||||
func NewSessionWithOptions(opts Options) (*Session, error) {
|
||||
var envCfg envConfig
|
||||
if opts.SharedConfigState == SharedConfigEnable {
|
||||
envCfg = loadSharedEnvConfig()
|
||||
} else {
|
||||
envCfg = loadEnvConfig()
|
||||
}
|
||||
|
||||
if len(opts.Profile) > 0 {
|
||||
envCfg.Profile = opts.Profile
|
||||
}
|
||||
|
||||
switch opts.SharedConfigState {
|
||||
case SharedConfigDisable:
|
||||
envCfg.EnableSharedConfig = false
|
||||
case SharedConfigEnable:
|
||||
envCfg.EnableSharedConfig = true
|
||||
}
|
||||
|
||||
// Only use AWS_CA_BUNDLE if session option is not provided.
|
||||
if len(envCfg.CustomCABundle) != 0 && opts.CustomCABundle == nil {
|
||||
f, err := os.Open(envCfg.CustomCABundle)
|
||||
if err != nil {
|
||||
return nil, awserr.New("LoadCustomCABundleError",
|
||||
"failed to open custom CA bundle PEM file", err)
|
||||
}
|
||||
defer f.Close()
|
||||
opts.CustomCABundle = f
|
||||
}
|
||||
|
||||
return newSession(opts, envCfg, &opts.Config)
|
||||
}
|
||||
|
||||
// Must is a helper function to ensure the Session is valid and there was no
|
||||
// error when calling a NewSession function.
|
||||
//
|
||||
// This helper is intended to be used in variable initialization to load the
|
||||
// Session and configuration at startup. Such as:
|
||||
//
|
||||
// var sess = session.Must(session.NewSession())
|
||||
func Must(sess *Session, err error) *Session {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return sess
|
||||
}
|
||||
|
||||
func deprecatedNewSession(cfgs ...*aws.Config) *Session {
|
||||
cfg := defaults.Config()
|
||||
handlers := defaults.Handlers()
|
||||
|
||||
// Apply the passed in configs so the configuration can be applied to the
|
||||
// default credential chain
|
||||
cfg.MergeIn(cfgs...)
|
||||
if cfg.EndpointResolver == nil {
|
||||
// An endpoint resolver is required for a session to be able to provide
|
||||
// endpoints for service client configurations.
|
||||
cfg.EndpointResolver = endpoints.DefaultResolver()
|
||||
}
|
||||
cfg.Credentials = defaults.CredChain(cfg, handlers)
|
||||
|
||||
// Reapply any passed in configs to override credentials if set
|
||||
cfg.MergeIn(cfgs...)
|
||||
|
||||
s := &Session{
|
||||
Config: cfg,
|
||||
Handlers: handlers,
|
||||
}
|
||||
|
||||
initHandlers(s)
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func newSession(opts Options, envCfg envConfig, cfgs ...*aws.Config) (*Session, error) {
|
||||
cfg := defaults.Config()
|
||||
handlers := defaults.Handlers()
|
||||
|
||||
// Get a merged version of the user provided config to determine if
|
||||
// credentials were.
|
||||
userCfg := &aws.Config{}
|
||||
userCfg.MergeIn(cfgs...)
|
||||
|
||||
// Order config files will be loaded in with later files overwriting
|
||||
// previous config file values.
|
||||
cfgFiles := []string{envCfg.SharedConfigFile, envCfg.SharedCredentialsFile}
|
||||
if !envCfg.EnableSharedConfig {
|
||||
// The shared config file (~/.aws/config) is only loaded if instructed
|
||||
// to load via the envConfig.EnableSharedConfig (AWS_SDK_LOAD_CONFIG).
|
||||
cfgFiles = cfgFiles[1:]
|
||||
}
|
||||
|
||||
// Load additional config from file(s)
|
||||
sharedCfg, err := loadSharedConfig(envCfg.Profile, cfgFiles)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := mergeConfigSrcs(cfg, userCfg, envCfg, sharedCfg, handlers, opts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s := &Session{
|
||||
Config: cfg,
|
||||
Handlers: handlers,
|
||||
}
|
||||
|
||||
initHandlers(s)
|
||||
|
||||
// Setup HTTP client with custom cert bundle if enabled
|
||||
if opts.CustomCABundle != nil {
|
||||
if err := loadCustomCABundle(s, opts.CustomCABundle); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func loadCustomCABundle(s *Session, bundle io.Reader) error {
|
||||
var t *http.Transport
|
||||
switch v := s.Config.HTTPClient.Transport.(type) {
|
||||
case *http.Transport:
|
||||
t = v
|
||||
default:
|
||||
if s.Config.HTTPClient.Transport != nil {
|
||||
return awserr.New("LoadCustomCABundleError",
|
||||
"unable to load custom CA bundle, HTTPClient's transport unsupported type", nil)
|
||||
}
|
||||
}
|
||||
if t == nil {
|
||||
t = &http.Transport{}
|
||||
}
|
||||
|
||||
p, err := loadCertPool(bundle)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if t.TLSClientConfig == nil {
|
||||
t.TLSClientConfig = &tls.Config{}
|
||||
}
|
||||
t.TLSClientConfig.RootCAs = p
|
||||
|
||||
s.Config.HTTPClient.Transport = t
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadCertPool(r io.Reader) (*x509.CertPool, error) {
|
||||
b, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, awserr.New("LoadCustomCABundleError",
|
||||
"failed to read custom CA bundle PEM file", err)
|
||||
}
|
||||
|
||||
p := x509.NewCertPool()
|
||||
if !p.AppendCertsFromPEM(b) {
|
||||
return nil, awserr.New("LoadCustomCABundleError",
|
||||
"failed to load custom CA bundle PEM file", err)
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func mergeConfigSrcs(cfg, userCfg *aws.Config, envCfg envConfig, sharedCfg sharedConfig, handlers request.Handlers, sessOpts Options) error {
|
||||
// Merge in user provided configuration
|
||||
cfg.MergeIn(userCfg)
|
||||
|
||||
// Region if not already set by user
|
||||
if len(aws.StringValue(cfg.Region)) == 0 {
|
||||
if len(envCfg.Region) > 0 {
|
||||
cfg.WithRegion(envCfg.Region)
|
||||
} else if envCfg.EnableSharedConfig && len(sharedCfg.Region) > 0 {
|
||||
cfg.WithRegion(sharedCfg.Region)
|
||||
}
|
||||
}
|
||||
|
||||
// Configure credentials if not already set
|
||||
if cfg.Credentials == credentials.AnonymousCredentials && userCfg.Credentials == nil {
|
||||
if len(envCfg.Creds.AccessKeyID) > 0 {
|
||||
cfg.Credentials = credentials.NewStaticCredentialsFromCreds(
|
||||
envCfg.Creds,
|
||||
)
|
||||
} else if envCfg.EnableSharedConfig && len(sharedCfg.AssumeRole.RoleARN) > 0 && sharedCfg.AssumeRoleSource != nil {
|
||||
cfgCp := *cfg
|
||||
cfgCp.Credentials = credentials.NewStaticCredentialsFromCreds(
|
||||
sharedCfg.AssumeRoleSource.Creds,
|
||||
)
|
||||
if len(sharedCfg.AssumeRole.MFASerial) > 0 && sessOpts.AssumeRoleTokenProvider == nil {
|
||||
// AssumeRole Token provider is required if doing Assume Role
|
||||
// with MFA.
|
||||
return AssumeRoleTokenProviderNotSetError{}
|
||||
}
|
||||
cfg.Credentials = stscreds.NewCredentials(
|
||||
&Session{
|
||||
Config: &cfgCp,
|
||||
Handlers: handlers.Copy(),
|
||||
},
|
||||
sharedCfg.AssumeRole.RoleARN,
|
||||
func(opt *stscreds.AssumeRoleProvider) {
|
||||
opt.RoleSessionName = sharedCfg.AssumeRole.RoleSessionName
|
||||
|
||||
// Assume role with external ID
|
||||
if len(sharedCfg.AssumeRole.ExternalID) > 0 {
|
||||
opt.ExternalID = aws.String(sharedCfg.AssumeRole.ExternalID)
|
||||
}
|
||||
|
||||
// Assume role with MFA
|
||||
if len(sharedCfg.AssumeRole.MFASerial) > 0 {
|
||||
opt.SerialNumber = aws.String(sharedCfg.AssumeRole.MFASerial)
|
||||
opt.TokenProvider = sessOpts.AssumeRoleTokenProvider
|
||||
}
|
||||
},
|
||||
)
|
||||
} else if len(sharedCfg.Creds.AccessKeyID) > 0 {
|
||||
cfg.Credentials = credentials.NewStaticCredentialsFromCreds(
|
||||
sharedCfg.Creds,
|
||||
)
|
||||
} else {
|
||||
// Fallback to default credentials provider, include mock errors
|
||||
// for the credential chain so user can identify why credentials
|
||||
// failed to be retrieved.
|
||||
cfg.Credentials = credentials.NewCredentials(&credentials.ChainProvider{
|
||||
VerboseErrors: aws.BoolValue(cfg.CredentialsChainVerboseErrors),
|
||||
Providers: []credentials.Provider{
|
||||
&credProviderError{Err: awserr.New("EnvAccessKeyNotFound", "failed to find credentials in the environment.", nil)},
|
||||
&credProviderError{Err: awserr.New("SharedCredsLoad", fmt.Sprintf("failed to load profile, %s.", envCfg.Profile), nil)},
|
||||
defaults.RemoteCredProvider(*cfg, handlers),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AssumeRoleTokenProviderNotSetError is an error returned when creating a session when the
|
||||
// MFAToken option is not set when shared config is configured load assume a
|
||||
// role with an MFA token.
|
||||
type AssumeRoleTokenProviderNotSetError struct{}
|
||||
|
||||
// Code is the short id of the error.
|
||||
func (e AssumeRoleTokenProviderNotSetError) Code() string {
|
||||
return "AssumeRoleTokenProviderNotSetError"
|
||||
}
|
||||
|
||||
// Message is the description of the error
|
||||
func (e AssumeRoleTokenProviderNotSetError) Message() string {
|
||||
return fmt.Sprintf("assume role with MFA enabled, but AssumeRoleTokenProvider session option not set.")
|
||||
}
|
||||
|
||||
// OrigErr is the underlying error that caused the failure.
|
||||
func (e AssumeRoleTokenProviderNotSetError) OrigErr() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Error satisfies the error interface.
|
||||
func (e AssumeRoleTokenProviderNotSetError) Error() string {
|
||||
return awserr.SprintError(e.Code(), e.Message(), "", nil)
|
||||
}
|
||||
|
||||
type credProviderError struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
var emptyCreds = credentials.Value{}
|
||||
|
||||
func (c credProviderError) Retrieve() (credentials.Value, error) {
|
||||
return credentials.Value{}, c.Err
|
||||
}
|
||||
func (c credProviderError) IsExpired() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func initHandlers(s *Session) {
|
||||
// Add the Validate parameter handler if it is not disabled.
|
||||
s.Handlers.Validate.Remove(corehandlers.ValidateParametersHandler)
|
||||
if !aws.BoolValue(s.Config.DisableParamValidation) {
|
||||
s.Handlers.Validate.PushBackNamed(corehandlers.ValidateParametersHandler)
|
||||
}
|
||||
}
|
||||
|
||||
// Copy creates and returns a copy of the current Session, coping the config
|
||||
// and handlers. If any additional configs are provided they will be merged
|
||||
// on top of the Session's copied config.
|
||||
//
|
||||
// // Create a copy of the current Session, configured for the us-west-2 region.
|
||||
// sess.Copy(&aws.Config{Region: aws.String("us-west-2")})
|
||||
func (s *Session) Copy(cfgs ...*aws.Config) *Session {
|
||||
newSession := &Session{
|
||||
Config: s.Config.Copy(cfgs...),
|
||||
Handlers: s.Handlers.Copy(),
|
||||
}
|
||||
|
||||
initHandlers(newSession)
|
||||
|
||||
return newSession
|
||||
}
|
||||
|
||||
// ClientConfig satisfies the client.ConfigProvider interface and is used to
|
||||
// configure the service client instances. Passing the Session to the service
|
||||
// client's constructor (New) will use this method to configure the client.
|
||||
func (s *Session) ClientConfig(serviceName string, cfgs ...*aws.Config) client.Config {
|
||||
// Backwards compatibility, the error will be eaten if user calls ClientConfig
|
||||
// directly. All SDK services will use ClientconfigWithError.
|
||||
cfg, _ := s.clientConfigWithErr(serviceName, cfgs...)
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
func (s *Session) clientConfigWithErr(serviceName string, cfgs ...*aws.Config) (client.Config, error) {
|
||||
s = s.Copy(cfgs...)
|
||||
|
||||
var resolved endpoints.ResolvedEndpoint
|
||||
var err error
|
||||
|
||||
region := aws.StringValue(s.Config.Region)
|
||||
|
||||
if endpoint := aws.StringValue(s.Config.Endpoint); len(endpoint) != 0 {
|
||||
resolved.URL = endpoints.AddScheme(endpoint, aws.BoolValue(s.Config.DisableSSL))
|
||||
resolved.SigningRegion = region
|
||||
} else {
|
||||
resolved, err = s.Config.EndpointResolver.EndpointFor(
|
||||
serviceName, region,
|
||||
func(opt *endpoints.Options) {
|
||||
opt.DisableSSL = aws.BoolValue(s.Config.DisableSSL)
|
||||
opt.UseDualStack = aws.BoolValue(s.Config.UseDualStack)
|
||||
|
||||
// Support the condition where the service is modeled but its
|
||||
// endpoint metadata is not available.
|
||||
opt.ResolveUnknownService = true
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
return client.Config{
|
||||
Config: s.Config,
|
||||
Handlers: s.Handlers,
|
||||
Endpoint: resolved.URL,
|
||||
SigningRegion: resolved.SigningRegion,
|
||||
SigningName: resolved.SigningName,
|
||||
}, err
|
||||
}
|
||||
|
||||
// ClientConfigNoResolveEndpoint is the same as ClientConfig with the exception
|
||||
// that the EndpointResolver will not be used to resolve the endpoint. The only
|
||||
// endpoint set must come from the aws.Config.Endpoint field.
|
||||
func (s *Session) ClientConfigNoResolveEndpoint(cfgs ...*aws.Config) client.Config {
|
||||
s = s.Copy(cfgs...)
|
||||
|
||||
var resolved endpoints.ResolvedEndpoint
|
||||
|
||||
region := aws.StringValue(s.Config.Region)
|
||||
|
||||
if ep := aws.StringValue(s.Config.Endpoint); len(ep) > 0 {
|
||||
resolved.URL = endpoints.AddScheme(ep, aws.BoolValue(s.Config.DisableSSL))
|
||||
resolved.SigningRegion = region
|
||||
}
|
||||
|
||||
return client.Config{
|
||||
Config: s.Config,
|
||||
Handlers: s.Handlers,
|
||||
Endpoint: resolved.URL,
|
||||
SigningRegion: resolved.SigningRegion,
|
||||
SigningName: resolved.SigningName,
|
||||
}
|
||||
}
|
||||
+404
@@ -0,0 +1,404 @@
|
||||
package session
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/aws/aws-sdk-go/aws/defaults"
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
)
|
||||
|
||||
func TestNewDefaultSession(t *testing.T) {
|
||||
oldEnv := initSessionTestEnv()
|
||||
defer popEnv(oldEnv)
|
||||
|
||||
s := New(&aws.Config{Region: aws.String("region")})
|
||||
|
||||
assert.Equal(t, "region", *s.Config.Region)
|
||||
assert.Equal(t, http.DefaultClient, s.Config.HTTPClient)
|
||||
assert.NotNil(t, s.Config.Logger)
|
||||
assert.Equal(t, aws.LogOff, *s.Config.LogLevel)
|
||||
}
|
||||
|
||||
func TestNew_WithCustomCreds(t *testing.T) {
|
||||
oldEnv := initSessionTestEnv()
|
||||
defer popEnv(oldEnv)
|
||||
|
||||
customCreds := credentials.NewStaticCredentials("AKID", "SECRET", "TOKEN")
|
||||
s := New(&aws.Config{Credentials: customCreds})
|
||||
|
||||
assert.Equal(t, customCreds, s.Config.Credentials)
|
||||
}
|
||||
|
||||
type mockLogger struct {
|
||||
*bytes.Buffer
|
||||
}
|
||||
|
||||
func (w mockLogger) Log(args ...interface{}) {
|
||||
fmt.Fprintln(w, args...)
|
||||
}
|
||||
|
||||
func TestNew_WithSessionLoadError(t *testing.T) {
|
||||
oldEnv := initSessionTestEnv()
|
||||
defer popEnv(oldEnv)
|
||||
|
||||
os.Setenv("AWS_SDK_LOAD_CONFIG", "1")
|
||||
os.Setenv("AWS_CONFIG_FILE", testConfigFilename)
|
||||
os.Setenv("AWS_PROFILE", "assume_role_invalid_source_profile")
|
||||
|
||||
logger := bytes.Buffer{}
|
||||
s := New(&aws.Config{Logger: &mockLogger{&logger}})
|
||||
|
||||
assert.NotNil(t, s)
|
||||
|
||||
svc := s3.New(s)
|
||||
_, err := svc.ListBuckets(&s3.ListBucketsInput{})
|
||||
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, logger.String(), "ERROR: failed to create session with AWS_SDK_LOAD_CONFIG enabled")
|
||||
assert.Contains(t, err.Error(), SharedConfigAssumeRoleError{
|
||||
RoleARN: "assume_role_invalid_source_profile_role_arn",
|
||||
}.Error())
|
||||
}
|
||||
|
||||
func TestSessionCopy(t *testing.T) {
|
||||
oldEnv := initSessionTestEnv()
|
||||
defer popEnv(oldEnv)
|
||||
|
||||
os.Setenv("AWS_REGION", "orig_region")
|
||||
|
||||
s := Session{
|
||||
Config: defaults.Config(),
|
||||
Handlers: defaults.Handlers(),
|
||||
}
|
||||
|
||||
newSess := s.Copy(&aws.Config{Region: aws.String("new_region")})
|
||||
|
||||
assert.Equal(t, "orig_region", *s.Config.Region)
|
||||
assert.Equal(t, "new_region", *newSess.Config.Region)
|
||||
}
|
||||
|
||||
func TestSessionClientConfig(t *testing.T) {
|
||||
s, err := NewSession(&aws.Config{Region: aws.String("orig_region")})
|
||||
assert.NoError(t, err)
|
||||
|
||||
cfg := s.ClientConfig("s3", &aws.Config{Region: aws.String("us-west-2")})
|
||||
|
||||
assert.Equal(t, "https://s3-us-west-2.amazonaws.com", cfg.Endpoint)
|
||||
assert.Equal(t, "us-west-2", cfg.SigningRegion)
|
||||
assert.Equal(t, "us-west-2", *cfg.Config.Region)
|
||||
}
|
||||
|
||||
func TestNewSession_NoCredentials(t *testing.T) {
|
||||
oldEnv := initSessionTestEnv()
|
||||
defer popEnv(oldEnv)
|
||||
|
||||
s, err := NewSession()
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.NotNil(t, s.Config.Credentials)
|
||||
assert.NotEqual(t, credentials.AnonymousCredentials, s.Config.Credentials)
|
||||
}
|
||||
|
||||
func TestNewSessionWithOptions_OverrideProfile(t *testing.T) {
|
||||
oldEnv := initSessionTestEnv()
|
||||
defer popEnv(oldEnv)
|
||||
|
||||
os.Setenv("AWS_SDK_LOAD_CONFIG", "1")
|
||||
os.Setenv("AWS_SHARED_CREDENTIALS_FILE", testConfigFilename)
|
||||
os.Setenv("AWS_PROFILE", "other_profile")
|
||||
|
||||
s, err := NewSessionWithOptions(Options{
|
||||
Profile: "full_profile",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "full_profile_region", *s.Config.Region)
|
||||
|
||||
creds, err := s.Config.Credentials.Get()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "full_profile_akid", creds.AccessKeyID)
|
||||
assert.Equal(t, "full_profile_secret", creds.SecretAccessKey)
|
||||
assert.Empty(t, creds.SessionToken)
|
||||
assert.Contains(t, creds.ProviderName, "SharedConfigCredentials")
|
||||
}
|
||||
|
||||
func TestNewSessionWithOptions_OverrideSharedConfigEnable(t *testing.T) {
|
||||
oldEnv := initSessionTestEnv()
|
||||
defer popEnv(oldEnv)
|
||||
|
||||
os.Setenv("AWS_SDK_LOAD_CONFIG", "0")
|
||||
os.Setenv("AWS_SHARED_CREDENTIALS_FILE", testConfigFilename)
|
||||
os.Setenv("AWS_PROFILE", "full_profile")
|
||||
|
||||
s, err := NewSessionWithOptions(Options{
|
||||
SharedConfigState: SharedConfigEnable,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "full_profile_region", *s.Config.Region)
|
||||
|
||||
creds, err := s.Config.Credentials.Get()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "full_profile_akid", creds.AccessKeyID)
|
||||
assert.Equal(t, "full_profile_secret", creds.SecretAccessKey)
|
||||
assert.Empty(t, creds.SessionToken)
|
||||
assert.Contains(t, creds.ProviderName, "SharedConfigCredentials")
|
||||
}
|
||||
|
||||
func TestNewSessionWithOptions_OverrideSharedConfigDisable(t *testing.T) {
|
||||
oldEnv := initSessionTestEnv()
|
||||
defer popEnv(oldEnv)
|
||||
|
||||
os.Setenv("AWS_SDK_LOAD_CONFIG", "1")
|
||||
os.Setenv("AWS_SHARED_CREDENTIALS_FILE", testConfigFilename)
|
||||
os.Setenv("AWS_PROFILE", "full_profile")
|
||||
|
||||
s, err := NewSessionWithOptions(Options{
|
||||
SharedConfigState: SharedConfigDisable,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Empty(t, *s.Config.Region)
|
||||
|
||||
creds, err := s.Config.Credentials.Get()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "full_profile_akid", creds.AccessKeyID)
|
||||
assert.Equal(t, "full_profile_secret", creds.SecretAccessKey)
|
||||
assert.Empty(t, creds.SessionToken)
|
||||
assert.Contains(t, creds.ProviderName, "SharedConfigCredentials")
|
||||
}
|
||||
|
||||
func TestNewSessionWithOptions_Overrides(t *testing.T) {
|
||||
cases := []struct {
|
||||
InEnvs map[string]string
|
||||
InProfile string
|
||||
OutRegion string
|
||||
OutCreds credentials.Value
|
||||
}{
|
||||
{
|
||||
InEnvs: map[string]string{
|
||||
"AWS_SDK_LOAD_CONFIG": "0",
|
||||
"AWS_SHARED_CREDENTIALS_FILE": testConfigFilename,
|
||||
"AWS_PROFILE": "other_profile",
|
||||
},
|
||||
InProfile: "full_profile",
|
||||
OutRegion: "full_profile_region",
|
||||
OutCreds: credentials.Value{
|
||||
AccessKeyID: "full_profile_akid",
|
||||
SecretAccessKey: "full_profile_secret",
|
||||
ProviderName: "SharedConfigCredentials",
|
||||
},
|
||||
},
|
||||
{
|
||||
InEnvs: map[string]string{
|
||||
"AWS_SDK_LOAD_CONFIG": "0",
|
||||
"AWS_SHARED_CREDENTIALS_FILE": testConfigFilename,
|
||||
"AWS_REGION": "env_region",
|
||||
"AWS_ACCESS_KEY": "env_akid",
|
||||
"AWS_SECRET_ACCESS_KEY": "env_secret",
|
||||
"AWS_PROFILE": "other_profile",
|
||||
},
|
||||
InProfile: "full_profile",
|
||||
OutRegion: "env_region",
|
||||
OutCreds: credentials.Value{
|
||||
AccessKeyID: "env_akid",
|
||||
SecretAccessKey: "env_secret",
|
||||
ProviderName: "EnvConfigCredentials",
|
||||
},
|
||||
},
|
||||
{
|
||||
InEnvs: map[string]string{
|
||||
"AWS_SDK_LOAD_CONFIG": "0",
|
||||
"AWS_SHARED_CREDENTIALS_FILE": testConfigFilename,
|
||||
"AWS_CONFIG_FILE": testConfigOtherFilename,
|
||||
"AWS_PROFILE": "shared_profile",
|
||||
},
|
||||
InProfile: "config_file_load_order",
|
||||
OutRegion: "shared_config_region",
|
||||
OutCreds: credentials.Value{
|
||||
AccessKeyID: "shared_config_akid",
|
||||
SecretAccessKey: "shared_config_secret",
|
||||
ProviderName: "SharedConfigCredentials",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
oldEnv := initSessionTestEnv()
|
||||
defer popEnv(oldEnv)
|
||||
|
||||
for k, v := range c.InEnvs {
|
||||
os.Setenv(k, v)
|
||||
}
|
||||
|
||||
s, err := NewSessionWithOptions(Options{
|
||||
Profile: c.InProfile,
|
||||
SharedConfigState: SharedConfigEnable,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
creds, err := s.Config.Credentials.Get()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, c.OutRegion, *s.Config.Region)
|
||||
assert.Equal(t, c.OutCreds.AccessKeyID, creds.AccessKeyID)
|
||||
assert.Equal(t, c.OutCreds.SecretAccessKey, creds.SecretAccessKey)
|
||||
assert.Equal(t, c.OutCreds.SessionToken, creds.SessionToken)
|
||||
assert.Contains(t, creds.ProviderName, c.OutCreds.ProviderName)
|
||||
}
|
||||
}
|
||||
|
||||
const assumeRoleRespMsg = `
|
||||
<AssumeRoleResponse xmlns="https://sts.amazonaws.com/doc/2011-06-15/">
|
||||
<AssumeRoleResult>
|
||||
<AssumedRoleUser>
|
||||
<Arn>arn:aws:sts::account_id:assumed-role/role/session_name</Arn>
|
||||
<AssumedRoleId>AKID:session_name</AssumedRoleId>
|
||||
</AssumedRoleUser>
|
||||
<Credentials>
|
||||
<AccessKeyId>AKID</AccessKeyId>
|
||||
<SecretAccessKey>SECRET</SecretAccessKey>
|
||||
<SessionToken>SESSION_TOKEN</SessionToken>
|
||||
<Expiration>%s</Expiration>
|
||||
</Credentials>
|
||||
</AssumeRoleResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>request-id</RequestId>
|
||||
</ResponseMetadata>
|
||||
</AssumeRoleResponse>
|
||||
`
|
||||
|
||||
func TestSesisonAssumeRole(t *testing.T) {
|
||||
oldEnv := initSessionTestEnv()
|
||||
defer popEnv(oldEnv)
|
||||
|
||||
os.Setenv("AWS_REGION", "us-east-1")
|
||||
os.Setenv("AWS_SDK_LOAD_CONFIG", "1")
|
||||
os.Setenv("AWS_SHARED_CREDENTIALS_FILE", testConfigFilename)
|
||||
os.Setenv("AWS_PROFILE", "assume_role_w_creds")
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte(fmt.Sprintf(assumeRoleRespMsg, time.Now().Add(15*time.Minute).Format("2006-01-02T15:04:05Z"))))
|
||||
}))
|
||||
|
||||
s, err := NewSession(&aws.Config{Endpoint: aws.String(server.URL), DisableSSL: aws.Bool(true)})
|
||||
|
||||
creds, err := s.Config.Credentials.Get()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "AKID", creds.AccessKeyID)
|
||||
assert.Equal(t, "SECRET", creds.SecretAccessKey)
|
||||
assert.Equal(t, "SESSION_TOKEN", creds.SessionToken)
|
||||
assert.Contains(t, creds.ProviderName, "AssumeRoleProvider")
|
||||
}
|
||||
|
||||
func TestSessionAssumeRole_WithMFA(t *testing.T) {
|
||||
oldEnv := initSessionTestEnv()
|
||||
defer popEnv(oldEnv)
|
||||
|
||||
os.Setenv("AWS_REGION", "us-east-1")
|
||||
os.Setenv("AWS_SDK_LOAD_CONFIG", "1")
|
||||
os.Setenv("AWS_SHARED_CREDENTIALS_FILE", testConfigFilename)
|
||||
os.Setenv("AWS_PROFILE", "assume_role_w_creds")
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, r.FormValue("SerialNumber"), "0123456789")
|
||||
assert.Equal(t, r.FormValue("TokenCode"), "tokencode")
|
||||
|
||||
w.Write([]byte(fmt.Sprintf(assumeRoleRespMsg, time.Now().Add(15*time.Minute).Format("2006-01-02T15:04:05Z"))))
|
||||
}))
|
||||
|
||||
customProviderCalled := false
|
||||
sess, err := NewSessionWithOptions(Options{
|
||||
Profile: "assume_role_w_mfa",
|
||||
Config: aws.Config{
|
||||
Region: aws.String("us-east-1"),
|
||||
Endpoint: aws.String(server.URL),
|
||||
DisableSSL: aws.Bool(true),
|
||||
},
|
||||
SharedConfigState: SharedConfigEnable,
|
||||
AssumeRoleTokenProvider: func() (string, error) {
|
||||
customProviderCalled = true
|
||||
|
||||
return "tokencode", nil
|
||||
},
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
creds, err := sess.Config.Credentials.Get()
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, customProviderCalled)
|
||||
|
||||
assert.Equal(t, "AKID", creds.AccessKeyID)
|
||||
assert.Equal(t, "SECRET", creds.SecretAccessKey)
|
||||
assert.Equal(t, "SESSION_TOKEN", creds.SessionToken)
|
||||
assert.Contains(t, creds.ProviderName, "AssumeRoleProvider")
|
||||
}
|
||||
|
||||
func TestSessionAssumeRole_WithMFA_NoTokenProvider(t *testing.T) {
|
||||
oldEnv := initSessionTestEnv()
|
||||
defer popEnv(oldEnv)
|
||||
|
||||
os.Setenv("AWS_REGION", "us-east-1")
|
||||
os.Setenv("AWS_SDK_LOAD_CONFIG", "1")
|
||||
os.Setenv("AWS_SHARED_CREDENTIALS_FILE", testConfigFilename)
|
||||
os.Setenv("AWS_PROFILE", "assume_role_w_creds")
|
||||
|
||||
_, err := NewSessionWithOptions(Options{
|
||||
Profile: "assume_role_w_mfa",
|
||||
SharedConfigState: SharedConfigEnable,
|
||||
})
|
||||
assert.Equal(t, err, AssumeRoleTokenProviderNotSetError{})
|
||||
}
|
||||
|
||||
func TestSessionAssumeRole_DisableSharedConfig(t *testing.T) {
|
||||
// Backwards compatibility with Shared config disabled
|
||||
// assume role should not be built into the config.
|
||||
oldEnv := initSessionTestEnv()
|
||||
defer popEnv(oldEnv)
|
||||
|
||||
os.Setenv("AWS_SDK_LOAD_CONFIG", "0")
|
||||
os.Setenv("AWS_SHARED_CREDENTIALS_FILE", testConfigFilename)
|
||||
os.Setenv("AWS_PROFILE", "assume_role_w_creds")
|
||||
|
||||
s, err := NewSession()
|
||||
assert.NoError(t, err)
|
||||
|
||||
creds, err := s.Config.Credentials.Get()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "assume_role_w_creds_akid", creds.AccessKeyID)
|
||||
assert.Equal(t, "assume_role_w_creds_secret", creds.SecretAccessKey)
|
||||
assert.Contains(t, creds.ProviderName, "SharedConfigCredentials")
|
||||
}
|
||||
|
||||
func TestSessionAssumeRole_InvalidSourceProfile(t *testing.T) {
|
||||
// Backwards compatibility with Shared config disabled
|
||||
// assume role should not be built into the config.
|
||||
oldEnv := initSessionTestEnv()
|
||||
defer popEnv(oldEnv)
|
||||
|
||||
os.Setenv("AWS_SDK_LOAD_CONFIG", "1")
|
||||
os.Setenv("AWS_SHARED_CREDENTIALS_FILE", testConfigFilename)
|
||||
os.Setenv("AWS_PROFILE", "assume_role_invalid_source_profile")
|
||||
|
||||
s, err := NewSession()
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "SharedConfigAssumeRoleError: failed to load assume role")
|
||||
assert.Nil(t, s)
|
||||
}
|
||||
|
||||
func initSessionTestEnv() (oldEnv []string) {
|
||||
oldEnv = stashEnv()
|
||||
os.Setenv("AWS_CONFIG_FILE", "file_not_exists")
|
||||
os.Setenv("AWS_SHARED_CREDENTIALS_FILE", "file_not_exists")
|
||||
|
||||
return oldEnv
|
||||
}
|
||||
+295
@@ -0,0 +1,295 @@
|
||||
package session
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/go-ini/ini"
|
||||
)
|
||||
|
||||
const (
|
||||
// Static Credentials group
|
||||
accessKeyIDKey = `aws_access_key_id` // group required
|
||||
secretAccessKey = `aws_secret_access_key` // group required
|
||||
sessionTokenKey = `aws_session_token` // optional
|
||||
|
||||
// Assume Role Credentials group
|
||||
roleArnKey = `role_arn` // group required
|
||||
sourceProfileKey = `source_profile` // group required
|
||||
externalIDKey = `external_id` // optional
|
||||
mfaSerialKey = `mfa_serial` // optional
|
||||
roleSessionNameKey = `role_session_name` // optional
|
||||
|
||||
// Additional Config fields
|
||||
regionKey = `region`
|
||||
|
||||
// DefaultSharedConfigProfile is the default profile to be used when
|
||||
// loading configuration from the config files if another profile name
|
||||
// is not provided.
|
||||
DefaultSharedConfigProfile = `default`
|
||||
)
|
||||
|
||||
type assumeRoleConfig struct {
|
||||
RoleARN string
|
||||
SourceProfile string
|
||||
ExternalID string
|
||||
MFASerial string
|
||||
RoleSessionName string
|
||||
}
|
||||
|
||||
// sharedConfig represents the configuration fields of the SDK config files.
|
||||
type sharedConfig struct {
|
||||
// Credentials values from the config file. Both aws_access_key_id
|
||||
// and aws_secret_access_key must be provided together in the same file
|
||||
// to be considered valid. The values will be ignored if not a complete group.
|
||||
// aws_session_token is an optional field that can be provided if both of the
|
||||
// other two fields are also provided.
|
||||
//
|
||||
// aws_access_key_id
|
||||
// aws_secret_access_key
|
||||
// aws_session_token
|
||||
Creds credentials.Value
|
||||
|
||||
AssumeRole assumeRoleConfig
|
||||
AssumeRoleSource *sharedConfig
|
||||
|
||||
// Region is the region the SDK should use for looking up AWS service endpoints
|
||||
// and signing requests.
|
||||
//
|
||||
// region
|
||||
Region string
|
||||
}
|
||||
|
||||
type sharedConfigFile struct {
|
||||
Filename string
|
||||
IniData *ini.File
|
||||
}
|
||||
|
||||
// loadSharedConfig retrieves the configuration from the list of files
|
||||
// using the profile provided. The order the files are listed will determine
|
||||
// precedence. Values in subsequent files will overwrite values defined in
|
||||
// earlier files.
|
||||
//
|
||||
// For example, given two files A and B. Both define credentials. If the order
|
||||
// of the files are A then B, B's credential values will be used instead of A's.
|
||||
//
|
||||
// See sharedConfig.setFromFile for information how the config files
|
||||
// will be loaded.
|
||||
func loadSharedConfig(profile string, filenames []string) (sharedConfig, error) {
|
||||
if len(profile) == 0 {
|
||||
profile = DefaultSharedConfigProfile
|
||||
}
|
||||
|
||||
files, err := loadSharedConfigIniFiles(filenames)
|
||||
if err != nil {
|
||||
return sharedConfig{}, err
|
||||
}
|
||||
|
||||
cfg := sharedConfig{}
|
||||
if err = cfg.setFromIniFiles(profile, files); err != nil {
|
||||
return sharedConfig{}, err
|
||||
}
|
||||
|
||||
if len(cfg.AssumeRole.SourceProfile) > 0 {
|
||||
if err := cfg.setAssumeRoleSource(profile, files); err != nil {
|
||||
return sharedConfig{}, err
|
||||
}
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func loadSharedConfigIniFiles(filenames []string) ([]sharedConfigFile, error) {
|
||||
files := make([]sharedConfigFile, 0, len(filenames))
|
||||
|
||||
for _, filename := range filenames {
|
||||
b, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
// Skip files which can't be opened and read for whatever reason
|
||||
continue
|
||||
}
|
||||
|
||||
f, err := ini.Load(b)
|
||||
if err != nil {
|
||||
return nil, SharedConfigLoadError{Filename: filename}
|
||||
}
|
||||
|
||||
files = append(files, sharedConfigFile{
|
||||
Filename: filename, IniData: f,
|
||||
})
|
||||
}
|
||||
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func (cfg *sharedConfig) setAssumeRoleSource(origProfile string, files []sharedConfigFile) error {
|
||||
var assumeRoleSrc sharedConfig
|
||||
|
||||
// Multiple level assume role chains are not support
|
||||
if cfg.AssumeRole.SourceProfile == origProfile {
|
||||
assumeRoleSrc = *cfg
|
||||
assumeRoleSrc.AssumeRole = assumeRoleConfig{}
|
||||
} else {
|
||||
err := assumeRoleSrc.setFromIniFiles(cfg.AssumeRole.SourceProfile, files)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(assumeRoleSrc.Creds.AccessKeyID) == 0 {
|
||||
return SharedConfigAssumeRoleError{RoleARN: cfg.AssumeRole.RoleARN}
|
||||
}
|
||||
|
||||
cfg.AssumeRoleSource = &assumeRoleSrc
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cfg *sharedConfig) setFromIniFiles(profile string, files []sharedConfigFile) error {
|
||||
// Trim files from the list that don't exist.
|
||||
for _, f := range files {
|
||||
if err := cfg.setFromIniFile(profile, f); err != nil {
|
||||
if _, ok := err.(SharedConfigProfileNotExistsError); ok {
|
||||
// Ignore proviles missings
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// setFromFile loads the configuration from the file using
|
||||
// the profile provided. A sharedConfig pointer type value is used so that
|
||||
// multiple config file loadings can be chained.
|
||||
//
|
||||
// Only loads complete logically grouped values, and will not set fields in cfg
|
||||
// for incomplete grouped values in the config. Such as credentials. For example
|
||||
// if a config file only includes aws_access_key_id but no aws_secret_access_key
|
||||
// the aws_access_key_id will be ignored.
|
||||
func (cfg *sharedConfig) setFromIniFile(profile string, file sharedConfigFile) error {
|
||||
section, err := file.IniData.GetSection(profile)
|
||||
if err != nil {
|
||||
// Fallback to to alternate profile name: profile <name>
|
||||
section, err = file.IniData.GetSection(fmt.Sprintf("profile %s", profile))
|
||||
if err != nil {
|
||||
return SharedConfigProfileNotExistsError{Profile: profile, Err: err}
|
||||
}
|
||||
}
|
||||
|
||||
// Shared Credentials
|
||||
akid := section.Key(accessKeyIDKey).String()
|
||||
secret := section.Key(secretAccessKey).String()
|
||||
if len(akid) > 0 && len(secret) > 0 {
|
||||
cfg.Creds = credentials.Value{
|
||||
AccessKeyID: akid,
|
||||
SecretAccessKey: secret,
|
||||
SessionToken: section.Key(sessionTokenKey).String(),
|
||||
ProviderName: fmt.Sprintf("SharedConfigCredentials: %s", file.Filename),
|
||||
}
|
||||
}
|
||||
|
||||
// Assume Role
|
||||
roleArn := section.Key(roleArnKey).String()
|
||||
srcProfile := section.Key(sourceProfileKey).String()
|
||||
if len(roleArn) > 0 && len(srcProfile) > 0 {
|
||||
cfg.AssumeRole = assumeRoleConfig{
|
||||
RoleARN: roleArn,
|
||||
SourceProfile: srcProfile,
|
||||
ExternalID: section.Key(externalIDKey).String(),
|
||||
MFASerial: section.Key(mfaSerialKey).String(),
|
||||
RoleSessionName: section.Key(roleSessionNameKey).String(),
|
||||
}
|
||||
}
|
||||
|
||||
// Region
|
||||
if v := section.Key(regionKey).String(); len(v) > 0 {
|
||||
cfg.Region = v
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SharedConfigLoadError is an error for the shared config file failed to load.
|
||||
type SharedConfigLoadError struct {
|
||||
Filename string
|
||||
Err error
|
||||
}
|
||||
|
||||
// Code is the short id of the error.
|
||||
func (e SharedConfigLoadError) Code() string {
|
||||
return "SharedConfigLoadError"
|
||||
}
|
||||
|
||||
// Message is the description of the error
|
||||
func (e SharedConfigLoadError) Message() string {
|
||||
return fmt.Sprintf("failed to load config file, %s", e.Filename)
|
||||
}
|
||||
|
||||
// OrigErr is the underlying error that caused the failure.
|
||||
func (e SharedConfigLoadError) OrigErr() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
// Error satisfies the error interface.
|
||||
func (e SharedConfigLoadError) Error() string {
|
||||
return awserr.SprintError(e.Code(), e.Message(), "", e.Err)
|
||||
}
|
||||
|
||||
// SharedConfigProfileNotExistsError is an error for the shared config when
|
||||
// the profile was not find in the config file.
|
||||
type SharedConfigProfileNotExistsError struct {
|
||||
Profile string
|
||||
Err error
|
||||
}
|
||||
|
||||
// Code is the short id of the error.
|
||||
func (e SharedConfigProfileNotExistsError) Code() string {
|
||||
return "SharedConfigProfileNotExistsError"
|
||||
}
|
||||
|
||||
// Message is the description of the error
|
||||
func (e SharedConfigProfileNotExistsError) Message() string {
|
||||
return fmt.Sprintf("failed to get profile, %s", e.Profile)
|
||||
}
|
||||
|
||||
// OrigErr is the underlying error that caused the failure.
|
||||
func (e SharedConfigProfileNotExistsError) OrigErr() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
// Error satisfies the error interface.
|
||||
func (e SharedConfigProfileNotExistsError) Error() string {
|
||||
return awserr.SprintError(e.Code(), e.Message(), "", e.Err)
|
||||
}
|
||||
|
||||
// SharedConfigAssumeRoleError is an error for the shared config when the
|
||||
// profile contains assume role information, but that information is invalid
|
||||
// or not complete.
|
||||
type SharedConfigAssumeRoleError struct {
|
||||
RoleARN string
|
||||
}
|
||||
|
||||
// Code is the short id of the error.
|
||||
func (e SharedConfigAssumeRoleError) Code() string {
|
||||
return "SharedConfigAssumeRoleError"
|
||||
}
|
||||
|
||||
// Message is the description of the error
|
||||
func (e SharedConfigAssumeRoleError) Message() string {
|
||||
return fmt.Sprintf("failed to load assume role for %s, source profile has no shared credentials",
|
||||
e.RoleARN)
|
||||
}
|
||||
|
||||
// OrigErr is the underlying error that caused the failure.
|
||||
func (e SharedConfigAssumeRoleError) OrigErr() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Error satisfies the error interface.
|
||||
func (e SharedConfigAssumeRoleError) Error() string {
|
||||
return awserr.SprintError(e.Code(), e.Message(), "", nil)
|
||||
}
|
||||
+274
@@ -0,0 +1,274 @@
|
||||
package session
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/go-ini/ini"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var (
|
||||
testConfigFilename = filepath.Join("testdata", "shared_config")
|
||||
testConfigOtherFilename = filepath.Join("testdata", "shared_config_other")
|
||||
)
|
||||
|
||||
func TestLoadSharedConfig(t *testing.T) {
|
||||
cases := []struct {
|
||||
Filenames []string
|
||||
Profile string
|
||||
Expected sharedConfig
|
||||
Err error
|
||||
}{
|
||||
{
|
||||
Filenames: []string{"file_not_exists"},
|
||||
Profile: "default",
|
||||
},
|
||||
{
|
||||
Filenames: []string{testConfigFilename},
|
||||
Expected: sharedConfig{
|
||||
Region: "default_region",
|
||||
},
|
||||
},
|
||||
{
|
||||
Filenames: []string{testConfigOtherFilename, testConfigFilename},
|
||||
Profile: "config_file_load_order",
|
||||
Expected: sharedConfig{
|
||||
Region: "shared_config_region",
|
||||
Creds: credentials.Value{
|
||||
AccessKeyID: "shared_config_akid",
|
||||
SecretAccessKey: "shared_config_secret",
|
||||
ProviderName: fmt.Sprintf("SharedConfigCredentials: %s", testConfigFilename),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Filenames: []string{testConfigFilename, testConfigOtherFilename},
|
||||
Profile: "config_file_load_order",
|
||||
Expected: sharedConfig{
|
||||
Region: "shared_config_other_region",
|
||||
Creds: credentials.Value{
|
||||
AccessKeyID: "shared_config_other_akid",
|
||||
SecretAccessKey: "shared_config_other_secret",
|
||||
ProviderName: fmt.Sprintf("SharedConfigCredentials: %s", testConfigOtherFilename),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Filenames: []string{testConfigOtherFilename, testConfigFilename},
|
||||
Profile: "assume_role",
|
||||
Expected: sharedConfig{
|
||||
AssumeRole: assumeRoleConfig{
|
||||
RoleARN: "assume_role_role_arn",
|
||||
SourceProfile: "complete_creds",
|
||||
},
|
||||
AssumeRoleSource: &sharedConfig{
|
||||
Creds: credentials.Value{
|
||||
AccessKeyID: "complete_creds_akid",
|
||||
SecretAccessKey: "complete_creds_secret",
|
||||
ProviderName: fmt.Sprintf("SharedConfigCredentials: %s", testConfigFilename),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Filenames: []string{testConfigOtherFilename, testConfigFilename},
|
||||
Profile: "assume_role_invalid_source_profile",
|
||||
Expected: sharedConfig{
|
||||
AssumeRole: assumeRoleConfig{
|
||||
RoleARN: "assume_role_invalid_source_profile_role_arn",
|
||||
SourceProfile: "profile_not_exists",
|
||||
},
|
||||
},
|
||||
Err: SharedConfigAssumeRoleError{RoleARN: "assume_role_invalid_source_profile_role_arn"},
|
||||
},
|
||||
{
|
||||
Filenames: []string{testConfigOtherFilename, testConfigFilename},
|
||||
Profile: "assume_role_w_creds",
|
||||
Expected: sharedConfig{
|
||||
Creds: credentials.Value{
|
||||
AccessKeyID: "assume_role_w_creds_akid",
|
||||
SecretAccessKey: "assume_role_w_creds_secret",
|
||||
ProviderName: fmt.Sprintf("SharedConfigCredentials: %s", testConfigFilename),
|
||||
},
|
||||
AssumeRole: assumeRoleConfig{
|
||||
RoleARN: "assume_role_w_creds_role_arn",
|
||||
SourceProfile: "assume_role_w_creds",
|
||||
ExternalID: "1234",
|
||||
RoleSessionName: "assume_role_w_creds_session_name",
|
||||
},
|
||||
AssumeRoleSource: &sharedConfig{
|
||||
Creds: credentials.Value{
|
||||
AccessKeyID: "assume_role_w_creds_akid",
|
||||
SecretAccessKey: "assume_role_w_creds_secret",
|
||||
ProviderName: fmt.Sprintf("SharedConfigCredentials: %s", testConfigFilename),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Filenames: []string{testConfigOtherFilename, testConfigFilename},
|
||||
Profile: "assume_role_wo_creds",
|
||||
Expected: sharedConfig{
|
||||
AssumeRole: assumeRoleConfig{
|
||||
RoleARN: "assume_role_wo_creds_role_arn",
|
||||
SourceProfile: "assume_role_wo_creds",
|
||||
},
|
||||
},
|
||||
Err: SharedConfigAssumeRoleError{RoleARN: "assume_role_wo_creds_role_arn"},
|
||||
},
|
||||
{
|
||||
Filenames: []string{filepath.Join("testdata", "shared_config_invalid_ini")},
|
||||
Profile: "profile_name",
|
||||
Err: SharedConfigLoadError{Filename: filepath.Join("testdata", "shared_config_invalid_ini")},
|
||||
},
|
||||
}
|
||||
|
||||
for i, c := range cases {
|
||||
cfg, err := loadSharedConfig(c.Profile, c.Filenames)
|
||||
if c.Err != nil {
|
||||
assert.Contains(t, err.Error(), c.Err.Error(), "expected error, %d", i)
|
||||
continue
|
||||
}
|
||||
|
||||
assert.NoError(t, err, "unexpected error, %d", i)
|
||||
assert.Equal(t, c.Expected, cfg, "not equal, %d", i)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadSharedConfigFromFile(t *testing.T) {
|
||||
filename := testConfigFilename
|
||||
f, err := ini.Load(filename)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load test config file, %s, %v", filename, err)
|
||||
}
|
||||
iniFile := sharedConfigFile{IniData: f, Filename: filename}
|
||||
|
||||
cases := []struct {
|
||||
Profile string
|
||||
Expected sharedConfig
|
||||
Err error
|
||||
}{
|
||||
{
|
||||
Profile: "default",
|
||||
Expected: sharedConfig{Region: "default_region"},
|
||||
},
|
||||
{
|
||||
Profile: "alt_profile_name",
|
||||
Expected: sharedConfig{Region: "alt_profile_name_region"},
|
||||
},
|
||||
{
|
||||
Profile: "short_profile_name_first",
|
||||
Expected: sharedConfig{Region: "short_profile_name_first_short"},
|
||||
},
|
||||
{
|
||||
Profile: "partial_creds",
|
||||
Expected: sharedConfig{},
|
||||
},
|
||||
{
|
||||
Profile: "complete_creds",
|
||||
Expected: sharedConfig{
|
||||
Creds: credentials.Value{
|
||||
AccessKeyID: "complete_creds_akid",
|
||||
SecretAccessKey: "complete_creds_secret",
|
||||
ProviderName: fmt.Sprintf("SharedConfigCredentials: %s", testConfigFilename),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Profile: "complete_creds_with_token",
|
||||
Expected: sharedConfig{
|
||||
Creds: credentials.Value{
|
||||
AccessKeyID: "complete_creds_with_token_akid",
|
||||
SecretAccessKey: "complete_creds_with_token_secret",
|
||||
SessionToken: "complete_creds_with_token_token",
|
||||
ProviderName: fmt.Sprintf("SharedConfigCredentials: %s", testConfigFilename),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Profile: "full_profile",
|
||||
Expected: sharedConfig{
|
||||
Creds: credentials.Value{
|
||||
AccessKeyID: "full_profile_akid",
|
||||
SecretAccessKey: "full_profile_secret",
|
||||
ProviderName: fmt.Sprintf("SharedConfigCredentials: %s", testConfigFilename),
|
||||
},
|
||||
Region: "full_profile_region",
|
||||
},
|
||||
},
|
||||
{
|
||||
Profile: "partial_assume_role",
|
||||
Expected: sharedConfig{},
|
||||
},
|
||||
{
|
||||
Profile: "assume_role",
|
||||
Expected: sharedConfig{
|
||||
AssumeRole: assumeRoleConfig{
|
||||
RoleARN: "assume_role_role_arn",
|
||||
SourceProfile: "complete_creds",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Profile: "assume_role_w_mfa",
|
||||
Expected: sharedConfig{
|
||||
AssumeRole: assumeRoleConfig{
|
||||
RoleARN: "assume_role_role_arn",
|
||||
SourceProfile: "complete_creds",
|
||||
MFASerial: "0123456789",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Profile: "does_not_exists",
|
||||
Err: SharedConfigProfileNotExistsError{Profile: "does_not_exists"},
|
||||
},
|
||||
}
|
||||
|
||||
for i, c := range cases {
|
||||
cfg := sharedConfig{}
|
||||
|
||||
err := cfg.setFromIniFile(c.Profile, iniFile)
|
||||
if c.Err != nil {
|
||||
assert.Contains(t, err.Error(), c.Err.Error(), "expected error, %d", i)
|
||||
continue
|
||||
}
|
||||
|
||||
assert.NoError(t, err, "unexpected error, %d", i)
|
||||
assert.Equal(t, c.Expected, cfg, "not equal, %d", i)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadSharedConfigIniFiles(t *testing.T) {
|
||||
cases := []struct {
|
||||
Filenames []string
|
||||
Expected []sharedConfigFile
|
||||
}{
|
||||
{
|
||||
Filenames: []string{"not_exists", testConfigFilename},
|
||||
Expected: []sharedConfigFile{
|
||||
{Filename: testConfigFilename},
|
||||
},
|
||||
},
|
||||
{
|
||||
Filenames: []string{testConfigFilename, testConfigOtherFilename},
|
||||
Expected: []sharedConfigFile{
|
||||
{Filename: testConfigFilename},
|
||||
{Filename: testConfigOtherFilename},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, c := range cases {
|
||||
files, err := loadSharedConfigIniFiles(c.Filenames)
|
||||
assert.NoError(t, err, "unexpected error, %d", i)
|
||||
assert.Equal(t, len(c.Expected), len(files), "expected num files, %d", i)
|
||||
|
||||
for i, expectedFile := range c.Expected {
|
||||
assert.Equal(t, expectedFile.Filename, files[i].Filename)
|
||||
}
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
[default]
|
||||
s3 =
|
||||
unsupported_key=123
|
||||
other_unsupported=abc
|
||||
|
||||
region = default_region
|
||||
|
||||
[profile alt_profile_name]
|
||||
region = alt_profile_name_region
|
||||
|
||||
[short_profile_name_first]
|
||||
region = short_profile_name_first_short
|
||||
|
||||
[profile short_profile_name_first]
|
||||
region = short_profile_name_first_alt
|
||||
|
||||
[partial_creds]
|
||||
aws_access_key_id = partial_creds_akid
|
||||
|
||||
[complete_creds]
|
||||
aws_access_key_id = complete_creds_akid
|
||||
aws_secret_access_key = complete_creds_secret
|
||||
|
||||
[complete_creds_with_token]
|
||||
aws_access_key_id = complete_creds_with_token_akid
|
||||
aws_secret_access_key = complete_creds_with_token_secret
|
||||
aws_session_token = complete_creds_with_token_token
|
||||
|
||||
[full_profile]
|
||||
aws_access_key_id = full_profile_akid
|
||||
aws_secret_access_key = full_profile_secret
|
||||
region = full_profile_region
|
||||
|
||||
[config_file_load_order]
|
||||
region = shared_config_region
|
||||
aws_access_key_id = shared_config_akid
|
||||
aws_secret_access_key = shared_config_secret
|
||||
|
||||
[partial_assume_role]
|
||||
role_arn = partial_assume_role_role_arn
|
||||
|
||||
[assume_role]
|
||||
role_arn = assume_role_role_arn
|
||||
source_profile = complete_creds
|
||||
|
||||
[assume_role_w_mfa]
|
||||
role_arn = assume_role_role_arn
|
||||
source_profile = complete_creds
|
||||
mfa_serial = 0123456789
|
||||
|
||||
[assume_role_invalid_source_profile]
|
||||
role_arn = assume_role_invalid_source_profile_role_arn
|
||||
source_profile = profile_not_exists
|
||||
|
||||
[assume_role_w_creds]
|
||||
role_arn = assume_role_w_creds_role_arn
|
||||
source_profile = assume_role_w_creds
|
||||
external_id = 1234
|
||||
role_session_name = assume_role_w_creds_session_name
|
||||
aws_access_key_id = assume_role_w_creds_akid
|
||||
aws_secret_access_key = assume_role_w_creds_secret
|
||||
|
||||
[assume_role_wo_creds]
|
||||
role_arn = assume_role_wo_creds_role_arn
|
||||
source_profile = assume_role_wo_creds
|
||||
+1
@@ -0,0 +1 @@
|
||||
[profile_nam
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
[default]
|
||||
region = default_region
|
||||
|
||||
[partial_creds]
|
||||
aws_access_key_id = AKID
|
||||
|
||||
[profile alt_profile_name]
|
||||
region = alt_profile_name_region
|
||||
|
||||
[creds_from_credentials]
|
||||
aws_access_key_id = creds_from_config_akid
|
||||
aws_secret_access_key = creds_from_config_secret
|
||||
|
||||
[config_file_load_order]
|
||||
region = shared_config_other_region
|
||||
aws_access_key_id = shared_config_other_akid
|
||||
aws_secret_access_key = shared_config_other_secret
|
||||
Reference in New Issue
Block a user