From e3d9e71e85f12248cd146528f5364aca432846a1 Mon Sep 17 00:00:00 2001 From: Thomas Boni Date: Thu, 7 May 2026 10:52:41 +0200 Subject: [PATCH] conf(compose & chart): update conf to be rootless --- charts/plumber/Chart.yaml | 4 +-- charts/plumber/values.yaml | 63 +++++++++++++++++++++++--------------- compose.yml | 7 +++-- configmap.yml.example | 4 ++- podman.local.yml.example | 6 ++-- podman.yml.example | 11 +++++-- versions.env | 4 +-- 7 files changed, 63 insertions(+), 36 deletions(-) diff --git a/charts/plumber/Chart.yaml b/charts/plumber/Chart.yaml index 901adf1..4d22056 100644 --- a/charts/plumber/Chart.yaml +++ b/charts/plumber/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v2 name: plumber description: Helm chart for Plumber type: application -version: "1.3.5" -appVersion: "1.3.5" +version: "1.3.6" +appVersion: "1.3.6" home: https://github.com/getplumber/platform/ maintainers: - name: devpro diff --git a/charts/plumber/values.yaml b/charts/plumber/values.yaml index 786c264..48c45bf 100644 --- a/charts/plumber/values.yaml +++ b/charts/plumber/values.yaml @@ -22,7 +22,7 @@ front: imageName: getplumber/frontend # -- Image pull policy for the container. imagePullPolicy: Always - tag: v2.34.6 + tag: v2.34.8 replicaCount: 1 revisionHistoryLimit: 5 port: 3000 @@ -46,6 +46,7 @@ front: # memory: 64Mi extraIngressAnnotations: {} # -- Security context for front containers. + # The frontend image runs as the dedicated non-root user nextjs (uid:gid 1001:1001). securityContext: # -- Allow privilege escalation within the container. allowPrivilegeEscalation: false @@ -54,17 +55,21 @@ front: drop: [ALL] # -- Run the container in privileged mode. privileged: false - # -- Enforce running the container as non-root. Leave `false` until the image is able to be run in rootless mode. - runAsNonRoot: false - # -- Run the container as the specified user id (uid). Leave `0` until the image is able to be run in rootless mode. - runAsUser: 0 - # -- Run the container as the specified group id (gid). Leave `0` until the image is able to be run in rootless mode. - runAsGroup: 0 + # -- Enforce running the container as non-root. + runAsNonRoot: true + # -- Run the container as the specified user id (uid). Frontend image bakes uid 1001 (nextjs). + runAsUser: 1001 + # -- Run the container as the specified group id (gid). Frontend image bakes gid 1001 (nodejs). + runAsGroup: 1001 seccompProfile: # -- The Seccomp profile to apply to this container. type: RuntimeDefault # -- Optional security context for pods. - podSecurityContext: {} + podSecurityContext: + runAsNonRoot: true + runAsUser: 1001 + runAsGroup: 1001 + fsGroup: 1001 automountServiceAccountToken: true # NodeSelector is a selector which must be true for the pod to fit on a node. @@ -86,7 +91,7 @@ backend: imageName: getplumber/backend # -- Image pull policy for the container. imagePullPolicy: Always - tag: v2.40.0 + tag: v2.40.1 replicaCount: 1 revisionHistoryLimit: 5 port: 3000 @@ -102,6 +107,7 @@ backend: serviceAccountName: "" extraIngressAnnotations: {} # -- Security context for backend containers. + # The backend image is distroless and runs as the rootless user nonroot (uid:gid 65532:65532). securityContext: # -- Allow privilege escalation within the container. allowPrivilegeEscalation: false @@ -110,17 +116,21 @@ backend: drop: [ALL] # -- Run the container in privileged mode. privileged: false - # -- Enforce running the container as non-root. Leave `false` until the image is able to be run in rootless mode. - runAsNonRoot: false - # -- Run the container as the specified user id (uid). Leave `0` until the image is able to be run in rootless mode. - runAsUser: 0 - # -- Run the container as the specified group id (gid). Leave `0` until the image is able to be run in rootless mode. - runAsGroup: 0 + # -- Enforce running the container as non-root. + runAsNonRoot: true + # -- Run the container as the specified user id (uid). Distroless bakes uid 65532 (nonroot). + runAsUser: 65532 + # -- Run the container as the specified group id (gid). Distroless bakes gid 65532 (nonroot). + runAsGroup: 65532 seccompProfile: # -- The Seccomp profile to apply to this container. type: RuntimeDefault # -- Optional security context for pods. - podSecurityContext: {} + podSecurityContext: + runAsNonRoot: true + runAsUser: 65532 + runAsGroup: 65532 + fsGroup: 65532 automountServiceAccountToken: true # NodeSelector is a selector which must be true for the pod to fit on a node. @@ -173,7 +183,7 @@ worker: imageName: getplumber/backend # -- Image pull policy for the container. imagePullPolicy: Always - tag: v2.40.0 + tag: v2.40.1 replicaCount: 5 revisionHistoryLimit: 5 args: @@ -189,6 +199,7 @@ worker: serviceAccountName: "" automountServiceAccountToken: true # -- Security context for worker containers. + # Worker uses the same distroless image as the backend (uid:gid 65532:65532). securityContext: # -- Allow privilege escalation within the container. allowPrivilegeEscalation: false @@ -197,17 +208,21 @@ worker: drop: [ALL] # -- Run the container in privileged mode. privileged: false - # -- Enforce running the container as non-root. Leave `false` until the image is able to be run in rootless mode. - runAsNonRoot: false - # -- Run the container as the specified user id (uid). Leave `0` until the image is able to be run in rootless mode. - runAsUser: 0 - # -- Run the container as the specified group id (gid). Leave `0` until the image is able to be run in rootless mode. - runAsGroup: 0 + # -- Enforce running the container as non-root. + runAsNonRoot: true + # -- Run the container as the specified user id (uid). Distroless bakes uid 65532 (nonroot). + runAsUser: 65532 + # -- Run the container as the specified group id (gid). Distroless bakes gid 65532 (nonroot). + runAsGroup: 65532 seccompProfile: # -- The Seccomp profile to apply to this container. type: RuntimeDefault # -- Optional security context for pods. - podSecurityContext: {} + podSecurityContext: + runAsNonRoot: true + runAsUser: 65532 + runAsGroup: 65532 + fsGroup: 65532 # NodeSelector is a selector which must be true for the pod to fit on a node. # Selector which must match a node's labels for the pod to be scheduled on that node. diff --git a/compose.yml b/compose.yml index 7967d52..d2fe909 100644 --- a/compose.yml +++ b/compose.yml @@ -3,9 +3,11 @@ name: "plumber" x-backend-common: &backend-common image: docker.io/getplumber/backend:${BACKEND_IMAGE_TAG} env_file: .env + # The backend runs as the rootless user uid:gid 65532:65532 (distroless nonroot) + # and therefore listens on a non-privileged port (>=1024). environment: - JOBS_LISTEN_ADDR=0.0.0.0 - - JOBS_LISTEN_PORT=80 + - JOBS_LISTEN_PORT=3000 - JOBS_CORS_ORIGIN=https://${DOMAIN_NAME} - JOBS_FRONTEND_URL=https://${DOMAIN_NAME} - JOBS_SESSION_TTL=168h @@ -70,13 +72,14 @@ services: backend: <<: *backend-common expose: - - "80" + - "3000" - "9090" labels: - "traefik.http.routers.api.rule=Host(`${DOMAIN_NAME}`)&&PathPrefix(`/api`)" - "traefik.http.routers.api.entrypoints=websecure" - "traefik.http.routers.api.tls=true" - "traefik.http.routers.api.tls.certresolver=${CERT_RESOLVER:-}" + - "traefik.http.services.api.loadbalancer.server.port=3000" worker: <<: *backend-common diff --git a/configmap.yml.example b/configmap.yml.example index 070b957..c3b4303 100644 --- a/configmap.yml.example +++ b/configmap.yml.example @@ -9,7 +9,9 @@ metadata: name: plumber-backend data: JOBS_LISTEN_ADDR: 0.0.0.0 - JOBS_LISTEN_PORT: 80 + # Use a non-privileged port (>=1024) so the backend can run rootless. + # Must match the containerPort in podman.yml.example. + JOBS_LISTEN_PORT: 3000 JOBS_CORS_ORIGIN: https://${DOMAIN_NAME} JOBS_FRONTEND_URL: https://${DOMAIN_NAME} JOBS_SESSION_TTL: 168h diff --git a/podman.local.yml.example b/podman.local.yml.example index b82cf6d..6282a16 100644 --- a/podman.local.yml.example +++ b/podman.local.yml.example @@ -91,9 +91,10 @@ spec: args: - "until echo 'Waiting for postgres...' && nc -vz -w 2 postgres 5432 && echo 'Waiting for redis...' && nc -vz -w 2 redis 6379; do echo 'Looping forever...'; sleep 2; done;" containers: + # The backend image is distroless and runs as the rootless user uid:gid 65532:65532. + # It listens on the non-privileged port 3000 so the pod can run rootless. - name: backend image: docker.io/getplumber/backend:${BACKEND_IMAGE_TAG} - command: ["/bin/sh", "-c", "sleep 10 && ./app"] ports: - containerPort: 3000 hostPort: 3001 @@ -137,9 +138,10 @@ spec: args: - "until echo 'Waiting for postgres...' && nc -vz -w 2 postgres 5432 && echo 'Waiting for redis...' && nc -vz -w 2 redis 6379; do echo 'Looping forever...'; sleep 2; done;" containers: + # Distroless image: invoke the binary directly with --worker (no shell available). - name: workers image: docker.io/getplumber/backend:${BACKEND_IMAGE_TAG} - command: ["/bin/sh", "-c", "sleep 15 && ./app --worker"] + args: ["--worker"] envFrom: - configMapRef: name: plumber-backend diff --git a/podman.yml.example b/podman.yml.example index c5dea64..56dabd8 100644 --- a/podman.yml.example +++ b/podman.yml.example @@ -92,6 +92,9 @@ metadata: traefik.http.routers.api.entrypoints: websecure traefik.http.routers.api.tls: true traefik.http.routers.api.tls.certresolver: le + # Pin Traefik to the API port; the pod also exposes 9090 for metrics, which + # Traefik must not pick by mistake. + traefik.http.services.api.loadbalancer.server.port: "3000" spec: initContainers: - name: wait-for-services @@ -104,9 +107,10 @@ spec: containers: - name: backend image: docker.io/getplumber/backend:${BACKEND_IMAGE_TAG} + # Backend listens on a non-privileged port (3000) so the image can run rootless. ports: - - containerPort: 80 - hostPort: 81 + - containerPort: 3000 + hostPort: 3001 - containerPort: 9090 envFrom: - configMapRef: @@ -150,9 +154,10 @@ spec: args: - "until echo 'Waiting for postgres...' && nc -vz -w 2 postgres 5432 && echo 'Waiting for redis...' && nc -vz -w 2 redis 6379; do echo 'Looping forever...'; sleep 2; done;" containers: + # Distroless image: invoke the binary directly with --worker (no shell available). - name: workers image: docker.io/getplumber/backend:${BACKEND_IMAGE_TAG} - args: ["--worker"] + args: ["--worker"] ports: - containerPort: 9090 envFrom: diff --git a/versions.env b/versions.env index 678157c..9f6c1b2 100644 --- a/versions.env +++ b/versions.env @@ -1,2 +1,2 @@ -FRONTEND_IMAGE_TAG=v2.34.6 -BACKEND_IMAGE_TAG=v2.40.0 +FRONTEND_IMAGE_TAG=v2.34.8 +BACKEND_IMAGE_TAG=v2.40.1