From e5abb22bb6c009ac21ce05d7afdf84b34f3f2683 Mon Sep 17 00:00:00 2001 From: Byron Wolfman Date: Mon, 2 Mar 2020 13:23:14 -0500 Subject: [PATCH 1/8] Add support for IAM external ID --- internal/aws.go | 18 +++++++++++++----- internal/docker.go | 8 ++++++++ internal/http_handler.go | 10 +++++----- internal/http_helper.go | 9 +++++++-- 4 files changed, 33 insertions(+), 12 deletions(-) diff --git a/internal/aws.go b/internal/aws.go index fce22f4..a72ca89 100644 --- a/internal/aws.go +++ b/internal/aws.go @@ -85,7 +85,7 @@ func readRoleFromAWS(role string, request *Request) (*iam.Role, error) { return roleObject, nil } -func assumeRoleFromAWS(arn string, request *Request) (*sts.AssumeRoleOutput, error) { +func assumeRoleFromAWS(arn string, externalId string, request *Request) (*sts.AssumeRoleOutput, error) { request.log.Infof("Looking for STS Assume Role for %s", arn) if assumedRole, ok := permissionCache.Get(arn); ok { @@ -96,10 +96,18 @@ func assumeRoleFromAWS(arn string, request *Request) (*sts.AssumeRoleOutput, err request.setLabel("assume_role_from_aws_cache", "miss") request.log.Infof("Requesting STS Assume Role info for %s from AWS", arn) - req := stsService.AssumeRoleRequest(&sts.AssumeRoleInput{ - RoleArn: aws.String(arn), - RoleSessionName: aws.String("go-metadataproxy"), - }) + if externalId == "" { + req := stsService.AssumeRoleRequest(&sts.AssumeRoleInput{ + RoleArn: aws.String(arn), + RoleSessionName: aws.String("go-metadataproxy"), + }) + } else { + req := stsService.AssumeRoleRequest(&sts.AssumeRoleInput{ + ExternalId: aws.String(externalId), + RoleArn: aws.String(arn), + RoleSessionName: aws.String("go-metadataproxy"), + }) + } assumedRole, err := req.Send() if err != nil { diff --git a/internal/docker.go b/internal/docker.go index cd666f9..5f02751 100644 --- a/internal/docker.go +++ b/internal/docker.go @@ -105,6 +105,14 @@ func findDockerContainerIAMRole(container *docker.Container, request *Request) ( return "", fmt.Errorf("Could not find IAM_ROLE in the container ENV config") } +func findDockerContainerExternalId(container *docker.Container, request *Request) (string, error) { + if v, ok := findDockerContainerEnvValue(container, "IAM_EXTERNAL_ID"); ok { + return v, nil + } + + return "", nil +} + func findDockerContainerEnvValue(container *docker.Container, key string) (string, bool) { for _, envPair := range container.Config.Env { chunks := strings.SplitN(envPair, "=", 2) diff --git a/internal/http_handler.go b/internal/http_handler.go index a3d7082..07f0ec3 100644 --- a/internal/http_handler.go +++ b/internal/http_handler.go @@ -105,7 +105,7 @@ func iamInfoHandler(w http.ResponseWriter, r *http.Request) { } // read the role from AWS - roleInfo, err := findContainerRoleByAddress(r.RemoteAddr, request) + roleInfo, externalId, err := findContainerRoleByAddress(r.RemoteAddr, request) if err != nil { request.setLabels(map[string]string{ "response_code": "404", @@ -121,7 +121,7 @@ func iamInfoHandler(w http.ResponseWriter, r *http.Request) { request.setLabel("role_name", *roleInfo.RoleName) // assume the role - assumeRole, err := assumeRoleFromAWS(*roleInfo.Arn, request) + assumeRole, err := assumeRoleFromAWS(*roleInfo.Arn, externalId, request) if err != nil { request.setLabels(map[string]string{ "response_code": "404", @@ -173,7 +173,7 @@ func iamSecurityCredentialsName(w http.ResponseWriter, r *http.Request) { } // read the role from AWS - roleInfo, err := findContainerRoleByAddress(r.RemoteAddr, request) + roleInfo, externalId, err := findContainerRoleByAddress(r.RemoteAddr, request) if err != nil { request.setLabels(map[string]string{ "response_code": "404", @@ -220,7 +220,7 @@ func iamSecurityCredentialsForRole(w http.ResponseWriter, r *http.Request) { } // read the role from AWS - roleInfo, err := findContainerRoleByAddress(r.RemoteAddr, request) + roleInfo, externalId, err := findContainerRoleByAddress(r.RemoteAddr, request) if err != nil { request.setLabels(map[string]string{ "response_code": "404", @@ -245,7 +245,7 @@ func iamSecurityCredentialsForRole(w http.ResponseWriter, r *http.Request) { } // assume the container role - assumeRole, err := assumeRoleFromAWS(*roleInfo.Arn, request) + assumeRole, err := assumeRoleFromAWS(*roleInfo.Arn, externalId, request) if err != nil { request.setLabels(map[string]string{ "response_code": "404", diff --git a/internal/http_helper.go b/internal/http_helper.go index 59046a0..678d22c 100644 --- a/internal/http_helper.go +++ b/internal/http_helper.go @@ -17,7 +17,7 @@ func remoteIP(addr string) string { return strings.Split(addr, ":")[0] } -func findContainerRoleByAddress(addr string, request *Request) (*iam.Role, error) { +func findContainerRoleByAddress(addr string, request *Request) (*iam.Role, string, error) { var container *docker.Container // retry finding the Docker container since sometimes Docker doesn't actually list the container until its been @@ -48,12 +48,17 @@ func findContainerRoleByAddress(addr string, request *Request) (*iam.Role, error return nil, err } + externalId, err := findDockerContainerExternalId(container, request) + if err != nil { + return nil, err + } + role, err := readRoleFromAWS(roleName, request) if err != nil { return nil, err } - return role, nil + return role, externalId, nil } func isCompatibleAPIVersion(r *http.Request) bool { From d53884abc1c3e9fd2dcace5ce51a513b44722931 Mon Sep 17 00:00:00 2001 From: Byron Wolfman Date: Mon, 2 Mar 2020 13:34:17 -0500 Subject: [PATCH 2/8] findDockerContainerExternalId never returns an error, so skip err check --- internal/docker.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/internal/docker.go b/internal/docker.go index 5f02751..d349513 100644 --- a/internal/docker.go +++ b/internal/docker.go @@ -106,11 +106,8 @@ func findDockerContainerIAMRole(container *docker.Container, request *Request) ( } func findDockerContainerExternalId(container *docker.Container, request *Request) (string, error) { - if v, ok := findDockerContainerEnvValue(container, "IAM_EXTERNAL_ID"); ok { - return v, nil - } - - return "", nil + v, _ := findDockerContainerEnvValue(container, "IAM_EXTERNAL_ID") + return v, nil } func findDockerContainerEnvValue(container *docker.Container, key string) (string, bool) { From 6506ad095b2287a80b0d83384353bb3cab8821b0 Mon Sep 17 00:00:00 2001 From: Byron Wolfman Date: Mon, 2 Mar 2020 13:44:33 -0500 Subject: [PATCH 3/8] Construct assume role inputs in helper function --- internal/aws.go | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/internal/aws.go b/internal/aws.go index a72ca89..429fc7b 100644 --- a/internal/aws.go +++ b/internal/aws.go @@ -85,6 +85,23 @@ func readRoleFromAWS(role string, request *Request) (*iam.Role, error) { return roleObject, nil } +func constructAssumeRoleInput(arn string, externalId string, sessionName string) (*sts.AssumeRoleInput) { + + if externalId == "" { + return &sts.AssumeRoleInput{ + RoleArn: aws.String(arn), + RoleSessionName: aws.String(sessionName), + } + } + + return &sts.AssumeRoleInput{ + ExternalId: aws.String(externalId), + RoleArn: aws.String(arn), + RoleSessionName: aws.String(sessionName), + } + +} + func assumeRoleFromAWS(arn string, externalId string, request *Request) (*sts.AssumeRoleOutput, error) { request.log.Infof("Looking for STS Assume Role for %s", arn) @@ -96,18 +113,7 @@ func assumeRoleFromAWS(arn string, externalId string, request *Request) (*sts.As request.setLabel("assume_role_from_aws_cache", "miss") request.log.Infof("Requesting STS Assume Role info for %s from AWS", arn) - if externalId == "" { - req := stsService.AssumeRoleRequest(&sts.AssumeRoleInput{ - RoleArn: aws.String(arn), - RoleSessionName: aws.String("go-metadataproxy"), - }) - } else { - req := stsService.AssumeRoleRequest(&sts.AssumeRoleInput{ - ExternalId: aws.String(externalId), - RoleArn: aws.String(arn), - RoleSessionName: aws.String("go-metadataproxy"), - }) - } + req := stsService.AssumeRoleRequest(constructAssumeRoleInput(arn, externalId, "go-metadataproxy")) assumedRole, err := req.Send() if err != nil { From 6a95ac8da73ef5d729bf6b6c81cdd4f7a352b9c8 Mon Sep 17 00:00:00 2001 From: Byron Wolfman Date: Mon, 2 Mar 2020 13:45:47 -0500 Subject: [PATCH 4/8] Remove empty whitespaces --- internal/aws.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/aws.go b/internal/aws.go index 429fc7b..7f0e2f3 100644 --- a/internal/aws.go +++ b/internal/aws.go @@ -86,7 +86,6 @@ func readRoleFromAWS(role string, request *Request) (*iam.Role, error) { } func constructAssumeRoleInput(arn string, externalId string, sessionName string) (*sts.AssumeRoleInput) { - if externalId == "" { return &sts.AssumeRoleInput{ RoleArn: aws.String(arn), @@ -99,7 +98,6 @@ func constructAssumeRoleInput(arn string, externalId string, sessionName string) RoleArn: aws.String(arn), RoleSessionName: aws.String(sessionName), } - } func assumeRoleFromAWS(arn string, externalId string, request *Request) (*sts.AssumeRoleOutput, error) { From 3b150808350845a12a65fe70e37b26be64afd7f5 Mon Sep 17 00:00:00 2001 From: Byron Wolfman Date: Mon, 2 Mar 2020 13:51:55 -0500 Subject: [PATCH 5/8] Return correct (new) number of args --- internal/http_helper.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/http_helper.go b/internal/http_helper.go index 678d22c..f729a0d 100644 --- a/internal/http_helper.go +++ b/internal/http_helper.go @@ -40,22 +40,22 @@ func findContainerRoleByAddress(addr string, request *Request) (*iam.Role, strin err := backoff.RetryNotify(retryable, b, notify) if err != nil { - return nil, err + return nil, nil, err } roleName, err := findDockerContainerIAMRole(container, request) if err != nil { - return nil, err + return nil, nil, err } externalId, err := findDockerContainerExternalId(container, request) if err != nil { - return nil, err + return nil, nil, err } role, err := readRoleFromAWS(roleName, request) if err != nil { - return nil, err + return nil, nil, err } return role, externalId, nil From 2e1dc14e02f2bd8664c50950da2acad69dc4877b Mon Sep 17 00:00:00 2001 From: Byron Wolfman Date: Mon, 2 Mar 2020 13:56:49 -0500 Subject: [PATCH 6/8] Fix 'cannot use nil as type string' --- internal/http_helper.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/http_helper.go b/internal/http_helper.go index f729a0d..4aff2e6 100644 --- a/internal/http_helper.go +++ b/internal/http_helper.go @@ -40,22 +40,22 @@ func findContainerRoleByAddress(addr string, request *Request) (*iam.Role, strin err := backoff.RetryNotify(retryable, b, notify) if err != nil { - return nil, nil, err + return nil, "", err } roleName, err := findDockerContainerIAMRole(container, request) if err != nil { - return nil, nil, err + return nil, "", err } externalId, err := findDockerContainerExternalId(container, request) if err != nil { - return nil, nil, err + return nil, "", err } role, err := readRoleFromAWS(roleName, request) if err != nil { - return nil, nil, err + return nil, "", err } return role, externalId, nil From fc8b9c6d66157968ffac1131a3d8534e20346f1b Mon Sep 17 00:00:00 2001 From: Byron Wolfman Date: Mon, 2 Mar 2020 14:06:21 -0500 Subject: [PATCH 7/8] Fix 'externalId declared and not used' --- internal/http_handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/http_handler.go b/internal/http_handler.go index 07f0ec3..2bc8ac1 100644 --- a/internal/http_handler.go +++ b/internal/http_handler.go @@ -173,7 +173,7 @@ func iamSecurityCredentialsName(w http.ResponseWriter, r *http.Request) { } // read the role from AWS - roleInfo, externalId, err := findContainerRoleByAddress(r.RemoteAddr, request) + roleInfo, _, err := findContainerRoleByAddress(r.RemoteAddr, request) if err != nil { request.setLabels(map[string]string{ "response_code": "404", From daf85a3ee75e932abb32ccfd48483b86b44263fa Mon Sep 17 00:00:00 2001 From: Byron Wolfman Date: Tue, 3 Mar 2020 09:14:23 -0500 Subject: [PATCH 8/8] Tune/address review feedback --- internal/aws.go | 8 ++++---- internal/docker.go | 4 ++-- internal/http_helper.go | 7 ++----- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/internal/aws.go b/internal/aws.go index 7f0e2f3..18b7125 100644 --- a/internal/aws.go +++ b/internal/aws.go @@ -85,18 +85,18 @@ func readRoleFromAWS(role string, request *Request) (*iam.Role, error) { return roleObject, nil } -func constructAssumeRoleInput(arn string, externalId string, sessionName string) (*sts.AssumeRoleInput) { +func constructAssumeRoleInput(arn string, externalId string) (*sts.AssumeRoleInput) { if externalId == "" { return &sts.AssumeRoleInput{ RoleArn: aws.String(arn), - RoleSessionName: aws.String(sessionName), + RoleSessionName: aws.String("go-metadataproxy"), } } return &sts.AssumeRoleInput{ ExternalId: aws.String(externalId), RoleArn: aws.String(arn), - RoleSessionName: aws.String(sessionName), + RoleSessionName: aws.String("go-metadataproxy"), } } @@ -111,7 +111,7 @@ func assumeRoleFromAWS(arn string, externalId string, request *Request) (*sts.As request.setLabel("assume_role_from_aws_cache", "miss") request.log.Infof("Requesting STS Assume Role info for %s from AWS", arn) - req := stsService.AssumeRoleRequest(constructAssumeRoleInput(arn, externalId, "go-metadataproxy")) + req := stsService.AssumeRoleRequest(constructAssumeRoleInput(arn, externalId)) assumedRole, err := req.Send() if err != nil { diff --git a/internal/docker.go b/internal/docker.go index d349513..aca7159 100644 --- a/internal/docker.go +++ b/internal/docker.go @@ -105,9 +105,9 @@ func findDockerContainerIAMRole(container *docker.Container, request *Request) ( return "", fmt.Errorf("Could not find IAM_ROLE in the container ENV config") } -func findDockerContainerExternalId(container *docker.Container, request *Request) (string, error) { +func findDockerContainerExternalId(container *docker.Container, request *Request) string { v, _ := findDockerContainerEnvValue(container, "IAM_EXTERNAL_ID") - return v, nil + return v } func findDockerContainerEnvValue(container *docker.Container, key string) (string, bool) { diff --git a/internal/http_helper.go b/internal/http_helper.go index 4aff2e6..c67df7a 100644 --- a/internal/http_helper.go +++ b/internal/http_helper.go @@ -48,16 +48,13 @@ func findContainerRoleByAddress(addr string, request *Request) (*iam.Role, strin return nil, "", err } - externalId, err := findDockerContainerExternalId(container, request) - if err != nil { - return nil, "", err - } - role, err := readRoleFromAWS(roleName, request) if err != nil { return nil, "", err } + externalId := findDockerContainerExternalId(container, request) + return role, externalId, nil }