Skip to content

Commit 7f1ca4a

Browse files
committed
fix(wik-webservice): add option securityDefaults
1 parent 9822d63 commit 7f1ca4a

6 files changed

Lines changed: 171 additions & 32 deletions

File tree

charts/wik-webservice/README.md

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -327,8 +327,9 @@ webservice:
327327
| `serviceAccount.name` | ServiceAccount name to use | `""` (default) |
328328
| `serviceAccount.annotations` | ServiceAccount annotations | `{}` |
329329
| `serviceAccount.automountServiceAccountToken` | Mount service account token (when creating SA) | `true` |
330-
| `webservice.securityContext` | Pod-level security context | `{}` |
331-
| `webservice.containerSecurityContext` | Container-level security context | `{}` |
330+
| `webservice.securityDefaults` | Apply hardened pod & container security defaults (merged with user overrides) | `true` |
331+
| `webservice.securityContext` | Pod-level security context (merged on top of defaults) | `{}` |
332+
| `webservice.containerSecurityContext` | Container-level security context (merged on top of defaults) | `{}` |
332333
| `webservice.enableServiceLinks` | Enable service links injection | `false` |
333334
| `webservice.hostNetwork` | Access host network namespace | `false` |
334335
| `webservice.hostPID` | Access host PID namespace | `false` |
@@ -692,7 +693,7 @@ This chart includes secure defaults that can be overridden as needed:
692693

693694
### Security Context Defaults
694695

695-
By default, pods run with the following security settings:
696+
When `webservice.securityDefaults` is `true` (the default), the chart renders the following baseline:
696697

697698
```yaml
698699
# Pod Security Context
@@ -712,21 +713,50 @@ securityContext:
712713
- ALL
713714
seccompProfile:
714715
type: RuntimeDefault
716+
```
715717

716-
To override these defaults, set `webservice.securityContext` or `webservice.containerSecurityContext`:
718+
`webservice.securityContext` and `webservice.containerSecurityContext` are merged **on top** of those defaults — user keys win, unset keys keep the default. So a partial override works as you'd expect:
717719

718720
```yaml
719721
webservice:
720722
securityContext:
721-
runAsUser: 2000
722-
fsGroup: 2000
723+
runAsUser: 2000 # overrides the default 1000
724+
fsGroup: 2000 # overrides the default 1000
725+
# runAsNonRoot: true # still applied from the defaults
723726
containerSecurityContext:
724727
readOnlyRootFilesystem: false
725728
seccompProfile:
726729
type: Localhost
727730
localhostProfile: "profiles/custom.json"
728731
```
729732

733+
#### Disabling the defaults (root user, writable filesystem, …)
734+
735+
Setting `securityContext: {}` / `containerSecurityContext: {}` is **not enough** to opt out — they're already `{}` by default, and the defaults still render on top. Use `securityDefaults: false` to start from a clean slate (no `securityContext` block emitted at all → root user, writable rootfs, default caps):
736+
737+
```yaml
738+
webservice:
739+
securityDefaults: false
740+
```
741+
742+
You can still selectively add fields on top — e.g. run as root without forcing any other hardening:
743+
744+
```yaml
745+
webservice:
746+
securityDefaults: false
747+
containerSecurityContext:
748+
runAsUser: 0
749+
```
750+
751+
#### Pod-level vs container-level — why both?
752+
753+
Kubernetes lets several fields (`runAsUser`, `runAsGroup`, `runAsNonRoot`, `seccompProfile`, `seLinuxOptions`) live at **either** level. Container-level wins; pod-level acts as the default for every container in the pod (main, sidecars from `additionalContainers`, init containers). The other fields are scoped:
754+
755+
- **Pod-only** (volume/kernel attrs shared across the pod): `fsGroup`, `fsGroupChangePolicy`, `supplementalGroups`, `sysctls`
756+
- **Container-only** (per-process kernel attrs): `allowPrivilegeEscalation`, `capabilities`, `privileged`, `readOnlyRootFilesystem`, `procMount`
757+
758+
The defaults set `runAsUser` / `runAsNonRoot` at both levels on purpose — pod-level covers any sidecar you add via `additionalContainers`, container-level locks the main container.
759+
730760
### Resource Defaults
731761

732762
If no resources are specified, the following defaults are applied:
@@ -990,6 +1020,7 @@ webservice:
9901020
| webservice.resources | object | `{}` | |
9911021
| webservice.revisionHistoryLimit | string | `""` | |
9921022
| webservice.securityContext | object | `{}` | |
1023+
| webservice.securityDefaults | bool | `true` | |
9931024
| webservice.service.annotations | object | `{}` | |
9941025
| webservice.service.enabled | bool | `true` | |
9951026
| webservice.service.port | int | `80` | |

charts/wik-webservice/README.md.gotmpl

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -327,8 +327,9 @@ webservice:
327327
| `serviceAccount.name` | ServiceAccount name to use | `""` (default) |
328328
| `serviceAccount.annotations` | ServiceAccount annotations | `{}` |
329329
| `serviceAccount.automountServiceAccountToken` | Mount service account token (when creating SA) | `true` |
330-
| `webservice.securityContext` | Pod-level security context | `{}` |
331-
| `webservice.containerSecurityContext` | Container-level security context | `{}` |
330+
| `webservice.securityDefaults` | Apply hardened pod & container security defaults (merged with user overrides) | `true` |
331+
| `webservice.securityContext` | Pod-level security context (merged on top of defaults) | `{}` |
332+
| `webservice.containerSecurityContext` | Container-level security context (merged on top of defaults) | `{}` |
332333
| `webservice.enableServiceLinks` | Enable service links injection | `false` |
333334
| `webservice.hostNetwork` | Access host network namespace | `false` |
334335
| `webservice.hostPID` | Access host PID namespace | `false` |
@@ -692,7 +693,7 @@ This chart includes secure defaults that can be overridden as needed:
692693

693694
### Security Context Defaults
694695

695-
By default, pods run with the following security settings:
696+
When `webservice.securityDefaults` is `true` (the default), the chart renders the following baseline:
696697

697698
```yaml
698699
# Pod Security Context
@@ -712,21 +713,50 @@ securityContext:
712713
- ALL
713714
seccompProfile:
714715
type: RuntimeDefault
716+
```
715717

716-
To override these defaults, set `webservice.securityContext` or `webservice.containerSecurityContext`:
718+
`webservice.securityContext` and `webservice.containerSecurityContext` are merged **on top** of those defaults — user keys win, unset keys keep the default. So a partial override works as you'd expect:
717719

718720
```yaml
719721
webservice:
720722
securityContext:
721-
runAsUser: 2000
722-
fsGroup: 2000
723+
runAsUser: 2000 # overrides the default 1000
724+
fsGroup: 2000 # overrides the default 1000
725+
# runAsNonRoot: true # still applied from the defaults
723726
containerSecurityContext:
724727
readOnlyRootFilesystem: false
725728
seccompProfile:
726729
type: Localhost
727730
localhostProfile: "profiles/custom.json"
728731
```
729732

733+
#### Disabling the defaults (root user, writable filesystem, …)
734+
735+
Setting `securityContext: {}` / `containerSecurityContext: {}` is **not enough** to opt out — they're already `{}` by default, and the defaults still render on top. Use `securityDefaults: false` to start from a clean slate (no `securityContext` block emitted at all → root user, writable rootfs, default caps):
736+
737+
```yaml
738+
webservice:
739+
securityDefaults: false
740+
```
741+
742+
You can still selectively add fields on top — e.g. run as root without forcing any other hardening:
743+
744+
```yaml
745+
webservice:
746+
securityDefaults: false
747+
containerSecurityContext:
748+
runAsUser: 0
749+
```
750+
751+
#### Pod-level vs container-level — why both?
752+
753+
Kubernetes lets several fields (`runAsUser`, `runAsGroup`, `runAsNonRoot`, `seccompProfile`, `seLinuxOptions`) live at **either** level. Container-level wins; pod-level acts as the default for every container in the pod (main, sidecars from `additionalContainers`, init containers). The other fields are scoped:
754+
755+
- **Pod-only** (volume/kernel attrs shared across the pod): `fsGroup`, `fsGroupChangePolicy`, `supplementalGroups`, `sysctls`
756+
- **Container-only** (per-process kernel attrs): `allowPrivilegeEscalation`, `capabilities`, `privileged`, `readOnlyRootFilesystem`, `procMount`
757+
758+
The defaults set `runAsUser` / `runAsNonRoot` at both levels on purpose — pod-level covers any sidecar you add via `additionalContainers`, container-level locks the main container.
759+
730760
### Resource Defaults
731761

732762
If no resources are specified, the following defaults are applied:

charts/wik-webservice/templates/deployment.yaml

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,16 @@ spec:
5858
hostAliases:
5959
{{ .Values.webservice.hostAliases | toYaml | indent 8 }}
6060
{{ end }}
61-
securityContext:
61+
{{- $podSec := dict }}
62+
{{- if .Values.webservice.securityDefaults }}
63+
{{- $podSec = dict "fsGroup" 1000 "runAsNonRoot" true "runAsUser" 1000 }}
64+
{{- end }}
6265
{{- if .Values.webservice.securityContext }}
63-
{{ .Values.webservice.securityContext | toYaml | indent 8 }}
64-
{{- else }}
65-
fsGroup: 1000
66-
runAsNonRoot: true
67-
runAsUser: 1000
66+
{{- $podSec = mustMergeOverwrite $podSec (deepCopy .Values.webservice.securityContext) }}
67+
{{- end }}
68+
{{- if $podSec }}
69+
securityContext:
70+
{{ $podSec | toYaml | indent 8 }}
6871
{{- end }}
6972
{{ if .Values.webservice.dnsConfig }}
7073
dnsConfig:
@@ -76,19 +79,23 @@ spec:
7679
containers:
7780
- name: {{ template "fullname" . }}
7881
image: {{ .Values.webservice.image }}
79-
securityContext:
82+
{{- $ctrSec := dict }}
83+
{{- if .Values.webservice.securityDefaults }}
84+
{{- $ctrSec = dict
85+
"runAsNonRoot" true
86+
"runAsUser" 1000
87+
"allowPrivilegeEscalation" false
88+
"readOnlyRootFilesystem" true
89+
"capabilities" (dict "drop" (list "ALL"))
90+
"seccompProfile" (dict "type" "RuntimeDefault")
91+
}}
92+
{{- end }}
8093
{{- if .Values.webservice.containerSecurityContext }}
81-
{{ .Values.webservice.containerSecurityContext | toYaml | indent 12 }}
82-
{{- else }}
83-
runAsNonRoot: true
84-
runAsUser: 1000
85-
allowPrivilegeEscalation: false
86-
readOnlyRootFilesystem: true
87-
capabilities:
88-
drop:
89-
- ALL
90-
seccompProfile:
91-
type: RuntimeDefault
94+
{{- $ctrSec = mustMergeOverwrite $ctrSec (deepCopy .Values.webservice.containerSecurityContext) }}
95+
{{- end }}
96+
{{- if $ctrSec }}
97+
securityContext:
98+
{{ $ctrSec | toYaml | indent 12 }}
9299
{{- end }}
93100
imagePullPolicy: {{ .Values.webservice.imagePullPolicy }}
94101
ports:

charts/wik-webservice/tests/deployment_test.yaml

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,63 @@ tests:
6161
- equal:
6262
path: spec.template.spec.securityContext.fsGroup
6363
value: 1000
64+
65+
- it: should merge user securityContext on top of hardened defaults
66+
set:
67+
webservice.image: nginx:latest
68+
webservice.securityContext:
69+
runAsUser: 2000
70+
asserts:
71+
- equal:
72+
path: spec.template.spec.securityContext.runAsUser
73+
value: 2000
74+
- equal:
75+
path: spec.template.spec.securityContext.fsGroup
76+
value: 1000
77+
- equal:
78+
path: spec.template.spec.securityContext.runAsNonRoot
79+
value: true
80+
81+
- it: should merge user containerSecurityContext on top of hardened defaults
82+
set:
83+
webservice.image: nginx:latest
84+
webservice.containerSecurityContext:
85+
readOnlyRootFilesystem: false
86+
asserts:
87+
- equal:
88+
path: spec.template.spec.containers[0].securityContext.readOnlyRootFilesystem
89+
value: false
90+
- equal:
91+
path: spec.template.spec.containers[0].securityContext.allowPrivilegeEscalation
92+
value: false
93+
- equal:
94+
path: spec.template.spec.containers[0].securityContext.runAsUser
95+
value: 1000
96+
- equal:
97+
path: spec.template.spec.containers[0].securityContext.capabilities.drop[0]
98+
value: ALL
99+
100+
- it: should omit securityContext entirely when securityDefaults is false and no override is set
101+
set:
102+
webservice.image: nginx:latest
103+
webservice.securityDefaults: false
104+
asserts:
105+
- isNull:
106+
path: spec.template.spec.securityContext
107+
- isNull:
108+
path: spec.template.spec.containers[0].securityContext
109+
110+
- it: should allow running as root with writable rootfs by disabling securityDefaults
111+
set:
112+
webservice.image: nginx:latest
113+
webservice.securityDefaults: false
114+
webservice.containerSecurityContext:
115+
runAsUser: 0
116+
asserts:
117+
- equal:
118+
path: spec.template.spec.containers[0].securityContext.runAsUser
119+
value: 0
120+
- isNull:
121+
path: spec.template.spec.containers[0].securityContext.readOnlyRootFilesystem
122+
- isNull:
123+
path: spec.template.spec.containers[0].securityContext.runAsNonRoot

charts/wik-webservice/values.schema.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,10 @@
259259
"type": "object"
260260
}
261261
},
262+
"securityDefaults": {
263+
"type": "boolean",
264+
"default": true
265+
},
262266
"securityContext": {
263267
"type": "object",
264268
"default": {}

charts/wik-webservice/values.yaml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,14 @@ webservice:
245245
# plaintext:
246246
# INIT_VAR: value
247247

248-
# Pod-level security context
248+
# Apply hardened security context defaults at the pod and container level.
249+
# When true, the chart renders the defaults documented in the README and
250+
# merges `securityContext` / `containerSecurityContext` on top (user values win).
251+
# Set to false to start from an empty context (e.g. to run as root or with a
252+
# writable root filesystem) — then opt back in only to the fields you want.
253+
securityDefaults: true
254+
255+
# Pod-level security context (merged on top of defaults when securityDefaults: true)
249256
securityContext: {}
250257
# runAsUser: 1000
251258
# runAsGroup: 1000
@@ -259,7 +266,7 @@ webservice:
259266
# Enable service links (injects all services as environment variables)
260267
enableServiceLinks: false
261268

262-
# Container-level security context
269+
# Container-level security context (merged on top of defaults when securityDefaults: true)
263270
containerSecurityContext: {}
264271
# allowPrivilegeEscalation: false
265272
# readOnlyRootFilesystem: true

0 commit comments

Comments
 (0)