harden(compose): scope host-gateway route to the web app only (audit finding)
A multi-agent security audit of this branch flagged that the new `extra_hosts: host.docker.internal:host-gateway` sat on the shared x-app/x-image anchor, so reverb/queue/scheduler inherited it too. Only the web `app` container serves the (admin-gated) "Clusev host" Docker view; the workers run background code with no role gate. Combined with the image's ability to decrypt the root HostCredential and open root SSH via HostCredential::toServer()+FleetService, every worker held a network path to a root shell on the host — defeating the manage-fleet gate that only exists in the HTTP layer. Not reachable as shipped (no job/scheduled code touches the host credential), but a least-privilege regression. Move extra_hosts off the anchor onto ONLY the `app` service (dev + prod). Verified: app resolves host.docker.internal, queue/scheduler do not. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>feat/v1-foundation
parent
4c67b278bd
commit
096ba3ca98
|
|
@ -32,14 +32,16 @@ x-image: &image
|
|||
security_opt:
|
||||
- "no-new-privileges:true"
|
||||
pids_limit: 1024
|
||||
# Reach the Docker host's sshd from the PHP layer so the "Clusev host" Docker view (and any other
|
||||
# host-targeted SSH) can talk to the real machine — same mapping the terminal sidecar uses.
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
|
||||
services:
|
||||
app:
|
||||
<<: *image
|
||||
# ONLY the web container serves the (admin-gated) "Clusev host" Docker view, so ONLY it gets a
|
||||
# route to the host gateway. Keeping it off the reverb/queue/scheduler workers (background code,
|
||||
# not role-gated HTTP) denies them a network path to a root shell on the host if compromised —
|
||||
# least privilege for the stored root HostCredential.
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
|
|
|
|||
|
|
@ -12,11 +12,6 @@ x-app: &app
|
|||
APP_GID: "${HOST_GID:-1002}"
|
||||
image: clusev-app:dev
|
||||
restart: unless-stopped
|
||||
# Reach the Docker host's sshd from the PHP layer so the "Clusev host" Docker view (and any
|
||||
# other host-targeted SSH) can talk to the real machine (host.docker.internal → host gateway),
|
||||
# the same mapping the terminal sidecar already uses.
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
volumes:
|
||||
- .:/var/www/html
|
||||
- ./run:/var/www/html/storage/app/restart-signal
|
||||
|
|
@ -34,6 +29,12 @@ services:
|
|||
ports:
|
||||
- "${APP_PORT:-80}:80"
|
||||
- "${VITE_PORT:-5173}:5173"
|
||||
# ONLY the web container serves the (admin-gated) "Clusev host" Docker view, so ONLY it gets a
|
||||
# route to the host gateway. Keeping it off the reverb/queue/scheduler workers (which run
|
||||
# background code, not role-gated HTTP) denies them a network path to a root shell on the host
|
||||
# if one were ever compromised — least privilege for the stored root HostCredential.
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
depends_on:
|
||||
mariadb:
|
||||
condition: service_healthy
|
||||
|
|
|
|||
Loading…
Reference in New Issue