-
Notifications
You must be signed in to change notification settings - Fork 50
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add rego rule to check for windows securityContext compliance #318
Conversation
Signed-off-by: Alessio Greggi <[email protected]>
Signed-off-by: Alessio Greggi <[email protected]>
Signed-off-by: Alessio Greggi <[email protected]>
Hello @0xquark, thanks for the fast contribution you gave. |
Hey @0xquark, is the PR still a Work In Progress? |
Hi @alegrey91 ! Edit : SSH'ed my way into commiting |
@alegrey91 This is ready for review now! 🚀 |
Signed-off-by: Karanjot Singh <[email protected]> Added set-gmsacredentialspec-value rule as part of windowssecuritycontext rules Signed-off-by: Karanjot Singh <[email protected]> Added set-gmsacredentialspecname-value rule as part of windowssecuritycontext rules Signed-off-by: Karanjot Singh <[email protected]> Added set-hostprocess-true rule as part of windowssecuritycontext rules Signed-off-by: Karanjot Singh <[email protected]> Added runAsUserName rule as part of windowssecuritycontext rules
obj := input[_] | ||
allowed_kinds := {"Pod", "Deployment", "ReplicaSet", "DaemonSet", "StatefulSet", "Job", "CronJob"} | ||
allowed_kinds[obj.kind] | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add OS check using the label kubernetes.io/os: "windows"
located in Node
object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added a check for OS but still while testing it takes the input as other than windows and returning {} as result. Could you check where i am going wrong?
rules/runAsUserName/raw.rego
Outdated
} else := false | ||
|
||
# Function to get the security context of an object | ||
getSecurityContext(obj) = obj.spec.securityContext { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Despite the function seems to work properly, we should keep the format used in other policies.
So, usually we split the policy in 3 rules (Cronjob
, Pod
and Workload
, where Workload
includes Deployment
, ReplicaSet
, DaemonSet
, StatefulSet
).
Here's an example: https://github.com/kubescape/regolibrary/blob/master/rules/set-seLinuxOptions/raw.rego
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I couldn't understand why we have to split into 3 different rules, isn't having a single function to deal with cronjob,pod and workload a much efficient way ?
rules/runAsUserName/raw.rego
Outdated
runAsUserNameSet(securityContext) := true { | ||
securityContext != null | ||
securityContext.windowsOptions != null | ||
securityContext.windowsOptions.runAsUserName != null |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should check both for PodSecurityContext
and SecurityContext
. What I mean is that windowsOptions
can be found both in these places, so you should check in both of them. You can find an example here: https://github.com/kubescape/regolibrary/blob/dev/rules/set-seLinuxOptions/raw.rego.
seLinuxOptions
can be found in both the paths. So, the path
variable that we are initializing, should have the right path according to which object has been checked.
Additionally, the check here, can be simplified using the following form:
runAsUserNameSet(securityContext) := true {
securityContext.windowsOptions.runAsUserName != ""
} else := false
obj := input[_] | ||
allowed_kinds := {"Pod", "Deployment", "ReplicaSet", "DaemonSet", "StatefulSet", "Job", "CronJob"} | ||
allowed_kinds[obj.kind] | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add OS check using the label kubernetes.io/os: "windows"
located in Node
object.
} | ||
|
||
# Function to check if the GMSA credential spec is set in the security context | ||
gmsaCredentialSpecSet(securityContext) := true if { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should check both for PodSecurityContext
and SecurityContext
. What I mean is that windowsOptions
can be found both in these places, so you should check in both of them. You can find an example here: https://github.com/kubescape/regolibrary/blob/dev/rules/set-seLinuxOptions/raw.rego.
seLinuxOptions
can be found in both the paths. So, the path
variable that we are initializing, should have the right path according to which object has been checked.
Additionally, the check here, can be simplified using the following form:
gmsaCredentialSpecSet(securityContext) := true {
securityContext.windowsOptions.gmsaCredentialSpec != ""
} else := false
obj := input[_] | ||
allowed_kinds := {"Pod", "Deployment", "ReplicaSet", "DaemonSet", "StatefulSet", "Job", "CronJob"} | ||
allowed_kinds[obj.kind] | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add OS check using the label kubernetes.io/os: "windows"
located in Node
object.
} | ||
|
||
# Function to check if the GMSA credential spec name is set in the security context | ||
gmsaCredentialSpecNameSet(securityContext) := true if { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should check both for PodSecurityContext
and SecurityContext
. What I mean is that windowsOptions
can be found both in these places, so you should check in both of them. You can find an example here: https://github.com/kubescape/regolibrary/blob/dev/rules/set-seLinuxOptions/raw.rego.
seLinuxOptions
can be found in both the paths. So, the path variable that we are initializing, should have the right path according to which object has been checked.
Additionally, the check here, can be simplified using the following form:
gmsaCredentialSpecNameSet(securityContext) := true {
securityContext.windowsOptions.gmsaCredentialSpecName != ""
} else := false
} else := false | ||
|
||
# Function to get the security context of an object | ||
getSecurityContext(obj) = obj.spec.securityContext { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Despite the function seems to work properly, we should keep the format used in other policies.
So, usually we split the policy in 3 rules (Cronjob
, Pod
and Workload
, where Workload
includes Deployment
, ReplicaSet
, DaemonSet
, StatefulSet
).
Here's an example: https://github.com/kubescape/regolibrary/blob/master/rules/set-seLinuxOptions/raw.rego
obj := input[_] | ||
allowed_kinds := {"Pod", "Deployment", "ReplicaSet", "DaemonSet", "StatefulSet", "Job", "CronJob"} | ||
allowed_kinds[obj.kind] | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add OS check using the label kubernetes.io/os: "windows"
located in Node
object.
|
||
# Function to check if container is set as a 'Host Process' container | ||
isHostProcessSet(obj) := true if { | ||
obj.spec.hostProcess == true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should check both for PodSecurityContext
and SecurityContext
. What I mean is that windowsOptions
can be found both in these places, so you should check in both of them. You can find an example here: https://github.com/kubescape/regolibrary/blob/dev/rules/set-seLinuxOptions/raw.rego.
seLinuxOptions
can be found in both the paths. So, the path variable that we are initializing, should have the right path according to which object has been checked.
@0xquark first of all, I would say thanks for the effort that you gave in the contribution. |
Additionally, there are some file out of scope this PR. Probably due to some mistake with branches. We can fix them one the PR is ready to be merged. |
@alegrey91 Thank you for taking your time to review! I'll make the changes but i do have few questions regarding the implementation, i've replied to the review regarding this. |
Signed-off-by: Karanjot Singh <[email protected]>
hey @0xquark, are you still working on that? |
@alegrey91 Left a couple of questions on the review above! |
Do you mean this: #318 (comment)? |
Yess! #318 (comment) This too |
Ok, so I already replied to these. Can you see my reply? |
@alegrey91 I am not able to view it! |
This should be for compatibility reasons. It is hard to maintain the same code written in different ways. Keep in mind that you have also to check for |
Try changing the check with this: system_info := input[_]
system_info.kind == "Node"
system_info.metadata.labels.kubernetes.io/os == "windows" And passing a Node resource as input. apiVersion: v1
items:
- apiVersion: v1
kind: Node
metadata:
annotations:
kubeadm.alpha.kubernetes.io/cri-socket: unix:///run/containerd/containerd.sock
node.alpha.kubernetes.io/ttl: "0"
volumes.kubernetes.io/controller-managed-attach-detach: "true"
creationTimestamp: "2023-03-13T20:37:33Z"
labels:
beta.kubernetes.io/arch: amd64
beta.kubernetes.io/os: linux
kubernetes.io/arch: amd64
kubernetes.io/hostname: test-control-plane
kubernetes.io/os: linux
node-role.kubernetes.io/control-plane: ""
node.kubernetes.io/exclude-from-external-load-balancers: ""
name: test-control-plane
resourceVersion: "404"
uid: 34f43f52-de94-45ae-ba8d-a43e595cf2d6
spec:
podCIDR: 10.244.0.0/24
podCIDRs:
- 10.244.0.0/24
providerID: kind://docker/test/test-control-plane
status:
addresses:
- address: 172.18.0.2
type: InternalIP
- address: test-control-plane
type: Hostname
allocatable:
cpu: "8"
ephemeral-storage: 486745Mi
hugepages-1Gi: "0"
hugepages-2Mi: "0"
memory: 16255180Ki
pods: "110"
capacity:
cpu: "8"
ephemeral-storage: 486745Mi
hugepages-1Gi: "0"
hugepages-2Mi: "0"
memory: 16255180Ki
pods: "110"
conditions:
- lastHeartbeatTime: "2023-03-13T20:37:57Z"
lastTransitionTime: "2023-03-13T20:37:31Z"
message: kubelet has sufficient memory available
reason: KubeletHasSufficientMemory
status: "False"
type: MemoryPressure
- lastHeartbeatTime: "2023-03-13T20:37:57Z"
lastTransitionTime: "2023-03-13T20:37:31Z"
message: kubelet has no disk pressure
reason: KubeletHasNoDiskPressure
status: "False"
type: DiskPressure
- lastHeartbeatTime: "2023-03-13T20:37:57Z"
lastTransitionTime: "2023-03-13T20:37:31Z"
message: kubelet has sufficient PID available
reason: KubeletHasSufficientPID
status: "False"
type: PIDPressure
- lastHeartbeatTime: "2023-03-13T20:37:57Z"
lastTransitionTime: "2023-03-13T20:37:57Z"
message: kubelet is posting ready status
reason: KubeletReady
status: "True"
type: Ready
daemonEndpoints:
kubeletEndpoint:
Port: 10250
images:
- names:
- registry.k8s.io/etcd:3.5.4-0
sizeBytes: 102157811
- names:
- docker.io/library/import-2022-10-25@sha256:4002c19dafb94b1995fc598fae590f70cac10135f61ca2551bd97aae37ed9c4a
- registry.k8s.io/kube-apiserver:v1.25.3
sizeBytes: 76530158
- names:
- docker.io/library/import-2022-10-25@sha256:1c35781a4b6011d5c27bedbba7ca130db72c4aaf74d108c60bc77ae49130e5e4
- registry.k8s.io/kube-controller-manager:v1.25.3
sizeBytes: 64499836
- names:
- docker.io/library/import-2022-10-25@sha256:0dae4b69c2aa90e6c24691ebbe2e860e2a1ae68463a622c627fb58110153d950
- registry.k8s.io/kube-proxy:v1.25.3
sizeBytes: 63275005
- names:
- docker.io/library/import-2022-10-25@sha256:409b0e81d9aecf59df96df445a3171f43e2ae834ef6c9e77b1492c4d19bfd78d
- registry.k8s.io/kube-scheduler:v1.25.3
sizeBytes: 51921020
- names:
- docker.io/kindest/kindnetd:v20221004-44d545d1
sizeBytes: 25830582
- names:
- docker.io/kindest/local-path-provisioner:v0.0.22-kind.0
sizeBytes: 17375346
- names:
- registry.k8s.io/coredns/coredns:v1.9.3
sizeBytes: 14837849
- names:
- docker.io/kindest/local-path-helper:v20220607-9a4d8d2a
sizeBytes: 2859509
- names:
- registry.k8s.io/pause:3.7
sizeBytes: 311278
nodeInfo:
architecture: amd64
bootID: bdef9f66-f199-4ef9-ba92-3a9902aaebe2
containerRuntimeVersion: containerd://1.6.9
kernelVersion: 6.1.11-200.fc37.x86_64
kubeProxyVersion: v1.25.3
kubeletVersion: v1.25.3
machineID: 4a57c491734b40f4a2f006225217d80f
operatingSystem: linux
osImage: Ubuntu 22.04.1 LTS
systemUUID: 294b1e5f-78d3-44a2-8161-c73979c56e25
kind: List
metadata:
resourceVersion: "" |
@0xquark sorry, I can't understand why my comments where hidden. |
Sure! I'll update the code |
Hi @0xquark, any news? |
Sorry for the delay! @alegrey91. I've implemented the changes for the first two rules. I had been busy with working over the port scanner, I'll push the code with other two rules by this weekend. |
Great! Thanks for the update! |
While using this, i am running into this error "rego_unsafe_var_error: var os is unsafe" |
Signed-off-by: Karanjot Singh <[email protected]>
Try this way:
|
Signed-off-by: Karanjot Singh [email protected]
Fixes: #317
Summary
In order to support Windows system, we should add rego rules to check for securityContext parameters also for this OS.
kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#windowssecuritycontextoptions-v1-core
So the following rules are being added:
set-gmsacredentialspec-value
This rule will check if the GMSA credential spec is set in the security context
The gmsaCredentialSpecSet function checks if the securityContext object exists and if the windowsOptions object exists within it. It then checks if the gmsaCredentialSpec field is set within windowsOptions. If all of these conditions are true, the rule will not fail. Otherwise, the rule will fail and return an alert message containing the name of the object that failed the check.
Test Cases
set-gmsacredentialspecname-value
This rule will check if the GMSA credential spec name is set in the security context
Test Cases
set-hostprocess-true
Check if any container in the pod is set to run as a 'Host Process' container or if WindowsHostProcessContainers feature flag is enabled in api-server when HostProcess is true
Test Cases
runAsUserName
checks if the runAsUserName is set in the security context
Test Cases