Skip to content

Commit 48d7d50

Browse files
carisetaraspos
authored andcommitted
Update app label value (aws#28)
`eks-pod-identiy-agent` -> `eks-pod-identity-agent` fix: fix metrics service selector (aws#24) fix: tidy go mods to fix vendoring (aws#25) Update app label value (aws#28) `eks-pod-identiy-agent` -> `eks-pod-identity-agent` Initial service account fetcher logic Add `kubernetes.io` parsing logic
1 parent 60a37c4 commit 48d7d50

File tree

10 files changed

+423
-133
lines changed

10 files changed

+423
-133
lines changed

charts/eks-pod-identity-agent/templates/_helpers.tpl

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ helm.sh/chart: {{ include "eks-pod-identity-agent.chart" . }}
4040
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
4141
{{- end }}
4242
app.kubernetes.io/managed-by: {{ .Release.Service }}
43-
app: eks-pod-identiy-agent
43+
app: eks-pod-identity-agent
4444
{{- end }}
4545

4646
{{/*

go.mod

+32-3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ require (
2020
go.uber.org/mock v0.3.0
2121
golang.org/x/sys v0.25.0
2222
golang.org/x/time v0.3.0
23+
k8s.io/api v0.31.1
24+
k8s.io/apimachinery v0.31.1
25+
k8s.io/client-go v0.31.1
2326
)
2427

2528
require (
@@ -33,21 +36,47 @@ require (
3336
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4 // indirect
3437
github.com/beorn7/perks v1.0.1 // indirect
3538
github.com/cespare/xxhash/v2 v2.3.0 // indirect
36-
github.com/davecgh/go-spew v1.1.1 // indirect
39+
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
40+
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
41+
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
42+
github.com/go-logr/logr v1.4.2 // indirect
43+
github.com/go-openapi/jsonpointer v0.19.6 // indirect
44+
github.com/go-openapi/jsonreference v0.20.2 // indirect
45+
github.com/go-openapi/swag v0.22.4 // indirect
46+
github.com/gogo/protobuf v1.3.2 // indirect
47+
github.com/golang/protobuf v1.5.4 // indirect
48+
github.com/google/gnostic-models v0.6.8 // indirect
3749
github.com/google/go-cmp v0.6.0 // indirect
50+
github.com/google/gofuzz v1.2.0 // indirect
51+
github.com/google/uuid v1.6.0 // indirect
3852
github.com/inconshreveable/mousetrap v1.1.0 // indirect
3953
github.com/jmespath/go-jmespath v0.4.0 // indirect
54+
github.com/josharian/intern v1.0.0 // indirect
55+
github.com/json-iterator/go v1.1.12 // indirect
4056
github.com/klauspost/compress v1.17.9 // indirect
41-
github.com/kr/text v0.2.0 // indirect
57+
github.com/mailru/easyjson v0.7.7 // indirect
58+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
59+
github.com/modern-go/reflect2 v1.0.2 // indirect
4260
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
43-
github.com/pmezard/go-difflib v1.0.0 // indirect
61+
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
4462
github.com/prometheus/client_model v0.6.1 // indirect
4563
github.com/prometheus/common v0.55.0 // indirect
4664
github.com/prometheus/procfs v0.15.1 // indirect
4765
github.com/spf13/pflag v1.0.5 // indirect
4866
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect
67+
github.com/x448/float16 v0.8.4 // indirect
4968
golang.org/x/net v0.29.0 // indirect
69+
golang.org/x/oauth2 v0.21.0 // indirect
70+
golang.org/x/term v0.24.0 // indirect
5071
golang.org/x/text v0.18.0 // indirect
5172
google.golang.org/protobuf v1.34.2 // indirect
73+
gopkg.in/inf.v0 v0.9.1 // indirect
74+
gopkg.in/yaml.v2 v2.4.0 // indirect
5275
gopkg.in/yaml.v3 v3.0.1 // indirect
76+
k8s.io/klog/v2 v2.130.1 // indirect
77+
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
78+
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
79+
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
80+
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
81+
sigs.k8s.io/yaml v1.4.0 // indirect
5382
)

go.sum

+112-11
Large diffs are not rendered by default.

pkg/extensions/chainrole/chainrole.go

+36-73
Original file line numberDiff line numberDiff line change
@@ -10,45 +10,47 @@ import (
1010

1111
"github.com/aws/aws-sdk-go-v2/aws"
1212
"github.com/aws/aws-sdk-go-v2/aws/arn"
13-
"github.com/aws/aws-sdk-go-v2/config"
14-
awsCreds "github.com/aws/aws-sdk-go-v2/credentials"
15-
"github.com/aws/aws-sdk-go-v2/service/eks"
1613
"github.com/aws/aws-sdk-go-v2/service/sts"
1714
"github.com/aws/aws-sdk-go-v2/service/sts/types"
1815
"github.com/golang-jwt/jwt/v5"
1916
"github.com/sirupsen/logrus"
2017
"go.amzn.com/eks/eks-pod-identity-agent/internal/middleware/logger"
2118
"go.amzn.com/eks/eks-pod-identity-agent/pkg/credentials"
19+
"go.amzn.com/eks/eks-pod-identity-agent/pkg/extensions/chainrole/ekspodidentities"
20+
"go.amzn.com/eks/eks-pod-identity-agent/pkg/extensions/chainrole/serviceaccount"
2221
)
2322

2423
const (
2524
assumeRoleAnnotationPrefix = "assume-role.ekspia.go.amzn.com/"
2625
sessionTagRoleAnnotationPrefix = assumeRoleAnnotationPrefix + "session-tag/"
26+
// service account annotations doesn't support more than one "/"
27+
sessionTagRoleAnnotationPrefix2 = assumeRoleAnnotationPrefix + "session-tag-"
2728
)
2829

2930
type (
3031
roleAssumer interface {
3132
AssumeRole(ctx context.Context, params *sts.AssumeRoleInput, optFns ...func(*sts.Options)) (*sts.AssumeRoleOutput, error)
3233
}
3334

34-
sessionConfigFunc func(ctx context.Context, awsCfg aws.Config, clusterName string, associationID string) (*sts.AssumeRoleInput, error)
35+
sessionConfigRetriever interface {
36+
GetSessionConfigMap(ctx context.Context, request *credentials.EksCredentialsRequest) (map[string]string, error)
37+
}
3538

3639
CredentialRetriever struct {
3740
delegate credentials.CredentialRetriever
3841
jwtParser *jwt.Parser
3942
roleAssumer roleAssumer
40-
getSessionConfig sessionConfigFunc
43+
sessionConfigRetriever sessionConfigRetriever
4144
reNamespaceFilter *regexp.Regexp
4245
reServiceAccountFilter *regexp.Regexp
4346
}
4447
)
4548

4649
func NewCredentialsRetriever(awsCfg aws.Config, eksCredentialsRetriever credentials.CredentialRetriever) *CredentialRetriever {
4750
cr := &CredentialRetriever{
48-
delegate: eksCredentialsRetriever,
49-
jwtParser: jwt.NewParser(),
50-
roleAssumer: sts.NewFromConfig(awsCfg),
51-
getSessionConfig: getSessionConfigurationFromEKSPodIdentityTags,
51+
delegate: eksCredentialsRetriever,
52+
jwtParser: jwt.NewParser(),
53+
roleAssumer: sts.NewFromConfig(awsCfg),
5254
}
5355

5456
log := logger.FromContext(context.TODO()).WithField("extension", "chainrole")
@@ -69,75 +71,46 @@ func NewCredentialsRetriever(awsCfg aws.Config, eksCredentialsRetriever credenti
6971
log.Info("Enabled extension...")
7072
}
7173

72-
return cr
73-
}
74-
75-
func getSessionConfigurationFromEKSPodIdentityTags(ctx context.Context, awsCfg aws.Config, clusterName, associationID string) (*sts.AssumeRoleInput, error) {
76-
// Describe pod identity association to get tags
77-
podIdentityAssociation, err := eks.NewFromConfig(awsCfg).DescribePodIdentityAssociation(ctx,
78-
&eks.DescribePodIdentityAssociationInput{
79-
AssociationId: aws.String(associationID),
80-
ClusterName: aws.String(clusterName),
81-
})
82-
if err != nil {
83-
return nil, fmt.Errorf("error describing pod identity association %s/%s: %w", clusterName, associationID, err)
74+
switch sessionConfigSourceVal {
75+
case eksPodIdentityAssociationTags:
76+
cr.sessionConfigRetriever = ekspodidentities.NewSessionConfigRetriever(eksCredentialsRetriever)
77+
case serviceAccountAnnotations:
78+
cr.sessionConfigRetriever = serviceaccount.NewSessionConfigRetriever()
79+
default:
8480
}
8581

86-
assumeRoleInput := tagsToSTSAssumeRole(podIdentityAssociation.Association.Tags)
87-
88-
if assumeRoleInput.RoleArn == nil {
89-
return nil, fmt.Errorf("couldn't get assume role arn from pod identity association tags %v", podIdentityAssociation.Association.Tags)
90-
}
91-
92-
return assumeRoleInput, nil
82+
return cr
9383
}
9484

9585
func (c *CredentialRetriever) GetIamCredentials(ctx context.Context, request *credentials.EksCredentialsRequest) (
9686
*credentials.EksCredentialsResponse, credentials.ResponseMetadata, error) {
9787
log := logger.FromContext(ctx).WithField("extension", "chainrole")
9888

99-
// Get AWS EKS Pod Identity credentials as usual
100-
iamCredentials, responseMetadata, err := c.delegate.GetIamCredentials(ctx, request)
101-
if err != nil {
102-
return nil, nil, err
103-
}
104-
10589
// Get Namespace and ServiceAccount names from JWT token
10690
ns, sa, err := c.serviceAccountFromJWT(request.ServiceAccountToken)
10791
if err != nil {
10892
return nil, nil, fmt.Errorf("error parsing JWT token: %w", err)
10993
}
11094

111-
log = log.WithFields(logrus.Fields{
112-
"namespace": ns,
113-
"serviceaccount": sa,
114-
"cluster-name": request.ClusterName,
115-
"association-id": responseMetadata.AssociationId(),
116-
})
117-
11895
// Check if Namespace/ServiceAccount filters configured
11996
// and do not proceed with role chaining if they don't match
12097
if !c.isEnabledFor(ns, sa) {
12198
log.Debug("namespace/serviceaccount do not match ChainRole filter. Skipping role chaining")
122-
return iamCredentials, responseMetadata, nil
99+
return c.delegate.GetIamCredentials(ctx, request)
123100
}
124101

125-
// Assume eks pod identity credentials
126-
podIdentityCfg, err := config.LoadDefaultConfig(context.TODO(), config.WithCredentialsProvider(
127-
awsCreds.NewStaticCredentialsProvider(iamCredentials.AccessKeyId, iamCredentials.SecretAccessKey, iamCredentials.Token),
128-
))
129-
if err != nil {
130-
return nil, nil, fmt.Errorf("error loading pod identity credentials: %w", err)
131-
}
102+
log = log.WithFields(logrus.Fields{
103+
"namespace": ns,
104+
"serviceaccount": sa,
105+
"cluster-name": request.ClusterName,
106+
})
132107

133-
// Assume new session based on the configurations provided in tags
134-
// session is assumed based on the IRSA credentials and NOT EKS Identity credentials
135-
// this is because EKS Identity credentials adds bunch of default tags
136-
// leaving no space for our custom tags https://github.com/aws/containers-roadmap/issues/2413
137-
assumeRoleInput, err := c.getSessionConfig(ctx, podIdentityCfg, request.ClusterName, responseMetadata.AssociationId())
108+
sessionConfigMap, err := c.sessionConfigRetriever.GetSessionConfigMap(ctx, request)
138109
if err != nil {
139-
return nil, nil, fmt.Errorf("error getting session configuration: %w", err)
110+
return nil, nil, err
140111
}
112+
113+
assumeRoleInput := tagsToSTSAssumeRole(sessionConfigMap)
141114
assumeRoleOutput, err := c.roleAssumer.AssumeRole(ctx, assumeRoleInput)
142115
if err != nil {
143116
return nil, nil, fmt.Errorf("error assuming role %s: %w", *assumeRoleInput.RoleArn, err)
@@ -154,7 +127,7 @@ func (c *CredentialRetriever) GetIamCredentials(ctx context.Context, request *cr
154127
return nil, nil, fmt.Errorf("error formatting IAM credentials: %w", err)
155128
}
156129

157-
return assumedCredentials, responseMetadata, nil
130+
return assumedCredentials, nil, nil
158131
}
159132

160133
func (c *CredentialRetriever) isEnabledFor(namespace, serviceAccount string) bool {
@@ -196,8 +169,9 @@ func tagsToSTSAssumeRole(tags map[string]string) *sts.AssumeRoleInput {
196169
assumeRoleParams.DurationSeconds = aws.Int32(int32(duration.Seconds()))
197170
}
198171

199-
if strings.HasPrefix(key, sessionTagRoleAnnotationPrefix) {
172+
if strings.HasPrefix(key, sessionTagRoleAnnotationPrefix) || strings.HasPrefix(key, sessionTagRoleAnnotationPrefix2) {
200173
tagKey := strings.TrimPrefix(key, sessionTagRoleAnnotationPrefix)
174+
tagKey = strings.TrimPrefix(tagKey, sessionTagRoleAnnotationPrefix2)
201175

202176
assumeRoleParams.Tags = append(assumeRoleParams.Tags, types.Tag{
203177
Key: aws.String(tagKey),
@@ -228,26 +202,15 @@ func formatIAMCredentials(o *sts.AssumeRoleOutput) (*credentials.EksCredentialsR
228202
}, nil
229203
}
230204

231-
func (c *CredentialRetriever) serviceAccountFromJWT(token string) (string, string, error) {
232-
parsedToken, _, err := c.jwtParser.ParseUnverified(token, &jwt.RegisteredClaims{})
205+
func (c *CredentialRetriever) serviceAccountFromJWT(token string) (ns string, sa string, err error) {
206+
claims, subject, err := serviceaccount.ServiceAccountFromJWT(token)
233207
if err != nil {
234208
return "", "", fmt.Errorf("error parsing JWT token: %w", err)
235209
}
236210

237-
subject, err := parsedToken.Claims.GetSubject()
238-
if err != nil {
239-
return "", "", fmt.Errorf("error reading JWT token subject: %w", err)
240-
}
241-
242-
// subject is in the format: system:serviceaccount:<namespace>:<service_account>
243-
if !strings.HasPrefix(subject, "system:serviceaccount:") {
244-
return "", "", errors.New("JWT token claim subject doesn't start with 'system:serviceaccount:'")
245-
}
246-
247-
subjectParts := strings.Split(subject, ":")
248-
if len(subjectParts) < 4 {
249-
return "", "", errors.New("invalid JWT token claim subject")
211+
if claims != nil && claims.Namespace != "" && claims.ServiceAccount.Name != "" {
212+
return claims.Namespace, claims.ServiceAccount.Name, nil
250213
}
251214

252-
return subjectParts[2], subjectParts[3], nil
215+
return serviceaccount.ServiceAccountFromJWTSubject(subject)
253216
}

0 commit comments

Comments
 (0)