Skip to content

Commit 0097910

Browse files
authored
Merge pull request #9059 from zalando-incubator/collaborator-admission-controller
Allow collaborators write access in collaborator namespaces
2 parents 9f88cfd + 097abfa commit 0097910

File tree

3 files changed

+110
-3
lines changed

3 files changed

+110
-3
lines changed

cluster/cluster.yaml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,37 @@ Resources:
318318
KubernetesGroups:
319319
- zalando:administrator
320320
Type: "STANDARD"
321+
E2EEKSIAMTestRoleCollaborator:
322+
Properties:
323+
AssumeRolePolicyDocument:
324+
Statement:
325+
- Action:
326+
- 'sts:AssumeRole'
327+
- 'sts:SetSourceIdentity'
328+
Effect: Allow
329+
Principal:
330+
AWS: !Sub "arn:aws:iam::${AWS::AccountId}:root"
331+
Version: 2012-10-17
332+
Path: /
333+
RoleName: "{{.Cluster.LocalID}}-e2e-eks-iam-test-collaborator-role"
334+
Type: 'AWS::IAM::Role'
335+
E2EEKSIAMTestAccessEntryCollaborator:
336+
Type: "AWS::EKS::AccessEntry"
337+
Properties:
338+
AccessPolicies:
339+
- AccessScope:
340+
Type: "cluster"
341+
PolicyArn: "arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy"
342+
ClusterName: !Ref EKSCluster
343+
PrincipalArn: !GetAtt E2EEKSIAMTestRoleCollaborator.Arn
344+
Username: !Join
345+
- ''
346+
- - !Sub 'arn:aws:sts::${AWS::AccountId}:assumed-role/'
347+
- !Ref E2EEKSIAMTestRoleCollaborator
348+
- '/{{`{{SessionName}}`}}'
349+
KubernetesGroups:
350+
- zalando:collaborator
351+
Type: "STANDARD"
321352
E2EEKSIAMTestRolePostgresAdministrator:
322353
Properties:
323354
AssumeRolePolicyDocument:

cluster/manifests/02-admission-control/teapot.yaml

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -538,7 +538,7 @@ webhooks:
538538
matchExpressions:
539539
- key: kubernetes.io/metadata.name
540540
operator: In
541-
values: [ "kube-system", "visibility", "kubenurse" ]
541+
values: [ "kube-system", "kubenurse" ]
542542
rules:
543543
- operations: [ "*" ]
544544
apiGroups: ["*"]
@@ -552,6 +552,39 @@ webhooks:
552552
expression: '!(request.userInfo.username in ["system:kube-controller-manager", "system:kube-scheduler", "zalando-iam:zalando:service:k8sapi_credentials-provider"])'
553553
- name: 'exclude-eks-components'
554554
expression: '!request.userInfo.username.startsWith("eks:")'
555+
- name: collaborator-deny-admitter.teapot.zalan.do
556+
clientConfig:
557+
{{- if eq .Cluster.Provider "zalando-eks"}}
558+
service:
559+
name: "admission-controller"
560+
namespace: "kube-system"
561+
path: "/deny"
562+
{{- else }}
563+
url: "https://localhost:8085/deny"
564+
{{- end }}
565+
caBundle: "{{ .Cluster.ConfigItems.ca_cert_decompressed }}"
566+
admissionReviewVersions: ["v1beta1"]
567+
failurePolicy: Fail
568+
sideEffects: "NoneOnDryRun"
569+
matchPolicy: Equivalent
570+
namespaceSelector:
571+
matchExpressions:
572+
- key: kubernetes.io/metadata.name
573+
operator: In
574+
values: [ "visibility" ]
575+
rules:
576+
- operations: [ "*" ]
577+
apiGroups: ["*"]
578+
apiVersions: ["*"]
579+
resources: ["*/*"]
580+
scope: "Namespaced"
581+
matchConditions:
582+
- name: 'exclude-privileged-groups'
583+
expression: 'request.userInfo.groups.all(g, !(g in ["system:masters", "system:nodes", "system:serviceaccounts:kube-system", "okta:common/administrator", "zalando:administrator", "okta:common/collaborator", "zalando:collaborator"]))'
584+
- name: 'exclude-privileged-usernames'
585+
expression: '!(request.userInfo.username in ["system:kube-controller-manager", "system:kube-scheduler", "zalando-iam:zalando:service:k8sapi_credentials-provider"])'
586+
- name: 'exclude-eks-components'
587+
expression: '!request.userInfo.username.startsWith("eks:")'
555588
- name: global-deny-admitter.teapot.zalan.do
556589
clientConfig:
557590
{{- if eq .Cluster.Provider "zalando-eks"}}

test/e2e/authorization.go

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -590,12 +590,14 @@ var _ = g.Describe("Authorization via admission-controller [RBAC] [Zalando]", fu
590590

591591
g.Context("for namespaced resources", func() {
592592
var (
593-
systemResource *corev1.Pod
594-
nonSystemResource *corev1.Pod
593+
systemResource *corev1.Pod
594+
collaboratorResource *corev1.Pod
595+
nonSystemResource *corev1.Pod
595596
)
596597

597598
g.BeforeEach(func() {
598599
systemResource = examplePod("kube-system", nil)
600+
collaboratorResource = examplePod("visibility", nil)
599601
nonSystemResource = examplePod(f.Namespace.Name, nil)
600602
})
601603

@@ -614,12 +616,43 @@ var _ = g.Describe("Authorization via admission-controller [RBAC] [Zalando]", fu
614616
framework.ExpectNoError(err, "failed to create pod: %s in namespace: %s", nonSystemResource.Name, nonSystemResource.Namespace)
615617
})
616618

619+
g.It("should allow write access in collaborator namespace", func() {
620+
_, err := client.CoreV1().Pods(collaboratorResource.Namespace).Create(context.Background(), collaboratorResource, metav1.CreateOptions{DryRun: []string{"All"}})
621+
framework.ExpectNoError(err, "failed to create pod: %s in namespace: %s", collaboratorResource.Name, collaboratorResource.Namespace)
622+
})
623+
617624
g.It("should allow write access in system namespace", func() {
618625
_, err := client.CoreV1().Pods(systemResource.Namespace).Create(context.Background(), systemResource, metav1.CreateOptions{DryRun: []string{"All"}})
619626
framework.ExpectNoError(err, "failed to create pod: %s in namespace: %s", systemResource.Name, systemResource.Namespace)
620627
})
621628
})
622629

630+
g.Context("as collaborator user", func() {
631+
var client *kubernetes.Clientset
632+
633+
g.BeforeEach(func() {
634+
var err error
635+
636+
client, err = getCollaboratorClient(eksCluster, awsAccountID)
637+
framework.ExpectNoError(err)
638+
})
639+
640+
g.It("should allow write access in user namespace", func() {
641+
_, err := client.CoreV1().Pods(nonSystemResource.Namespace).Create(context.Background(), nonSystemResource, metav1.CreateOptions{DryRun: []string{"All"}})
642+
framework.ExpectNoError(err, "failed to create pod: %s in namespace: %s", nonSystemResource.Name, nonSystemResource.Namespace)
643+
})
644+
645+
g.It("should allow write access in collaborator namespace", func() {
646+
_, err := client.CoreV1().Pods(collaboratorResource.Namespace).Create(context.Background(), collaboratorResource, metav1.CreateOptions{DryRun: []string{"All"}})
647+
framework.ExpectNoError(err, "failed to create pod: %s in namespace: %s", collaboratorResource.Name, collaboratorResource.Namespace)
648+
})
649+
650+
g.It("should deny write access in system namespace", func() {
651+
_, err := client.CoreV1().Pods(systemResource.Namespace).Create(context.Background(), systemResource, metav1.CreateOptions{DryRun: []string{"All"}})
652+
gomega.Expect(err).To(gomega.MatchError(gomega.ContainSubstring("write operations are forbidden")))
653+
})
654+
})
655+
623656
g.Context("as unprivileged user", func() {
624657
var client *kubernetes.Clientset
625658

@@ -635,6 +668,11 @@ var _ = g.Describe("Authorization via admission-controller [RBAC] [Zalando]", fu
635668
framework.ExpectNoError(err, "failed to create pod: %s in namespace: %s", nonSystemResource.Name, nonSystemResource.Namespace)
636669
})
637670

671+
g.It("should deny write access in collaborator namespace", func() {
672+
_, err := client.CoreV1().Pods(collaboratorResource.Namespace).Create(context.Background(), collaboratorResource, metav1.CreateOptions{DryRun: []string{"All"}})
673+
gomega.Expect(err).To(gomega.MatchError(gomega.ContainSubstring("write operations are forbidden")))
674+
})
675+
638676
g.It("should deny write access in system namespace", func() {
639677
_, err := client.CoreV1().Pods(systemResource.Namespace).Create(context.Background(), systemResource, metav1.CreateOptions{DryRun: []string{"All"}})
640678
gomega.Expect(err).To(gomega.MatchError(gomega.ContainSubstring("write operations are forbidden")))
@@ -806,6 +844,11 @@ func getPrivilegedClient(cluster *types.Cluster, awsAccountID string) (*kubernet
806844
return newClientWithRole(cluster, fmt.Sprintf("arn:aws:iam::%s:role/%s-e2e-eks-iam-test-privileged-role", awsAccountID, aws.ToString(cluster.Name)))
807845
}
808846

847+
// getCollaboratorClient returns a client with the `zalando:collaborator` group.
848+
func getCollaboratorClient(cluster *types.Cluster, awsAccountID string) (*kubernetes.Clientset, error) {
849+
return newClientWithRole(cluster, fmt.Sprintf("arn:aws:iam::%s:role/%s-e2e-eks-iam-test-collaborator-role", awsAccountID, aws.ToString(cluster.Name)))
850+
}
851+
809852
// getUnprivilegedClient returns a client with the `zalando:readonly` group.
810853
func getUnprivilegedClient(cluster *types.Cluster, awsAccountID string) (*kubernetes.Clientset, error) {
811854
return newClientWithRole(cluster, fmt.Sprintf("arn:aws:iam::%s:role/%s-e2e-eks-iam-test-unprivileged-role", awsAccountID, aws.ToString(cluster.Name)))

0 commit comments

Comments
 (0)