Skip to content

Commit

Permalink
feat(k8sdisallowanonymous): allow preventing system:authenticated (#579)
Browse files Browse the repository at this point in the history
Previously, the k8sdisallowanonymous would prevent bindings to the
system:anonymous and system:unauthenticated groups.

For Google Kubernetes Engine, the system:authenticated group is a
potential security threat.  This was described by Orca Security in this
blog post:

https://orca.security/resources/blog/sys-all-google-kubernetes-engine-risk/

This PR updates the k8sdisallowanonymous to prevent bindings to
`system:authenticated` if the parameters.disallowAuthenticated toggle is
set to true.  This will not break existing customers as the default
boolean value is false, leaving this functionality disabled.

Signed-off-by: juliankatz <[email protected]>
  • Loading branch information
julianKatz authored Aug 27, 2024
1 parent d975f10 commit 5153330
Show file tree
Hide file tree
Showing 17 changed files with 407 additions and 32 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
version: 1.1.0
name: k8sdisallowanonymous
displayName: Disallow Anonymous Access
createdAt: "2024-08-21T21:13:49Z"
description: Disallows associating ClusterRole and Role resources to the system:anonymous user and system:unauthenticated group.
digest: 8de75985d7841ab683cf095d1b86934a3d3278b20816c0bb2a10680d25ddd740
license: Apache-2.0
homeURL: https://open-policy-agent.github.io/gatekeeper-library/website/disallowanonymous
keywords:
- gatekeeper
- open-policy-agent
- policies
readme: |-
# Disallow Anonymous Access
Disallows associating ClusterRole and Role resources to the system:anonymous user and system:unauthenticated group.
install: |-
### Usage
```shell
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/artifacthub/library/general/disallowanonymous/1.1.0/template.yaml
```
provider:
name: Gatekeeper Library
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
resources:
- template.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sDisallowAnonymous
metadata:
name: no-anonymous
spec:
match:
kinds:
- apiGroups: ["rbac.authorization.k8s.io"]
kinds: ["ClusterRoleBinding"]
- apiGroups: ["rbac.authorization.k8s.io"]
kinds: ["RoleBinding"]
parameters:
allowedRoles:
- cluster-role-1
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: cluster-role-binding-1
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-role-1
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: Group
name: system:authenticated
- apiGroup: rbac.authorization.k8s.io
kind: Group
name: system:unauthenticated
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: cluster-role-binding-2
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-role-2
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: Group
name: system:authenticated
- apiGroup: rbac.authorization.k8s.io
kind: Group
name: system:unauthenticated
- apiGroup: rbac.authorization.k8s.io
kind: Group
name: system:anonymous
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sDisallowAnonymous
metadata:
name: no-anonymous
spec:
match:
kinds:
- apiGroups: ["rbac.authorization.k8s.io"]
kinds: ["ClusterRoleBinding"]
- apiGroups: ["rbac.authorization.k8s.io"]
kinds: ["RoleBinding"]
parameters:
disallowAuthenticated: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: cluster-role-binding-2
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-role-2
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: Group
name: system:authenticated
35 changes: 35 additions & 0 deletions artifacthub/library/general/disallowanonymous/1.1.0/suite.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
kind: Suite
apiVersion: test.gatekeeper.sh/v1alpha1
metadata:
name: disallowanonymous
tests:
- name: disallow-anonymous
template: template.yaml
constraint: samples/no-anonymous-bindings/constraint.yaml
cases:
- name: example-allowed
object: samples/no-anonymous-bindings/example_allowed.yaml
assertions:
- violations: no
- name: example-disallowed
object: samples/no-anonymous-bindings/example_disallowed.yaml
assertions:
- message: "system:unauthenticated"
violations: 1
- message: "system:anonymous"
violations: 1
- message: "system:authenticated"
violations: 0
- name: disallow-authenticated
template: template.yaml
constraint: samples/no-authenticated/constraint.yaml
cases:
- name: authenticated-disallowed-with-parameter-true
object: samples/no-anonymous-bindings/example_disallowed.yaml
assertions:
- message: "system:unauthenticated"
violations: 1
- message: "system:anonymous"
violations: 1
- message: "system:authenticated"
violations: 1
68 changes: 68 additions & 0 deletions artifacthub/library/general/disallowanonymous/1.1.0/template.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8sdisallowanonymous
annotations:
metadata.gatekeeper.sh/title: "Disallow Anonymous Access"
metadata.gatekeeper.sh/version: 1.1.0
description: Disallows associating ClusterRole and Role resources to the system:anonymous user and system:unauthenticated group.
spec:
crd:
spec:
names:
kind: K8sDisallowAnonymous
validation:
# Schema for the `parameters` field
openAPIV3Schema:
type: object
properties:
allowedRoles:
description: >-
The list of ClusterRoles and Roles that may be associated
with the `system:unauthenticated` group and `system:anonymous`
user.
type: array
items:
type: string
disallowAuthenticated:
description: >-
A boolean indicating whether `system:authenticated` should also
be disallowed by this policy.
type: boolean
default: false
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8sdisallowanonymous
violation[{"msg": msg}] {
not is_allowed(input.review.object.roleRef, object.get(input, ["parameters", "allowedRoles"], []))
group := ["system:unauthenticated", "system:anonymous"][_]
subject_is(input.review.object.subjects[_], group)
msg := message(group)
}
violation[{"msg": msg}] {
not is_allowed(input.review.object.roleRef, object.get(input, ["parameters", "allowedRoles"], []))
object.get(input, ["parameters", "disallowAuthenticated"], false)
group := "system:authenticated"
subject_is(input.review.object.subjects[_], group)
msg := message(group)
}
is_allowed(role, allowedRoles) {
role.name == allowedRoles[_]
}
subject_is(subject, expected) {
subject.name == expected
}
message(name) := val {
val := sprintf("%v is not allowed as a subject name in %v %v", [name, input.review.object.kind, input.review.object.metadata.name])
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@ subjects:
- apiGroup: rbac.authorization.k8s.io
kind: Group
name: system:unauthenticated
- apiGroup: rbac.authorization.k8s.io
kind: Group
name: system:anonymous
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sDisallowAnonymous
metadata:
name: no-anonymous
spec:
match:
kinds:
- apiGroups: ["rbac.authorization.k8s.io"]
kinds: ["ClusterRoleBinding"]
- apiGroups: ["rbac.authorization.k8s.io"]
kinds: ["RoleBinding"]
parameters:
disallowAuthenticated: true
20 changes: 19 additions & 1 deletion library/general/disallowanonymous/suite.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,22 @@ tests:
- name: example-disallowed
object: samples/no-anonymous-bindings/example_disallowed.yaml
assertions:
- violations: yes
- message: "system:unauthenticated"
violations: 1
- message: "system:anonymous"
violations: 1
- message: "system:authenticated"
violations: 0
- name: disallow-authenticated
template: template.yaml
constraint: samples/no-authenticated/constraint.yaml
cases:
- name: authenticated-disallowed-with-parameter-true
object: samples/no-anonymous-bindings/example_disallowed.yaml
assertions:
- message: "system:unauthenticated"
violations: 1
- message: "system:anonymous"
violations: 1
- message: "system:authenticated"
violations: 1
34 changes: 27 additions & 7 deletions library/general/disallowanonymous/template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ metadata:
name: k8sdisallowanonymous
annotations:
metadata.gatekeeper.sh/title: "Disallow Anonymous Access"
metadata.gatekeeper.sh/version: 1.0.1
metadata.gatekeeper.sh/version: 1.1.0
description: Disallows associating ClusterRole and Role resources to the system:anonymous user and system:unauthenticated group.
spec:
crd:
Expand All @@ -24,25 +24,45 @@ spec:
type: array
items:
type: string
disallowAuthenticated:
description: >-
A boolean indicating whether `system:authenticated` should also
be disallowed by this policy.
type: boolean
default: false
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8sdisallowanonymous
violation[{"msg": msg}] {
not is_allowed(input.review.object.roleRef, object.get(input, ["parameters", "allowedRoles"], []))
review(input.review.object.subjects[_])
msg := sprintf("Unauthenticated user reference is not allowed in %v %v ", [input.review.object.kind, input.review.object.metadata.name])
group := ["system:unauthenticated", "system:anonymous"][_]
subject_is(input.review.object.subjects[_], group)
msg := message(group)
}
violation[{"msg": msg}] {
not is_allowed(input.review.object.roleRef, object.get(input, ["parameters", "allowedRoles"], []))
object.get(input, ["parameters", "disallowAuthenticated"], false)
group := "system:authenticated"
subject_is(input.review.object.subjects[_], group)
msg := message(group)
}
is_allowed(role, allowedRoles) {
role.name == allowedRoles[_]
}
review(subject) = true {
subject.name == "system:unauthenticated"
subject_is(subject, expected) {
subject.name == expected
}
review(subject) = true {
subject.name == "system:anonymous"
message(name) := val {
val := sprintf("%v is not allowed as a subject name in %v %v", [name, input.review.object.kind, input.review.object.metadata.name])
}
8 changes: 7 additions & 1 deletion src/general/disallowanonymous/constraint.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ metadata:
name: k8sdisallowanonymous
annotations:
metadata.gatekeeper.sh/title: "Disallow Anonymous Access"
metadata.gatekeeper.sh/version: 1.0.1
metadata.gatekeeper.sh/version: 1.1.0
description: Disallows associating ClusterRole and Role resources to the system:anonymous user and system:unauthenticated group.
spec:
crd:
Expand All @@ -24,6 +24,12 @@ spec:
type: array
items:
type: string
disallowAuthenticated:
description: >-
A boolean indicating whether `system:authenticated` should also
be disallowed by this policy.
type: boolean
default: false
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
Expand Down
26 changes: 20 additions & 6 deletions src/general/disallowanonymous/src.rego
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,32 @@ package k8sdisallowanonymous

violation[{"msg": msg}] {
not is_allowed(input.review.object.roleRef, object.get(input, ["parameters", "allowedRoles"], []))
review(input.review.object.subjects[_])
msg := sprintf("Unauthenticated user reference is not allowed in %v %v ", [input.review.object.kind, input.review.object.metadata.name])

group := ["system:unauthenticated", "system:anonymous"][_]
subject_is(input.review.object.subjects[_], group)

msg := message(group)
}

violation[{"msg": msg}] {
not is_allowed(input.review.object.roleRef, object.get(input, ["parameters", "allowedRoles"], []))

object.get(input, ["parameters", "disallowAuthenticated"], false)

group := "system:authenticated"
subject_is(input.review.object.subjects[_], group)

msg := message(group)
}

is_allowed(role, allowedRoles) {
role.name == allowedRoles[_]
}

review(subject) = true {
subject.name == "system:unauthenticated"
subject_is(subject, expected) {
subject.name == expected
}

review(subject) = true {
subject.name == "system:anonymous"
message(name) := val {
val := sprintf("%v is not allowed as a subject name in %v %v", [name, input.review.object.kind, input.review.object.metadata.name])
}
Loading

0 comments on commit 5153330

Please sign in to comment.