Refactor Dockerfile and Helm chart to enhance security and user permissions
All checks were successful
Talks slides — image & chart / vars (push) Successful in 1s
Talks slides — image & chart / Helm chart (push) Successful in 11s
Talks slides — image & chart / Build container image (push) Successful in 33s

- Updated Dockerfile to run as non-root user 'nginx' and adjusted Nginx configuration for improved security.
- Added pod security context in values.yaml to align with the non-root user setup.
- Refined deployment.yaml to utilize the new pod security context for better compliance with Kubernetes security standards.
This commit is contained in:
Le Prévost-Corvellec Arnault
2026-04-08 21:36:09 +02:00
parent e1576d2360
commit 9a4942daad
3 changed files with 28 additions and 13 deletions

View File

@@ -1,5 +1,6 @@
# syntax=docker/dockerfile:1 # syntax=docker/dockerfile:1
# BuildKit / buildx : cache apk + permissions posées au build (moins de travail / capabilities au runtime). # BuildKit / buildx : cache apk + permissions au build.
# Limage nginx:alpine fournit déjà lutilisateur / groupe nginx (typiquement 101:101) — pas besoin de le créer.
FROM nginx:alpine FROM nginx:alpine
RUN --mount=type=cache,target=/var/cache/apk \ RUN --mount=type=cache,target=/var/cache/apk \
@@ -7,9 +8,7 @@ RUN --mount=type=cache,target=/var/cache/apk \
WORKDIR /usr/share/nginx/html WORKDIR /usr/share/nginx/html
# Image figée au clone ; le conteneur met à jour via refresh.sh (git pull origin main). # Image figée au clone ; refresh.sh fait git pull en boucle (même utilisateur que nginx grâce à USER nginx).
# Seul le dossier content/ est extrait (sparse checkout) : léger et aligné sur la racine Nginx.
# Limage nginx:alpine place déjà des fichiers ici — il faut vider le répertoire avant git clone vers « . ».
ARG TALKS_REPO_URL=https://git.specificat.io/arnault/Talks.git ARG TALKS_REPO_URL=https://git.specificat.io/arnault/Talks.git
ARG TALKS_BRANCH=main ARG TALKS_BRANCH=main
ARG TALKS_SPARSE_DIR=content ARG TALKS_SPARSE_DIR=content
@@ -17,14 +16,13 @@ ARG TALKS_SPARSE_DIR=content
RUN find . -mindepth 1 -delete \ RUN find . -mindepth 1 -delete \
&& git clone --filter=blob:none --sparse --branch "${TALKS_BRANCH}" --single-branch "${TALKS_REPO_URL}" . \ && git clone --filter=blob:none --sparse --branch "${TALKS_BRANCH}" --single-branch "${TALKS_REPO_URL}" . \
&& git sparse-checkout init --cone \ && git sparse-checkout init --cone \
&& git sparse-checkout set "${TALKS_SPARSE_DIR}" \ && git sparse-checkout set "${TALKS_SPARSE_DIR}"
&& git config --global --add safe.directory /usr/share/nginx/html
COPY nginx/default.conf /etc/nginx/conf.d/default.conf COPY nginx/default.conf /etc/nginx/conf.d/default.conf
COPY refresh.sh /refresh.sh COPY refresh.sh /refresh.sh
# Caches et logs : créés ici avec le même schéma que lentrypoint nginx (évite le chown au démarrage). # Master non-root : pid hors /run (root-only), directive user commentée (évite setgid vers 101).
# Propriétaire nginx (cf. /etc/nginx/nginx.conf user) → lentrypoint ne refait pas chown si tout est déjà cohérent. # Caches, logs, dépôt : nginx — pas de setuid/setgid ni CAP dans Kubernetes.
RUN chmod +x /refresh.sh \ RUN chmod +x /refresh.sh \
&& mkdir -p \ && mkdir -p \
/var/cache/nginx/client_temp \ /var/cache/nginx/client_temp \
@@ -36,9 +34,19 @@ RUN chmod +x /refresh.sh \
&& chown -R nginx:nginx \ && chown -R nginx:nginx \
/var/cache/nginx \ /var/cache/nginx \
/var/log/nginx \ /var/log/nginx \
/usr/share/nginx/html /usr/share/nginx/html \
&& sed -i 's|pid /run/nginx.pid;|pid /tmp/nginx.pid;|g' /etc/nginx/nginx.conf \
&& sed -i 's|pid /run/nginx/nginx.pid;|pid /tmp/nginx.pid;|g' /etc/nginx/nginx.conf \
&& sed -i 's/^[[:space:]]*user nginx;/# user nginx (master non-root)/' /etc/nginx/nginx.conf \
&& mkdir -p /home/nginx \
&& chown nginx:nginx /home/nginx /refresh.sh \
&& su -s /bin/sh nginx -c 'HOME=/home/nginx git config --global --add safe.directory /usr/share/nginx/html'
# Port non privilégié (pas de CAP_NET_BIND_SERVICE) ; le Service K8s mappe souvent 80 → 8080. ENV HOME=/home/nginx
USER nginx
# Port non privilégié ; le Service K8s mappe souvent 80 → 8080.
EXPOSE 8080 EXPOSE 8080
CMD sh -c "/refresh.sh & exec nginx -g 'daemon off;'" CMD sh -c "/refresh.sh & exec nginx -g 'daemon off;'"

View File

@@ -24,15 +24,14 @@ spec:
{{- toYaml . | nindent 8 }} {{- toYaml . | nindent 8 }}
{{- end }} {{- end }}
securityContext: securityContext:
seccompProfile: {{- toYaml .Values.slides.podSecurityContext | nindent 8 }}
type: RuntimeDefault
containers: containers:
- name: nginx - name: nginx
image: "{{ .Values.slides.image.repository }}:{{ .Values.slides.image.tag | default .Chart.AppVersion }}" image: "{{ .Values.slides.image.repository }}:{{ .Values.slides.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.slides.image.pullPolicy }} imagePullPolicy: {{ .Values.slides.image.pullPolicy }}
securityContext: securityContext:
allowPrivilegeEscalation: false allowPrivilegeEscalation: false
# Port 8080 dans le conteneur : pas besoin de NET_BIND_SERVICE (ports privilégiés). # Image : USER nginx + master non-root → pas de SETUID/SETGID ; port 8080 → pas de NET_BIND_SERVICE.
capabilities: capabilities:
drop: ["ALL"] drop: ["ALL"]
readOnlyRootFilesystem: false readOnlyRootFilesystem: false

View File

@@ -17,6 +17,14 @@ slides:
# Port découte dans le conteneur (doit correspondre à server/nginx/default.conf, ex. 8080). # Port découte dans le conteneur (doit correspondre à server/nginx/default.conf, ex. 8080).
containerPort: 8080 containerPort: 8080
# Aligné sur lutilisateur nginx de limage (UID/GID 101). Limage utilise USER nginx.
podSecurityContext:
runAsUser: 101
runAsGroup: 101
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
service: service:
type: ClusterIP type: ClusterIP
# Port du Service (Ingress pointe ici) ; le trafic est envoyé vers containerPort sur les pods. # Port du Service (Ingress pointe ici) ; le trafic est envoyé vers containerPort sur les pods.