From 2670727cfa0d10ad4fdaac6ec9279e7f449f018d Mon Sep 17 00:00:00 2001 From: Cristian Felipe Cruz Buitron Date: Mon, 24 Aug 2026 20:35:08 -0500 Subject: [PATCH] fix(ci,ecommerce): escanear el manifiesto renderizado y endurecer readOnlyRootFilesystem del frontend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Trivy IaC escaneaba solo el patch aislado de Kustomize (workloads/ecommerce/frontend/patches/deployment.yaml), no lo que realmente corre en el clúster. Eso generaba falsos positivos en todo lo heredado del base remoto de platform-templates (runAsNonRoot, seccompProfile) porque el patch nunca los repite. Se instala kubectl, se renderiza el kustomization completo y se escanea ese output. - readOnlyRootFilesystem pasa de false a true en el contenedor del frontend: revisando next.config.ts (optimización de imágenes) y app/api/revalidate/route.ts (revalidateTag sin cache handler custom), la imagen sí escribe a ./.next/cache en runtime. Se agregan volúmenes emptyDir para .next/cache y /tmp. --- .gitea/workflows/build.yaml | 29 ++++++++++++++++++- .../frontend/patches/deployment.yaml | 23 ++++++++++++++- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index 3dda2c8..c6aa96f 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -127,6 +127,7 @@ jobs: env: APP_DIR: workloads/ecommerce MANIFEST_FILE: workloads/ecommerce/frontend/patches/deployment.yaml + KUSTOMIZE_DIR: workloads/ecommerce/frontend IMAGE_NAME: gitea.cruzcloud.net/devops/ecommerce-frontend steps: @@ -276,6 +277,32 @@ jobs: chmod +x /tmp/trivy /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): # contenedores como root, falta de resource limits, falta de # readiness/liveness probes, etc. Informativo por ahora — no bloquea @@ -287,7 +314,7 @@ jobs: /tmp/trivy config \ --severity CRITICAL,HIGH,MEDIUM \ --exit-code 0 \ - "${MANIFEST_FILE}" + /tmp/frontend-rendered.yaml # Instalado directo con pip (no como container aparte): el runner de # Gitea ejecuta cada job ya dentro de un contenedor propio que habla diff --git a/workloads/ecommerce/frontend/patches/deployment.yaml b/workloads/ecommerce/frontend/patches/deployment.yaml index 0b58bfb..cb2efa0 100644 --- a/workloads/ecommerce/frontend/patches/deployment.yaml +++ b/workloads/ecommerce/frontend/patches/deployment.yaml @@ -64,9 +64,20 @@ spec: # binario de node para esto, pero eso solo funciona si el # bounding set del contenedor lo permite -- drop: ALL del base # 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: allowPrivilegeEscalation: false - readOnlyRootFilesystem: false + readOnlyRootFilesystem: true capabilities: drop: - ALL @@ -79,3 +90,13 @@ spec: limits: cpu: "1" memory: 768Mi + volumeMounts: + - name: next-cache + mountPath: /app/.next/cache + - name: tmp + mountPath: /tmp + volumes: + - name: next-cache + emptyDir: {} + - name: tmp + emptyDir: {} -- 2.54.0