fix(ci,ecommerce): escanear el manifiesto renderizado y endurecer readOnlyRootFilesystem del frontend #31

Merged
devops merged 1 commits from fix/frontend-trivy-iac-and-readonly-fs into main 2026-08-25 01:36:37 +00:00
2 changed files with 50 additions and 2 deletions
Showing only changes of commit 2670727cfa - Show all commits

No files matched your search

+28 -1
View File
@@ -127,6 +127,7 @@ jobs:
env: env:
APP_DIR: workloads/ecommerce APP_DIR: workloads/ecommerce
MANIFEST_FILE: workloads/ecommerce/frontend/patches/deployment.yaml MANIFEST_FILE: workloads/ecommerce/frontend/patches/deployment.yaml
KUSTOMIZE_DIR: workloads/ecommerce/frontend
IMAGE_NAME: gitea.cruzcloud.net/devops/ecommerce-frontend IMAGE_NAME: gitea.cruzcloud.net/devops/ecommerce-frontend
steps: steps:
@@ -276,6 +277,32 @@ jobs:
chmod +x /tmp/trivy chmod +x /tmp/trivy
/tmp/trivy version /tmp/trivy version
- name: Instalar kubectl
shell: bash
run: |
set -euo pipefail
KUBECTL_VERSION="v1.31.2"
curl -sSfL \
"https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/amd64/kubectl" \
-o /tmp/kubectl
chmod +x /tmp/kubectl
/tmp/kubectl version --client
# ${MANIFEST_FILE} es solo el patch de Kustomize del Deployment, no el
# manifiesto que de verdad corre en el clúster -- escanearlo directo
# generaba falsos positivos en todo lo que hereda del base remoto de
# platform-templates (runAsNonRoot, seccompProfile), porque ese patch
# nunca repite lo que ya trae el base (visto en vivo 2026-08-24:
# comparando el patch aislado contra el output real de
# "kubectl kustomize", 2 de 5 hallazgos desaparecían). Renderizamos el
# kustomization completo (${KUSTOMIZE_DIR}, que sí trae el base vía
# git remoto) y escaneamos eso.
- name: Renderizar manifiesto Kustomize
shell: bash
run: |
set -euo pipefail
/tmp/kubectl kustomize "${KUSTOMIZE_DIR}" > /tmp/frontend-rendered.yaml
# Escanea el manifiesto Kubernetes real del frontend (no la imagen): # Escanea el manifiesto Kubernetes real del frontend (no la imagen):
# contenedores como root, falta de resource limits, falta de # contenedores como root, falta de resource limits, falta de
# readiness/liveness probes, etc. Informativo por ahora — no bloquea # readiness/liveness probes, etc. Informativo por ahora — no bloquea
@@ -287,7 +314,7 @@ jobs:
/tmp/trivy config \ /tmp/trivy config \
--severity CRITICAL,HIGH,MEDIUM \ --severity CRITICAL,HIGH,MEDIUM \
--exit-code 0 \ --exit-code 0 \
"${MANIFEST_FILE}" /tmp/frontend-rendered.yaml
# Instalado directo con pip (no como container aparte): el runner de # Instalado directo con pip (no como container aparte): el runner de
# Gitea ejecuta cada job ya dentro de un contenedor propio que habla # Gitea ejecuta cada job ya dentro de un contenedor propio que habla
@@ -64,9 +64,20 @@ spec:
# binario de node para esto, pero eso solo funciona si el # binario de node para esto, pero eso solo funciona si el
# bounding set del contenedor lo permite -- drop: ALL del base # bounding set del contenedor lo permite -- drop: ALL del base
# lo bloquea igual, root o no root. Mismo motivo que docs-portal. # lo bloquea igual, root o no root. Mismo motivo que docs-portal.
#
# readOnlyRootFilesystem: true -- revisado en código (2026-08-24,
# aún no probado en vivo con este cambio) que esta imagen sí
# escribe a disco en runtime, no solo en build: el servidor
# standalone de Next.js cachea a ./.next/cache tanto el
# optimizador de imágenes (next.config.ts trae remotePatterns,
# sin "unoptimized: true") como el revalidateTag("medusa-products")
# de app/api/revalidate/route.ts (cache handler por default, sin
# uno custom tipo Redis). De ahí los dos volumeMounts de abajo:
# sin ellos, con root filesystem de solo lectura, ambas rutas
# fallarían al intentar escribir.
securityContext: securityContext:
allowPrivilegeEscalation: false allowPrivilegeEscalation: false
readOnlyRootFilesystem: false readOnlyRootFilesystem: true
capabilities: capabilities:
drop: drop:
- ALL - ALL
@@ -79,3 +90,13 @@ spec:
limits: limits:
cpu: "1" cpu: "1"
memory: 768Mi memory: 768Mi
volumeMounts:
- name: next-cache
mountPath: /app/.next/cache
- name: tmp
mountPath: /tmp
volumes:
- name: next-cache
emptyDir: {}
- name: tmp
emptyDir: {}