Skip to content

Commit

Permalink
fix(k8spsphostnetworkingports): CEL fixes for hostNetwork variable and
Browse files Browse the repository at this point in the history
message

My updates to the suite.yaml file yielded an expected failure due to an
incorrect CEL expression:

```
        unexpected number of violations: got 1 violations but want none: got messages [failed expression: (has(request.operation) && request.operation == "UPDATE") ||
(!has(variables.params.hostNetwork) || !variables.params.hostNetwork ? (has(variables.anyObject.spec.hostNetwork) && !variables.anyObject.spec.hostNetwork) : true)]
```

By contrast, a run of `gator verify -v .
--enable-k8s-native-validation=false` yielded a fully passing
suite.yaml.

This expression was actually failing due to its `messageExpression`, as
non-primitive types cannot be combined with strings as in some
interpreted languages (like rego).  Unfortunately the compiler does not
indicate that the messageExpression is the source of the problem.

Once the message was fixed, I resolved the incorrect violation
expression to fix the bug in the handling of params.hostNetwork.

Signed-off-by: juliankatz <[email protected]>
  • Loading branch information
julianKatz committed Aug 30, 2024
1 parent 888da7b commit 7266367
Show file tree
Hide file tree
Showing 25 changed files with 561 additions and 50 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
version: 1.1.3
name: k8spsphostnetworkingports
displayName: Host Networking Ports
createdAt: "2024-08-29T21:28:18Z"
description: Controls usage of host network namespace by pod containers. HostNetwork verification happens without exception for exemptImages. Specific ports must be specified. Corresponds to the `hostNetwork` and `hostPorts` fields in a PodSecurityPolicy. For more information, see https://kubernetes.io/docs/concepts/policy/pod-security-policy/#host-namespaces
digest: 751752950daeb4002a10cad6cbeba6a4afe03b98605f32885ae3fe0179eaff67
license: Apache-2.0
homeURL: https://open-policy-agent.github.io/gatekeeper-library/website/host-network-ports
keywords:
- gatekeeper
- open-policy-agent
- policies
readme: |-
# Host Networking Ports
Controls usage of host network namespace by pod containers. HostNetwork verification happens without exception for exemptImages. Specific ports must be specified. Corresponds to the `hostNetwork` and `hostPorts` fields in a PodSecurityPolicy. For more information, see https://kubernetes.io/docs/concepts/policy/pod-security-policy/#host-namespaces
install: |-
### Usage
```shell
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/artifacthub/library/pod-security-policy/host-network-ports/1.1.3/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
Expand Up @@ -9,5 +9,3 @@ spec:
kinds: ["Pod"]
parameters:
hostNetwork: false
exemptImages:
- "nginx"
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPHostNetworkingPorts
metadata:
name: psp-host-network-ports
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
parameters:
hostNetwork: false
min: 80
max: 9000
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPHostNetworkingPorts
metadata:
name: psp-host-network-ports
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
parameters:
hostNetwork: true
min: 80
max: 9000
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
apiVersion: v1
kind: Pod
metadata:
name: nginx-host-networking-ports-disallowed
labels:
app: nginx-host-networking-ports
spec:
hostNetwork: true
ephemeralContainers:
- name: nginx
image: nginx
ports:
- containerPort: 9001
hostPort: 9001
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ metadata:
labels:
app: nginx-host-networking-ports
spec:
hostNetwork: false
containers:
- name: nginx
image: nginx
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: v1
kind: Pod
metadata:
name: nginx-host-networking-ports-disallowed
labels:
app: nginx-host-networking-ports
spec:
hostNetwork: true
containers:
- name: nginx
image: nginx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
apiVersion: v1
kind: Pod
metadata:
name: nginx-host-network-false
spec:
hostNetwork: false
containers:
- name: nginx
image: nginx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
apiVersion: v1
kind: Pod
metadata:
name: nginx-host-network-true
spec:
hostNetwork: true
containers:
- name: nginx
image: nginx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
kind: AdmissionReview
apiVersion: admission.k8s.io/v1beta1
request:
operation: "UPDATE"
object:
apiVersion: v1
kind: Pod
metadata:
name: nginx-host-networking-ports-disallowed
labels:
app: nginx-host-networking-ports
spec:
hostNetwork: true
containers:
- name: nginx
image: nginx
ports:
- containerPort: 9001
hostPort: 9001
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
kind: Suite
apiVersion: test.gatekeeper.sh/v1alpha1
metadata:
name: host-network-ports
tests:
- name: port-range-with-host-network-allowed
template: template.yaml
constraint: samples/psp-host-network-ports/constraint.yaml
cases:
- name: out-of-range
object: samples/port_range_block_host_network/example_disallowed_out_of_range_host_network_true.yaml
assertions:
- violations: yes
- name: example-allowed
object: samples/psp-host-network-ports/example_allowed_in_range.yaml
assertions:
- violations: no
- name: out-of-range-ephemeral
object: samples/psp-host-network-ports/disallowed_ephemeral.yaml
assertions:
- violations: yes
- name: update
object: samples/psp-host-network-ports/update.yaml
assertions:
- violations: no
- name: no-ports-specified
object: samples/psp-host-network-ports/example_allowed_no_ports.yaml
assertions:
- violations: no
- name: host-network-forbidden
template: template.yaml
constraint: samples/block_host_network/constraint.yaml
cases:
- name: hostnetwork-true
object: samples/psp-host-network-ports/example_allowed_no_ports_host_network_true.yaml
assertions:
- violations: yes
- name: hostnetwork-false
object: samples/psp-host-network-ports/example_allowed_no_ports_host_network_false.yaml
assertions:
- violations: no
- name: port-range-with-host-network-forbidden
template: template.yaml
constraint: samples/port_range_block_host_network/constraint.yaml
cases:
- name: out-of-range-and-host-network-true
object: samples/port_range_block_host_network/example_disallowed_out_of_range_host_network_true.yaml
assertions:
- violations: yes
- name: in-range-host-network-false
object: samples/psp-host-network-ports/example_allowed_in_range.yaml
assertions:
- violations: no
- name: disallowed-ephemeral
object: samples/psp-host-network-ports/disallowed_ephemeral.yaml
assertions:
- violations: yes
- name: update
object: samples/psp-host-network-ports/update.yaml
assertions:
- violations: no

Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8spsphostnetworkingports
annotations:
metadata.gatekeeper.sh/title: "Host Networking Ports"
metadata.gatekeeper.sh/version: 1.1.3
description: >-
Controls usage of host network namespace by pod containers. HostNetwork verification happens without exception for exemptImages. Specific
ports must be specified. Corresponds to the `hostNetwork` and
`hostPorts` fields in a PodSecurityPolicy. For more information, see
https://kubernetes.io/docs/concepts/policy/pod-security-policy/#host-namespaces
spec:
crd:
spec:
names:
kind: K8sPSPHostNetworkingPorts
validation:
# Schema for the `parameters` field
openAPIV3Schema:
type: object
description: >-
Controls usage of host network namespace by pod containers. HostNetwork verification happens without exception for exemptImages. Specific
ports must be specified. Corresponds to the `hostNetwork` and
`hostPorts` fields in a PodSecurityPolicy. For more information, see
https://kubernetes.io/docs/concepts/policy/pod-security-policy/#host-namespaces
properties:
exemptImages:
description: >-
Any container that uses an image that matches an entry in this list will be excluded
from enforcement. Prefix-matching can be signified with `*`. For example: `my-image-*`.
It is recommended that users use the fully-qualified Docker image name (e.g. start with a domain name)
in order to avoid unexpectedly exempting images from an untrusted repository.
type: array
items:
type: string
hostNetwork:
description: "Determines if the policy allows the use of HostNetwork in the pod spec."
type: boolean
min:
description: "The start of the allowed port range, inclusive."
type: integer
max:
description: "The end of the allowed port range, inclusive."
type: integer
targets:
- target: admission.k8s.gatekeeper.sh
code:
- engine: K8sNativeValidation
source:
variables:
- name: containers
expression: 'has(variables.anyObject.spec.containers) ? variables.anyObject.spec.containers : []'
- name: initContainers
expression: 'has(variables.anyObject.spec.initContainers) ? variables.anyObject.spec.initContainers : []'
- name: ephemeralContainers
expression: 'has(variables.anyObject.spec.ephemeralContainers) ? variables.anyObject.spec.ephemeralContainers : []'
- name: exemptImagePrefixes
expression: |
!has(variables.params.exemptImages) ? [] :
variables.params.exemptImages.filter(image, image.endsWith("*")).map(image, string(image).replace("*", ""))
- name: exemptImageExplicit
expression: |
!has(variables.params.exemptImages) ? [] :
variables.params.exemptImages.filter(image, !image.endsWith("*"))
- name: exemptImages
expression: |
(variables.containers + variables.initContainers + variables.ephemeralContainers).filter(container,
container.image in variables.exemptImageExplicit ||
variables.exemptImagePrefixes.exists(exemption, string(container.image).startsWith(exemption)))
- name: badContainers
expression: |
(variables.containers + variables.initContainers + variables.ephemeralContainers).filter(container,
!(container.image in variables.exemptImages) && has(container.ports) &&
(
(container.ports.all(port, has(port.hostPort) && has(variables.params.min) && port.hostPort < variables.params.min)) ||
(container.ports.all(port, has(port.hostPort) && has(variables.params.max) && port.hostPort > variables.params.max))
)
)
- name: isUpdate
expression: has(request.operation) && request.operation == "UPDATE"
- name: hostNetworkAllowed
expression: has(variables.params.hostNetwork) && variables.params.hostNetwork
- name: hostNetworkEnabled
expression: has(variables.anyObject.spec.hostNetwork) && variables.anyObject.spec.hostNetwork
- name: hostNetworkViolation
expression: variables.hostNetworkEnabled && !variables.hostNetworkAllowed
validations:
- expression: 'variables.isUpdate || size(variables.badContainers) == 0'
messageExpression: '"The specified hostNetwork and hostPort are not allowed, pod: " + variables.anyObject.metadata.name'
- expression: variables.isUpdate || !variables.hostNetworkViolation
messageExpression: '"The specified hostNetwork and hostPort are not allowed, pod: " + variables.anyObject.metadata.name'
- engine: Rego
source:
rego: |
package k8spsphostnetworkingports
import data.lib.exclude_update.is_update
import data.lib.exempt_container.is_exempt
violation[{"msg": msg, "details": {}}] {
# spec.hostNetwork field is immutable.
not is_update(input.review)
input_share_hostnetwork(input.review.object)
msg := sprintf("The specified hostNetwork and hostPort are not allowed, pod: %v. Allowed values: %v", [input.review.object.metadata.name, input.parameters])
}
input_share_hostnetwork(o) {
not input.parameters.hostNetwork
o.spec.hostNetwork
}
input_share_hostnetwork(_) {
hostPort := input_containers[_].ports[_].hostPort
hostPort < input.parameters.min
}
input_share_hostnetwork(_) {
hostPort := input_containers[_].ports[_].hostPort
hostPort > input.parameters.max
}
input_containers[c] {
c := input.review.object.spec.containers[_]
not is_exempt(c)
}
input_containers[c] {
c := input.review.object.spec.initContainers[_]
not is_exempt(c)
}
input_containers[c] {
c := input.review.object.spec.ephemeralContainers[_]
not is_exempt(c)
}
libs:
- |
package lib.exclude_update
is_update(review) {
review.operation == "UPDATE"
}
- |
package lib.exempt_container
is_exempt(container) {
exempt_images := object.get(object.get(input, "parameters", {}), "exemptImages", [])
img := container.image
exemption := exempt_images[_]
_matches_exemption(img, exemption)
}
_matches_exemption(img, exemption) {
not endswith(exemption, "*")
exemption == img
}
_matches_exemption(img, exemption) {
endswith(exemption, "*")
prefix := trim_suffix(exemption, "*")
startswith(img, prefix)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPHostNetworkingPorts
metadata:
name: psp-host-network
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
parameters:
hostNetwork: false
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPHostNetworkingPorts
metadata:
name: psp-host-network-ports
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
parameters:
hostNetwork: false
min: 80
max: 9000
Loading

0 comments on commit 7266367

Please sign in to comment.