|
@@ -62,6 +62,13 @@ const (
|
|
|
// The AWS authorization header name for the auto-generated date.
|
|
|
awsDateHeader = "x-amz-date"
|
|
|
|
|
|
+ // Supported AWS configuration environment variables.
|
|
|
+ awsAccessKeyId = "AWS_ACCESS_KEY_ID"
|
|
|
+ awsDefaultRegion = "AWS_DEFAULT_REGION"
|
|
|
+ awsRegion = "AWS_REGION"
|
|
|
+ awsSecretAccessKey = "AWS_SECRET_ACCESS_KEY"
|
|
|
+ awsSessionToken = "AWS_SESSION_TOKEN"
|
|
|
+
|
|
|
awsTimeFormatLong = "20060102T150405Z"
|
|
|
awsTimeFormatShort = "20060102"
|
|
|
)
|
|
@@ -267,6 +274,49 @@ type awsRequest struct {
|
|
|
Headers []awsRequestHeader `json:"headers"`
|
|
|
}
|
|
|
|
|
|
+func (cs awsCredentialSource) validateMetadataServers() error {
|
|
|
+ if err := cs.validateMetadataServer(cs.RegionURL, "region_url"); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ if err := cs.validateMetadataServer(cs.CredVerificationURL, "url"); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ return cs.validateMetadataServer(cs.IMDSv2SessionTokenURL, "imdsv2_session_token_url")
|
|
|
+}
|
|
|
+
|
|
|
+var validHostnames []string = []string{"169.254.169.254", "fd00:ec2::254"}
|
|
|
+
|
|
|
+func (cs awsCredentialSource) isValidMetadataServer(metadataUrl string) bool {
|
|
|
+ if metadataUrl == "" {
|
|
|
+ // Zero value means use default, which is valid.
|
|
|
+ return true
|
|
|
+ }
|
|
|
+
|
|
|
+ u, err := url.Parse(metadataUrl)
|
|
|
+ if err != nil {
|
|
|
+ // Unparseable URL means invalid
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, validHostname := range validHostnames {
|
|
|
+ if u.Hostname() == validHostname {
|
|
|
+ // If it's one of the valid hostnames, everything is good
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // hostname not found in our allowlist, so not valid
|
|
|
+ return false
|
|
|
+}
|
|
|
+
|
|
|
+func (cs awsCredentialSource) validateMetadataServer(metadataUrl, urlName string) error {
|
|
|
+ if !cs.isValidMetadataServer(metadataUrl) {
|
|
|
+ return fmt.Errorf("oauth2/google: invalid hostname %s for %s", metadataUrl, urlName)
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
func (cs awsCredentialSource) doRequest(req *http.Request) (*http.Response, error) {
|
|
|
if cs.client == nil {
|
|
|
cs.client = oauth2.NewClient(cs.ctx, nil)
|
|
@@ -274,16 +324,33 @@ func (cs awsCredentialSource) doRequest(req *http.Request) (*http.Response, erro
|
|
|
return cs.client.Do(req.WithContext(cs.ctx))
|
|
|
}
|
|
|
|
|
|
+func canRetrieveRegionFromEnvironment() bool {
|
|
|
+ // The AWS region can be provided through AWS_REGION or AWS_DEFAULT_REGION. Only one is
|
|
|
+ // required.
|
|
|
+ return getenv(awsRegion) != "" || getenv(awsDefaultRegion) != ""
|
|
|
+}
|
|
|
+
|
|
|
+func canRetrieveSecurityCredentialFromEnvironment() bool {
|
|
|
+ // Check if both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are available.
|
|
|
+ return getenv(awsAccessKeyId) != "" && getenv(awsSecretAccessKey) != ""
|
|
|
+}
|
|
|
+
|
|
|
+func shouldUseMetadataServer() bool {
|
|
|
+ return !canRetrieveRegionFromEnvironment() || !canRetrieveSecurityCredentialFromEnvironment()
|
|
|
+}
|
|
|
+
|
|
|
func (cs awsCredentialSource) subjectToken() (string, error) {
|
|
|
if cs.requestSigner == nil {
|
|
|
- awsSessionToken, err := cs.getAWSSessionToken()
|
|
|
- if err != nil {
|
|
|
- return "", err
|
|
|
- }
|
|
|
-
|
|
|
headers := make(map[string]string)
|
|
|
- if awsSessionToken != "" {
|
|
|
- headers[awsIMDSv2SessionTokenHeader] = awsSessionToken
|
|
|
+ if shouldUseMetadataServer() {
|
|
|
+ awsSessionToken, err := cs.getAWSSessionToken()
|
|
|
+ if err != nil {
|
|
|
+ return "", err
|
|
|
+ }
|
|
|
+
|
|
|
+ if awsSessionToken != "" {
|
|
|
+ headers[awsIMDSv2SessionTokenHeader] = awsSessionToken
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
awsSecurityCredentials, err := cs.getSecurityCredentials(headers)
|
|
@@ -389,11 +456,11 @@ func (cs *awsCredentialSource) getAWSSessionToken() (string, error) {
|
|
|
}
|
|
|
|
|
|
func (cs *awsCredentialSource) getRegion(headers map[string]string) (string, error) {
|
|
|
- if envAwsRegion := getenv("AWS_REGION"); envAwsRegion != "" {
|
|
|
- return envAwsRegion, nil
|
|
|
- }
|
|
|
- if envAwsRegion := getenv("AWS_DEFAULT_REGION"); envAwsRegion != "" {
|
|
|
- return envAwsRegion, nil
|
|
|
+ if canRetrieveRegionFromEnvironment() {
|
|
|
+ if envAwsRegion := getenv(awsRegion); envAwsRegion != "" {
|
|
|
+ return envAwsRegion, nil
|
|
|
+ }
|
|
|
+ return getenv("AWS_DEFAULT_REGION"), nil
|
|
|
}
|
|
|
|
|
|
if cs.RegionURL == "" {
|
|
@@ -434,14 +501,12 @@ func (cs *awsCredentialSource) getRegion(headers map[string]string) (string, err
|
|
|
}
|
|
|
|
|
|
func (cs *awsCredentialSource) getSecurityCredentials(headers map[string]string) (result awsSecurityCredentials, err error) {
|
|
|
- if accessKeyID := getenv("AWS_ACCESS_KEY_ID"); accessKeyID != "" {
|
|
|
- if secretAccessKey := getenv("AWS_SECRET_ACCESS_KEY"); secretAccessKey != "" {
|
|
|
- return awsSecurityCredentials{
|
|
|
- AccessKeyID: accessKeyID,
|
|
|
- SecretAccessKey: secretAccessKey,
|
|
|
- SecurityToken: getenv("AWS_SESSION_TOKEN"),
|
|
|
- }, nil
|
|
|
- }
|
|
|
+ if canRetrieveSecurityCredentialFromEnvironment() {
|
|
|
+ return awsSecurityCredentials{
|
|
|
+ AccessKeyID: getenv(awsAccessKeyId),
|
|
|
+ SecretAccessKey: getenv(awsSecretAccessKey),
|
|
|
+ SecurityToken: getenv(awsSessionToken),
|
|
|
+ }, nil
|
|
|
}
|
|
|
|
|
|
roleName, err := cs.getMetadataRoleName(headers)
|