diff --git a/internal/services/engines/kubernetes/rules.go b/internal/services/engines/kubernetes/rules.go index b75914016..20f8ce114 100644 --- a/internal/services/engines/kubernetes/rules.go +++ b/internal/services/engines/kubernetes/rules.go @@ -26,11 +26,13 @@ import ( func NewAllowPrivilegeEscalation() *text.Rule { return &text.Rule{ Metadata: engine.Metadata{ - ID: "HS-KUBERNETES-1", - Name: "Allow Privilege Escalation", - Description: "Privileged containers share namespaces with the host system, eschew cgroup restrictions, and do not offer any security. They should be used exclusively as a bundling and distribution mechanism for the code in the container, and not for isolation.", - Severity: severities.Medium.ToString(), - Confidence: confidence.High.ToString(), + ID: "HS-KUBERNETES-1", + Name: "Allow Privilege Escalation", + Description: "Privileged containers share namespaces with the host system, eschew cgroup restrictions, and do not offer any security. They should be used exclusively as a bundling and distribution mechanism for the code in the container, and not for isolation.", + Severity: severities.Medium.ToString(), + Confidence: confidence.High.ToString(), + SafeExample: SampleSafeHSKUBERNETES1, + UnsafeExample: SampleVulnerableHSKUBERNETES1, }, Type: text.AndMatch, Expressions: []*regexp.Regexp{ @@ -44,11 +46,13 @@ func NewAllowPrivilegeEscalation() *text.Rule { func NewHostAliases() *text.Rule { return &text.Rule{ Metadata: engine.Metadata{ - ID: "HS-KUBERNETES-2", - Name: "Host Aliases", - Description: "Managing /etc/hosts aliases can prevent the container from modifying the file after a pod's containers have already been started. DNS should be managed by the orchestrator.", - Severity: severities.Low.ToString(), - Confidence: confidence.Medium.ToString(), + ID: "HS-KUBERNETES-2", + Name: "Host Aliases", + Description: "Managing /etc/hosts aliases can prevent the container from modifying the file after a pod's containers have already been started. DNS should be managed by the orchestrator.", + Severity: severities.Low.ToString(), + Confidence: confidence.Medium.ToString(), + SafeExample: SampleSafeHSKUBERNETES2, + UnsafeExample: SampleVulnerableHSKUBERNETES2, }, Type: text.AndMatch, Expressions: []*regexp.Regexp{ @@ -62,11 +66,13 @@ func NewHostAliases() *text.Rule { func NewDockerSock() *text.Rule { return &text.Rule{ Metadata: engine.Metadata{ - ID: "HS-KUBERNETES-3", - Name: "Docker Sock", - Description: "Mounting the docker.socket leaks information about other containers and can allow container breakout.", - Severity: severities.Medium.ToString(), - Confidence: confidence.High.ToString(), + ID: "HS-KUBERNETES-3", + Name: "Docker Sock", + Description: "Mounting the docker.socket leaks information about other containers and can allow container breakout.", + Severity: severities.Medium.ToString(), + Confidence: confidence.High.ToString(), + SafeExample: SampleSafeHSKUBERNETES3, + UnsafeExample: SampleVulnerableHSKUBERNETES3, }, Type: text.AndMatch, Expressions: []*regexp.Regexp{ @@ -80,11 +86,13 @@ func NewDockerSock() *text.Rule { func NewCapabilitySystemAdmin() *text.Rule { return &text.Rule{ Metadata: engine.Metadata{ - ID: "HS-KUBERNETES-4", - Name: "Capability System Admin", - Description: "CAP_SYS_ADMIN is the most privileged capability and should always be avoided.", - Severity: severities.Critical.ToString(), - Confidence: confidence.High.ToString(), + ID: "HS-KUBERNETES-4", + Name: "Capability System Admin", + Description: "CAP_SYS_ADMIN is the most privileged capability and should always be avoided.", + Severity: severities.Critical.ToString(), + Confidence: confidence.High.ToString(), + SafeExample: SampleSafeHSKUBERNETES4, + UnsafeExample: SampleVulnerableHSKUBERNETES4, }, Type: text.AndMatch, Expressions: []*regexp.Regexp{ @@ -100,11 +108,13 @@ func NewCapabilitySystemAdmin() *text.Rule { func NewPrivilegedContainer() *text.Rule { return &text.Rule{ Metadata: engine.Metadata{ - ID: "HS-KUBERNETES-5", - Name: "Privileged Container", - Description: "Privileged containers can allow almost completely unrestricted host access.", - Severity: severities.High.ToString(), - Confidence: confidence.Medium.ToString(), + ID: "HS-KUBERNETES-5", + Name: "Privileged Container", + Description: "Privileged containers can allow almost completely unrestricted host access.", + Severity: severities.High.ToString(), + Confidence: confidence.Medium.ToString(), + SafeExample: SampleSafeHSKUBERNETES5, + UnsafeExample: SampleVulnerableHSKUBERNETES5, }, Type: text.AndMatch, Expressions: []*regexp.Regexp{ @@ -118,11 +128,13 @@ func NewPrivilegedContainer() *text.Rule { func NewSeccompUnconfined() *text.Rule { return &text.Rule{ Metadata: engine.Metadata{ - ID: "HS-KUBERNETES-6", - Name: "Seccomp Unconfined", - Description: "Unconfined Seccomp profiles have full system call access.", - Severity: severities.Low.ToString(), - Confidence: confidence.Low.ToString(), + ID: "HS-KUBERNETES-6", + Name: "Seccomp Unconfined", + Description: "Unconfined Seccomp profiles have full system call access.", + Severity: severities.Low.ToString(), + Confidence: confidence.Low.ToString(), + SafeExample: SampleSafeHSKUBERNETES6, + UnsafeExample: SampleVulnerableHSKUBERNETES6, }, Type: text.OrMatch, Expressions: []*regexp.Regexp{ @@ -135,11 +147,13 @@ func NewSeccompUnconfined() *text.Rule { func NewHostIPC() *text.Rule { return &text.Rule{ Metadata: engine.Metadata{ - ID: "HS-KUBERNETES-7", - Name: "Host IPC", - Description: "Sharing the host's IPC namespace allows container processes to communicate with processes on the host.", - Severity: severities.Medium.ToString(), - Confidence: confidence.Low.ToString(), + ID: "HS-KUBERNETES-7", + Name: "Host IPC", + Description: "Sharing the host's IPC namespace allows container processes to communicate with processes on the host.", + Severity: severities.Medium.ToString(), + Confidence: confidence.Low.ToString(), + SafeExample: SampleSafeHSKUBERNETES7, + UnsafeExample: SampleVulnerableHSKUBERNETES7, }, Type: text.OrMatch, Expressions: []*regexp.Regexp{ @@ -151,11 +165,13 @@ func NewHostIPC() *text.Rule { func NewHostPID() *text.Rule { return &text.Rule{ Metadata: engine.Metadata{ - ID: "HS-KUBERNETES-8", - Name: "Host PID", - Description: "Sharing the host's PID namespace allows visibility of processes on the host, potentially leaking information such as environment variables and configuration.", - Severity: severities.Medium.ToString(), - Confidence: confidence.Low.ToString(), + ID: "HS-KUBERNETES-8", + Name: "Host PID", + Description: "Sharing the host's PID namespace allows visibility of processes on the host, potentially leaking information such as environment variables and configuration.", + Severity: severities.Medium.ToString(), + Confidence: confidence.Low.ToString(), + SafeExample: SampleSafeHSKUBERNETES8, + UnsafeExample: SampleVulnerableHSKUBERNETES8, }, Type: text.OrMatch, Expressions: []*regexp.Regexp{ @@ -167,11 +183,13 @@ func NewHostPID() *text.Rule { func NewHostNetwork() *text.Rule { return &text.Rule{ Metadata: engine.Metadata{ - ID: "HS-KUBERNETES-9", - Name: "Host Network", - Description: "Sharing the host's network namespace permits processes in the pod to communicate with processes bound to the host's loopback adapter.", - Severity: severities.Medium.ToString(), - Confidence: confidence.Low.ToString(), + ID: "HS-KUBERNETES-9", + Name: "Host Network", + Description: "Sharing the host's network namespace permits processes in the pod to communicate with processes bound to the host's loopback adapter.", + Severity: severities.Medium.ToString(), + Confidence: confidence.Low.ToString(), + SafeExample: SampleSafeHSKUBERNETES9, + UnsafeExample: SampleVulnerableHSKUBERNETES9, }, Type: text.OrMatch, Expressions: []*regexp.Regexp{ diff --git a/internal/services/engines/kubernetes/samples_test.go b/internal/services/engines/kubernetes/samples.go similarity index 98% rename from internal/services/engines/kubernetes/samples_test.go rename to internal/services/engines/kubernetes/samples.go index 156b798b9..6ed81ed55 100644 --- a/internal/services/engines/kubernetes/samples_test.go +++ b/internal/services/engines/kubernetes/samples.go @@ -34,7 +34,30 @@ spec: command: [ "sh", "-c", "sleep 1h" ] volumeMounts: - name: sec-ctx-vol - mountPath: /data/demo` + mountPath: /data/demo +` + + SampleSafeHSKUBERNETES1 = `apiVersion: v1 +kind: Pod +metadata: + name: security-context-demo +spec: + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + fsGroup: 2000 + volumes: + - name: sec-ctx-vol + emptyDir: {} + containers: + - name: sec-ctx-demo + image: busybox + command: [ "sh", "-c", "sleep 1h" ] + volumeMounts: + - name: sec-ctx-vol + mountPath: /data/demo +` + SampleVulnerableHSKUBERNETES2 = ` apiVersion: v1 kind: Pod @@ -59,6 +82,27 @@ spec: args: - "/etc/hosts" ` + SampleSafeHSKUBERNETES2 = ` +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: example-ingress + annotations: + ingress.kubernetes.io/rewrite-target: / +spec: + rules: + - http: + paths: + - path: /foo + backend: + serviceName: foo-service + servicePort: 8000 + - path: /bar + backend: + serviceName: bar-service + servicePort: 8000 +` + SampleVulnerableHSKUBERNETES3 = ` apiVersion: v1 kind: Pod @@ -71,6 +115,19 @@ spec: hostPath: path: /var/run/docker.sock ` + SampleSafeHSKUBERNETES3 = ` +apiVersion: v1 +kind: Pod +metadata: + name: security-best-practice +spec: + containers: + # specification of the pod’s containers + # ... + securityContext: + readOnlyRootFilesystem: true +` + SampleVulnerableHSKUBERNETES4 = ` --- apiVersion: extensions/v1beta1 @@ -86,6 +143,22 @@ kind: Deployment add: # Add sys_admin is broken of security - SYS_ADMIN ` + SampleSafeHSKUBERNETES4 = ` +--- +apiVersion: extensions/v1beta1 +kind: Deployment +... + containers: + - name: payment + image: nginx + securityContext: + capabilities: + drop: # Drop all capabilities from a pod as above + - all + add: # Add only those required + - NET_BIND_SERVICE +` + SampleVulnerableHSKUBERNETES5 = ` apiVersion: v1 kind: Pod @@ -99,6 +172,19 @@ spec: securityContext: privileged: true ` + SampleSafeHSKUBERNETES5 = ` +apiVersion: v1 +kind: Pod +metadata: + name: privileged +spec: + containers: + - name: pause + image: k8s.gcr.io/pause + securityContext: + privileged: false +` + SampleVulnerableHSKUBERNETES6 = `apiVersion: policy/v1beta1 kind: PodSecurityPolicy metadata: @@ -127,19 +213,36 @@ spec: fsGroup: rule: 'RunAsAny' ` - SampleVulnerableHSKUBERNETES7 = `apiVersion: policy/v1beta1 + SampleSafeHSKUBERNETES6 = `apiVersion: policy/v1beta1 kind: PodSecurityPolicy metadata: name: privileged annotations: - seccomp.security.alpha.kubernetes.io/allowedProfileNames: '*' + seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'runtime/default' spec: + privileged: true + allowPrivilegeEscalation: true + allowedCapabilities: + - '*' + volumes: + - '*' + hostNetwork: true hostPorts: - min: 0 max: 65535 hostIPC: true + hostPID: true + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' ` - SampleVulnerableHSKUBERNETES8 = `apiVersion: policy/v1beta1 + + SampleVulnerableHSKUBERNETES7 = `apiVersion: policy/v1beta1 kind: PodSecurityPolicy metadata: name: privileged @@ -149,131 +252,34 @@ spec: hostPorts: - min: 0 max: 65535 - hostPID: true + hostIPC: true ` - SampleVulnerableHSKUBERNETES9 = ` -apiVersion: policy/v1beta1 + SampleSafeHSKUBERNETES7 = `apiVersion: policy/v1beta1 kind: PodSecurityPolicy metadata: name: privileged annotations: - seccomp.security.alpha.kubernetes.io/allowedProfileNames: unconfined + seccomp.security.alpha.kubernetes.io/allowedProfileNames: '*' spec: hostPorts: - min: 0 max: 65535 - hostNetwork: true + hostIPC: false ` -) -const ( - SampleSafeHSKUBERNETES1 = `apiVersion: v1 -kind: Pod -metadata: - name: security-context-demo -spec: - securityContext: - runAsUser: 1000 - runAsGroup: 3000 - fsGroup: 2000 - volumes: - - name: sec-ctx-vol - emptyDir: {} - containers: - - name: sec-ctx-demo - image: busybox - command: [ "sh", "-c", "sleep 1h" ] - volumeMounts: - - name: sec-ctx-vol - mountPath: /data/demo` - SampleSafeHSKUBERNETES2 = ` -apiVersion: extensions/v1beta1 -kind: Ingress -metadata: - name: example-ingress - annotations: - ingress.kubernetes.io/rewrite-target: / -spec: - rules: - - http: - paths: - - path: /foo - backend: - serviceName: foo-service - servicePort: 8000 - - path: /bar - backend: - serviceName: bar-service - servicePort: 8000 -` - SampleSafeHSKUBERNETES3 = ` -apiVersion: v1 -kind: Pod -metadata: - name: security-best-practice -spec: - containers: - # specification of the pod’s containers - # ... - securityContext: - readOnlyRootFilesystem: true -` - SampleSafeHSKUBERNETES4 = ` ---- -apiVersion: extensions/v1beta1 -kind: Deployment -... - containers: - - name: payment - image: nginx - securityContext: - capabilities: - drop: # Drop all capabilities from a pod as above - - all - add: # Add only those required - - NET_BIND_SERVICE -` - SampleSafeHSKUBERNETES5 = ` -apiVersion: v1 -kind: Pod -metadata: - name: privileged -spec: - containers: - - name: pause - image: k8s.gcr.io/pause - securityContext: - privileged: false -` - SampleSafeHSKUBERNETES6 = `apiVersion: policy/v1beta1 + SampleVulnerableHSKUBERNETES8 = `apiVersion: policy/v1beta1 kind: PodSecurityPolicy metadata: name: privileged annotations: - seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'runtime/default' + seccomp.security.alpha.kubernetes.io/allowedProfileNames: '*' spec: - privileged: true - allowPrivilegeEscalation: true - allowedCapabilities: - - '*' - volumes: - - '*' - hostNetwork: true hostPorts: - min: 0 max: 65535 - hostIPC: true hostPID: true - runAsUser: - rule: 'RunAsAny' - seLinux: - rule: 'RunAsAny' - supplementalGroups: - rule: 'RunAsAny' - fsGroup: - rule: 'RunAsAny' ` - SampleSafeHSKUBERNETES7 = `apiVersion: policy/v1beta1 + SampleSafeHSKUBERNETES8 = `apiVersion: policy/v1beta1 kind: PodSecurityPolicy metadata: name: privileged @@ -283,19 +289,22 @@ spec: hostPorts: - min: 0 max: 65535 - hostIPC: false + hostPID: false ` - SampleSafeHSKUBERNETES8 = `apiVersion: policy/v1beta1 + + SampleVulnerableHSKUBERNETES9 = ` +apiVersion: policy/v1beta1 kind: PodSecurityPolicy metadata: name: privileged annotations: - seccomp.security.alpha.kubernetes.io/allowedProfileNames: '*' + seccomp.security.alpha.kubernetes.io/allowedProfileNames: unconfined spec: hostPorts: - min: 0 max: 65535 - hostPID: false` + hostNetwork: true +` SampleSafeHSKUBERNETES9 = ` apiVersion: policy/v1beta1 kind: PodSecurityPolicy