Skip to content

Commit cc7336d

Browse files
committed
Test secrets read permission for CDP and deployment-service
Signed-off-by: Katyanna Moura <[email protected]>
1 parent 3ccedbe commit cc7336d

File tree

2 files changed

+200
-0
lines changed

2 files changed

+200
-0
lines changed

cluster/cluster.yaml

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,76 @@ Resources:
428428
KubernetesGroups:
429429
- zalando:postgres-admin
430430
Type: "STANDARD"
431+
E2EEKSIAMTestCDP:
432+
Properties:
433+
AssumeRolePolicyDocument: !Sub
434+
- |
435+
{
436+
"Version": "2012-10-17",
437+
"Statement": [
438+
{
439+
"Effect": "Allow",
440+
"Principal": {
441+
"Federated": [
442+
"arn:aws:iam::${AWS::AccountId}:oidc-provider/${OIDC}"
443+
]
444+
},
445+
"Action": [
446+
"sts:AssumeRoleWithWebIdentity"
447+
],
448+
"Condition": {
449+
"StringEquals": {
450+
"${OIDC}:sub": "system:serviceaccount:default:cdp"
451+
}
452+
}
453+
}
454+
]
455+
}
456+
- OIDC: !Select [1, !Split ["//", !GetAtt EKSCluster.OpenIdConnectIssuerUrl]]
457+
Path: /
458+
Policies:
459+
- PolicyDocument:
460+
Statement:
461+
- Action: 'secretsmanager:GetSecretValue'
462+
Effect: Allow
463+
Resource: "arn:aws:secretsmanager:{{.Cluster.Region}}:{{.Cluster.InfrastructureAccountID}}:secret:*.*.*"
464+
RoleName: "{{.Cluster.LocalID}}-cdp"
465+
Type: 'AWS::IAM::Role'
466+
E2EEKSIAMTestDeploymentService:
467+
Properties:
468+
AssumeRolePolicyDocument: !Sub
469+
- |
470+
{
471+
"Version": "2012-10-17",
472+
"Statement": [
473+
{
474+
"Effect": "Allow",
475+
"Principal": {
476+
"Federated": [
477+
"arn:aws:iam::${AWS::AccountId}:oidc-provider/${OIDC}"
478+
]
479+
},
480+
"Action": [
481+
"sts:AssumeRoleWithWebIdentity"
482+
],
483+
"Condition": {
484+
"StringEquals": {
485+
"${OIDC}:sub": "system:serviceaccount:kube-system:deployment-service-controller"
486+
}
487+
}
488+
}
489+
]
490+
}
491+
- OIDC: !Select [1, !Split ["//", !GetAtt EKSCluster.OpenIdConnectIssuerUrl]]
492+
Path: /
493+
Policies:
494+
- PolicyDocument:
495+
Statement:
496+
- Action: 'secretsmanager:GetSecretValue'
497+
Effect: Allow
498+
Resource: "arn:aws:secretsmanager:{{.Cluster.Region}}:{{.Cluster.InfrastructureAccountID}}:secret:*.*.*"
499+
RoleName: "{{.Cluster.LocalID}}-deployment-service"
500+
Type: 'AWS::IAM::Role'
431501
{{ end }}
432502
# TODO: IAM POLICY
433503
EKSCNIIPv6Policy:

test/e2e/authorization.go

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,52 @@ var _ = g.Describe("Authorization [RBAC] [Zalando]", func() {
496496
})
497497
})
498498

499+
// Test secret read permissions for CDP and deployment-service
500+
// =============================================================================
501+
// Validates the RBAC permissions granted to CDP and deployment-service for reading
502+
// secrets across all namespaces, including kube-system. These permissions enable
503+
// the workflow where users deploy cluster roles with secret read permissions that
504+
// are subsequently rewritten by the admission controller.
505+
g.When("the service account is deployment-service-controller", func() {
506+
g.BeforeEach(func() {
507+
tc.data.users = []string{"system:serviceaccount:kube-system:deployment-service-controller"}
508+
tc.data.groups = [][]string{{"system:serviceaccounts:kube-system"}}
509+
})
510+
g.It("should allow to read secrets on user namespaces", func() {
511+
tc.data.namespaces = []string{"teapot"}
512+
tc.data.resources = []string{"secrets"}
513+
tc.data.verbs = []string{"read"}
514+
tc.run(context.TODO(), cs, true)
515+
gomega.Expect(tc.output.passed).To(gomega.BeTrue(), tc.output.String())
516+
})
517+
g.It("should allow to read secrets on system namespace", func() {
518+
tc.data.namespaces = []string{"kube-system"}
519+
tc.data.resources = []string{"secrets"}
520+
tc.data.verbs = []string{"read"}
521+
tc.run(context.TODO(), cs, true)
522+
gomega.Expect(tc.output.passed).To(gomega.BeTrue(), tc.output.String())
523+
})
524+
})
525+
g.When("the service account is CDP", func() {
526+
g.BeforeEach(func() {
527+
tc.data.users = []string{"system:serviceaccount:default:cdp"}
528+
tc.data.groups = [][]string{{"system:serviceaccounts:default"}}
529+
})
530+
g.It("should allow to read secrets on user namespaces", func() {
531+
tc.data.namespaces = []string{"teapot"}
532+
tc.data.resources = []string{"secrets"}
533+
tc.data.verbs = []string{"read"}
534+
tc.run(context.TODO(), cs, true)
535+
gomega.Expect(tc.output.passed).To(gomega.BeTrue(), tc.output.String())
536+
})
537+
g.It("should allow to read secrets on system namespace", func() {
538+
tc.data.namespaces = []string{"kube-system"}
539+
tc.data.resources = []string{"secrets"}
540+
tc.data.verbs = []string{"read"}
541+
tc.run(context.TODO(), cs, true)
542+
gomega.Expect(tc.output.passed).To(gomega.BeTrue(), tc.output.String())
543+
})
544+
})
499545
})
500546

501547
g.Context("For administrators", func() {
@@ -885,6 +931,69 @@ var _ = g.Describe("Authorization via admission-controller [RBAC] [Zalando]", fu
885931
gomega.Expect(result.Error()).To(gomega.MatchError(gomega.ContainSubstring("write operations are forbidden")))
886932
})
887933
})
934+
})
935+
936+
// Test secret read permissions for CDP and deployment-service
937+
// =============================================================================
938+
// Validates that the admission controller correctly rewrites ClusterRole
939+
// permissions related to secret access, ensuring that secret read permissions
940+
// granted to CDP and deployment-service are revoked.
941+
g.Context("cdp and deployment-service", func() {
942+
var (
943+
testSecret *corev1.Secret
944+
systemSecret *corev1.Secret
945+
)
946+
947+
g.BeforeEach(func() {
948+
var err error
949+
testSecret, err = createSecret(context.Background(), f.ClientSet, f.Namespace.Name, map[string]string{"application": "my-app"})
950+
framework.ExpectNoError(err)
951+
952+
systemSecret, err = createSecret(context.Background(), f.ClientSet, "kube-system", map[string]string{"application": "my-app"})
953+
framework.ExpectNoError(err)
954+
})
955+
956+
g.Context("cdp", func() {
957+
var client *kubernetes.Clientset
958+
959+
g.BeforeEach(func() {
960+
var err error
961+
962+
client, err = getCDPClient(eksCluster, awsAccountID)
963+
framework.ExpectNoError(err)
964+
})
965+
966+
g.It("should deny secret read access to user namespace", func() {
967+
_, err := client.CoreV1().Secrets(testSecret.Namespace).Get(context.Background(), testSecret.Name, metav1.GetOptions{})
968+
gomega.Expect(err).To(gomega.MatchError(gomega.ContainSubstring("read operations are forbidden")))
969+
})
970+
971+
g.It("should deny secret read access to kube-system namespace", func() {
972+
_, err := client.CoreV1().Secrets(systemSecret.Namespace).Get(context.Background(), systemSecret.Name, metav1.GetOptions{})
973+
gomega.Expect(err).To(gomega.MatchError(gomega.ContainSubstring("read operations are forbidden")))
974+
})
975+
})
976+
977+
g.Context("deployment-service", func() {
978+
var client *kubernetes.Clientset
979+
980+
g.BeforeEach(func() {
981+
var err error
982+
983+
client, err = getDeploymentServiceClient(eksCluster, awsAccountID)
984+
framework.ExpectNoError(err)
985+
})
986+
987+
g.It("should deny secret read access to user namespace", func() {
988+
_, err := client.CoreV1().Secrets(testSecret.Namespace).Get(context.Background(), testSecret.Name, metav1.GetOptions{})
989+
gomega.Expect(err).To(gomega.MatchError(gomega.ContainSubstring("read operations are forbidden")))
990+
})
991+
992+
g.It("should deny secret read access to kube-system namespace", func() {
993+
_, err := client.CoreV1().Secrets(systemSecret.Namespace).Get(context.Background(), systemSecret.Name, metav1.GetOptions{})
994+
gomega.Expect(err).To(gomega.MatchError(gomega.ContainSubstring("read operations are forbidden")))
995+
})
996+
})
888997
})
889998
})
890999

@@ -913,6 +1022,15 @@ func getPostgresAdministratorClient(cluster *types.Cluster, awsAccountID string)
9131022
return newClientWithRole(cluster, fmt.Sprintf("arn:aws:iam::%s:role/%s-e2e-eks-iam-test-postgres-admin-role", awsAccountID, aws.ToString(cluster.Name)))
9141023
}
9151024

1025+
// getCDPClient returns a client with the `zalando:cdp` group.
1026+
func getCDPClient(cluster *types.Cluster, awsAccountID string) (*kubernetes.Clientset, error) {
1027+
return newClientWithRole(cluster, fmt.Sprintf("arn:aws:iam::%s:role/%s-e2e-eks-iam-test-cdp-role", awsAccountID, aws.ToString(cluster.Name)))
1028+
}
1029+
// getDeploymentServiceClient returns a client with the `zalando:deployment-service` group.
1030+
func getDeploymentServiceClient(cluster *types.Cluster, awsAccountID string) (*kubernetes.Clientset, error) {
1031+
return newClientWithRole(cluster, fmt.Sprintf("arn:aws:iam::%s:role/%s-e2e-eks-iam-test-deployment-service-role", awsAccountID, aws.ToString(cluster.Name)))
1032+
}
1033+
9161034
// newClientWithRole returns a new Kubernetes client with the specified IAM role and its associated AccessEntries.
9171035
func newClientWithRole(cluster *types.Cluster, assumeRole string) (*kubernetes.Clientset, error) {
9181036
gen, err := token.NewGenerator(true, false)
@@ -1013,6 +1131,18 @@ func createClusterRole(ctx context.Context, client kubernetes.Interface, labels
10131131
return client.RbacV1().ClusterRoles().Create(ctx, clusterRole, metav1.CreateOptions{})
10141132
}
10151133

1134+
// createSecret creates a Secret with the specified labels.
1135+
func createSecret(ctx context.Context, client kubernetes.Interface, namespace string, labels map[string]string) (*corev1.Secret, error) {
1136+
secret := &corev1.Secret{
1137+
ObjectMeta: metav1.ObjectMeta{
1138+
GenerateName: "test-secret-",
1139+
Labels: labels,
1140+
},
1141+
}
1142+
1143+
return client.CoreV1().Secrets(namespace).Create(ctx, secret, metav1.CreateOptions{})
1144+
}
1145+
10161146
// getAWSAccountID returns the current AWS account's ID.
10171147
func getAWSAccountID(ctx context.Context, awsConfig aws.Config) (string, error) {
10181148
client := sts.NewFromConfig(awsConfig)

0 commit comments

Comments
 (0)