diff --git a/README-fr.md b/README-fr.md index 410d96e..1a6d61b 100644 --- a/README-fr.md +++ b/README-fr.md @@ -511,6 +511,45 @@ kubectl describe svc vtom-server -n vtom # Côté cloud : vérifier annotations et quotas LB ``` +# Sécurité + +## Conformité + +Ce chart est conforme aux référentiels suivants : + +- **Kubernetes Pod Security Standards (PSS) — profil `restricted`** (tous les pods) +- **CIS Kubernetes Benchmark — Level 1** (tous les contrôles applicables) + +### Contrôles en place + +| Contrôle | Détail | +|---|---| +| `runAsNonRoot: true` | Tous les pods | +| `runAsUser` / `runAsGroup` non nuls | uid=1000 / gid=1000 (vtom), uid=10001 (ITC, ITM, MFT) | +| `fsGroup` non nul | 1000 sur les composants VTOM core ; 10001 sur ITC, ITM, MFT | +| `allowPrivilegeEscalation: false` | Tous les conteneurs, y compris les init containers et sidecars | +| `capabilities.drop: ["ALL"]` | Tous les conteneurs, y compris les init containers et sidecars | +| `seccompProfile: RuntimeDefault` | Tous les pods | +| `readOnlyRootFilesystem: true` | Init containers et sidecar proxy DB | +| `automountServiceAccountToken: false` | Tous les pods | +| NetworkPolicies — deny par défaut | Ingress et egress bloqués par défaut ; règles d'autorisation explicites par composant | +| Secrets via `secretKeyRef` | Aucune donnée sensible dans les ConfigMaps ou variables d'environnement | +| `defaultMode: 0440` sur les volumes secrets | Fichiers de licence montés en lecture seule | + +### Exception documentée (CIS Level 2) + +`readOnlyRootFilesystem` n'est pas activé sur les conteneurs applicatifs principaux (vtom-server, vtom-agent, vtom-apiserver, ITC, ITM). Ces composants écrivent sur leur système de fichiers à l'exécution, ce qui constitue une contrainte image upstream. Ce contrôle est classé **Level 2** dans le CIS Benchmark et son absence est compensée par les contrôles listés ci-dessus. + +### Pod Security Admission + +Pour appliquer le profil PSS `restricted` au niveau du namespace, ajouter le label suivant à la création du namespace : + +```bash +kubectl label namespace vtom \ + pod-security.kubernetes.io/enforce=restricted \ + pod-security.kubernetes.io/warn=restricted +``` + # Licence Ce projet est sous licence Apache 2.0. Voir le fichier [LICENSE](LICENSE) pour plus de détails. diff --git a/README.md b/README.md index 00e45c4..a579375 100644 --- a/README.md +++ b/README.md @@ -511,6 +511,45 @@ kubectl describe svc vtom-server -n vtom # Cloud side: check annotations and LB quotas ``` +# Security + +## Compliance + +This chart is compliant with: + +- **Kubernetes Pod Security Standards (PSS) — `restricted` profile** (all pods) +- **CIS Kubernetes Benchmark — Level 1** (all applicable controls) + +### Controls in place + +| Control | Detail | +|---|---| +| `runAsNonRoot: true` | All pods | +| `runAsUser` / `runAsGroup` non-zero | uid=1000 / gid=1000 (vtom), uid=10001 (ITC, ITM, MFT) | +| `fsGroup` non-zero | 1000 on VTOM core; 10001 on ITC, ITM, MFT | +| `allowPrivilegeEscalation: false` | All containers, including init containers and sidecars | +| `capabilities.drop: ["ALL"]` | All containers, including init containers and sidecars | +| `seccompProfile: RuntimeDefault` | All pods | +| `readOnlyRootFilesystem: true` | Init containers and DB proxy sidecar | +| `automountServiceAccountToken: false` | All pods | +| NetworkPolicies — default deny | Ingress and egress blocked by default; explicit allow rules per component | +| Secrets via `secretKeyRef` | No credentials in ConfigMaps or environment variables | +| Secret volume `defaultMode: 0440` | License files mounted read-only | + +### Known exception (CIS Level 2) + +`readOnlyRootFilesystem` is not set on the main application containers (vtom-server, vtom-agent, vtom-apiserver, ITC, ITM). These components write to their filesystem at runtime, which is an upstream image constraint. This control is classified **Level 2** in the CIS Benchmark and its absence is compensated by the controls listed above. + +### Pod Security Admission + +To enforce the `restricted` PSS profile at the namespace level, add the following label when creating the namespace: + +```bash +kubectl label namespace vtom \ + pod-security.kubernetes.io/enforce=restricted \ + pod-security.kubernetes.io/warn=restricted +``` + # License This project is licensed under the Apache 2.0 License — see the [LICENSE](LICENSE) file for details. diff --git a/charts/visual-tom/Chart.yaml b/charts/visual-tom/Chart.yaml index 879ff5e..5012bb6 100644 --- a/charts/visual-tom/Chart.yaml +++ b/charts/visual-tom/Chart.yaml @@ -7,7 +7,8 @@ description: > type: application # Chart version (follows SemVer). Increment on every chart change. -version: 0.2.4 +version: 0.2.5 + # Reference application version (VTOM). ITC, ITM and MFT versions are defined in values.yaml. appVersion: "7.3.2c" diff --git a/charts/visual-tom/templates/_helpers.tpl b/charts/visual-tom/templates/_helpers.tpl index b9b6b8f..cac95c0 100644 --- a/charts/visual-tom/templates/_helpers.tpl +++ b/charts/visual-tom/templates/_helpers.tpl @@ -212,6 +212,7 @@ Rendered only when dbProxy.enabled=true. runAsNonRoot: true runAsUser: 1000 allowPrivilegeEscalation: false + readOnlyRootFilesystem: true capabilities: drop: ["ALL"] {{- end }} diff --git a/charts/visual-tom/templates/vtom/deployment-agent.yaml b/charts/visual-tom/templates/vtom/deployment-agent.yaml index 4379d51..f59b4f2 100644 --- a/charts/visual-tom/templates/vtom/deployment-agent.yaml +++ b/charts/visual-tom/templates/vtom/deployment-agent.yaml @@ -37,7 +37,10 @@ spec: {{- if .Values.securityContext.runAsUser }} runAsUser: {{ .Values.securityContext.runAsUser }} {{- end }} - fsGroup: 0 + {{- if .Values.securityContext.runAsGroup }} + runAsGroup: {{ .Values.securityContext.runAsGroup }} + {{- end }} + fsGroup: {{ .Values.securityContext.fsGroup | default 0 }} seccompProfile: type: RuntimeDefault containers: diff --git a/charts/visual-tom/templates/vtom/deployment-apiserver.yaml b/charts/visual-tom/templates/vtom/deployment-apiserver.yaml index 9b938b8..4f6c3b1 100644 --- a/charts/visual-tom/templates/vtom/deployment-apiserver.yaml +++ b/charts/visual-tom/templates/vtom/deployment-apiserver.yaml @@ -40,7 +40,10 @@ spec: {{- if .Values.securityContext.runAsUser }} runAsUser: {{ .Values.securityContext.runAsUser }} {{- end }} - fsGroup: 0 + {{- if .Values.securityContext.runAsGroup }} + runAsGroup: {{ .Values.securityContext.runAsGroup }} + {{- end }} + fsGroup: {{ .Values.securityContext.fsGroup | default 0 }} seccompProfile: type: RuntimeDefault initContainers: diff --git a/charts/visual-tom/templates/vtom/deployment-server.yaml b/charts/visual-tom/templates/vtom/deployment-server.yaml index d87194a..c2357a3 100644 --- a/charts/visual-tom/templates/vtom/deployment-server.yaml +++ b/charts/visual-tom/templates/vtom/deployment-server.yaml @@ -42,7 +42,10 @@ spec: {{- if .Values.securityContext.runAsUser }} runAsUser: {{ .Values.securityContext.runAsUser }} {{- end }} - fsGroup: 0 + {{- if .Values.securityContext.runAsGroup }} + runAsGroup: {{ .Values.securityContext.runAsGroup }} + {{- end }} + fsGroup: {{ .Values.securityContext.fsGroup | default 0 }} seccompProfile: type: RuntimeDefault initContainers: diff --git a/charts/visual-tom/values-aws.yaml b/charts/visual-tom/values-aws.yaml index 6e045b5..0e0634b 100644 --- a/charts/visual-tom/values-aws.yaml +++ b/charts/visual-tom/values-aws.yaml @@ -66,7 +66,9 @@ tls: clusterIssuer: letsencrypt-prod securityContext: - runAsUser: 1000 # UID of the vtom user in Absyss images + runAsUser: 1000 # uid=1000(vtom) — confirmed from running image (kubectl exec id) + runAsGroup: 1000 # gid=1000(vtom) — confirmed from running image + fsGroup: 1000 networkPolicy: enabled: true diff --git a/charts/visual-tom/values-azure.yaml b/charts/visual-tom/values-azure.yaml index 9853199..1c7a7ec 100644 --- a/charts/visual-tom/values-azure.yaml +++ b/charts/visual-tom/values-azure.yaml @@ -72,7 +72,9 @@ tls: clusterIssuer: letsencrypt-prod securityContext: - runAsUser: 1000 # UID of the vtom user in Absyss images + runAsUser: 1000 # uid=1000(vtom) — confirmed from running image (kubectl exec id) + runAsGroup: 1000 # gid=1000(vtom) — confirmed from running image + fsGroup: 1000 networkPolicy: enabled: true diff --git a/charts/visual-tom/values-gcp.yaml b/charts/visual-tom/values-gcp.yaml index cc4863d..4670109 100644 --- a/charts/visual-tom/values-gcp.yaml +++ b/charts/visual-tom/values-gcp.yaml @@ -66,7 +66,9 @@ tls: clusterIssuer: letsencrypt-prod securityContext: - runAsUser: 1000 # UID of the vtom user in Absyss images + runAsUser: 1000 # uid=1000(vtom) — confirmed from running image (kubectl exec id) + runAsGroup: 1000 # gid=1000(vtom) — confirmed from running image + fsGroup: 1000 networkPolicy: enabled: true diff --git a/charts/visual-tom/values-onpremise.yaml b/charts/visual-tom/values-onpremise.yaml index 37f0abd..2ff272d 100644 --- a/charts/visual-tom/values-onpremise.yaml +++ b/charts/visual-tom/values-onpremise.yaml @@ -37,8 +37,10 @@ tls: clusterIssuer: letsencrypt-prod securityContext: - runAsUser: 1000 # UID of the vtom user in Absyss images - # Leave null on OpenShift (nonroot SCC manages the UID) + runAsUser: 1000 # uid=1000(vtom) — confirmed from running image (kubectl exec id) + runAsGroup: 1000 # gid=1000(vtom) — confirmed from running image + fsGroup: 1000 + # Leave all three null on OpenShift (nonroot SCC manages the UID/GID automatically) networkPolicy: enabled: true diff --git a/charts/visual-tom/values.yaml b/charts/visual-tom/values.yaml index c400a34..cc76d34 100644 --- a/charts/visual-tom/values.yaml +++ b/charts/visual-tom/values.yaml @@ -532,16 +532,19 @@ networkPolicy: # ----------------------------------------------------------------------------- # Pod security context — applies to VTOM components only (server, apiserver, agent). -# runAsUser: numeric UID required on standard K8s when the image uses a symbolic -# user (e.g.: USER vtom) — K8s cannot verify runAsNonRoot without a numeric UID. -# Leave null on OpenShift: the nonroot SCC assigns the UID automatically from the -# namespace range, avoiding conflicts with a hardcoded UID. -# Recommended value for VTOM images (server, apiserver, agent): 1000 (set in values-.yaml). -# ITC, ITM and MFT run under their own hardcoded uid 10001 (absyss) in their templates -# and ignore this value. +# runAsUser / runAsGroup: numeric UID/GID required on standard K8s when the image uses +# a symbolic user (e.g.: USER vtom) — K8s cannot verify runAsNonRoot without a numeric UID. +# fsGroup: controls volume ownership on mounted PVCs. +# Leave all three null on OpenShift: the nonroot SCC assigns the UID automatically from +# the namespace range, avoiding conflicts with a hardcoded value. +# Confirmed values for VTOM images (server, apiserver, agent): uid=1000 gid=1000 (set in +# values-.yaml). ITC, ITM and MFT run under their own hardcoded uid/gid 10001 and +# ignore these values. # ----------------------------------------------------------------------------- securityContext: runAsUser: null + runAsGroup: null + fsGroup: null # ----------------------------------------------------------------------------- # PodDisruptionBudget