Skip to content

Give the observability containers least privilege #443

Open

Description

@HMarzban

Summary

Two observability containers can reach the Docker socket, and the Postgres exporter holds the application's full database URL. A bug in any of them would give much more than monitoring needs: the Docker socket means root on the host, and the database URL means full read and write on application data.

  • Severity: Low (needs a bug in a monitoring container first)
  • Area: docker-compose.observability.yml
  • Source: security review of 2026-10-06, finding L17

Where

  • Alloy mounts the socket: docker-compose.observability.yml:26 (/var/run/docker.sock:/var/run/docker.sock:ro). :ro does not limit the Docker API.
  • cAdvisor mounts all of /var/run: docker-compose.observability.yml:67.
  • postgres-exporter uses the application URL: docker-compose.observability.yml:248 (DATA_SOURCE_NAME: ${DATABASE_URL}).
  • The compose comment near :62-64 calls these mounts read-only and safe, and cites docs/observability.md, which does not exist.

Fix plan

  1. Add a Docker socket proxy (for example tecnativa/docker-socket-proxy, pinned by digest like the other images) that allows only the read endpoints Alloy and cAdvisor use (containers, info). Point both at it, and remove their socket mounts.
  2. Create a pg_monitor login role for the exporter in a migration or the deploy setup, and give the exporter its own POSTGRES_EXPORTER_DSN env var.
  3. Correct the compose comment and remove the dead docs/observability.md reference.

Acceptance criteria

  • No observability container mounts /var/run/docker.sock or /var/run.
  • Container metrics and logs still arrive in Grafana.
  • postgres-exporter connects as a pg_monitor role, not the application role, and its metrics still arrive.

Verify

  • docker compose -f docker-compose.observability.yml config shows no socket mounts and the new DSN.
  • On the server: Grafana dashboards for containers and Postgres still update after deploy.

Activity

  1. added theissue type on Oct 6, 2026
  2. HMarzban commented on Oct 9, 2026

    @HMarzban
    CollaboratorAuthor

    Code is live (73e5bd972): Alloy reads the Docker API through docker-socket-proxy (GET only), and node-exporter no longer sees the host socket. postgres-exporter prefers POSTGRES_EXPORTER_DSN.

    Left before closing (maintainer): create a pg_monitor login role on the DigitalOcean cluster and set POSTGRES_EXPORTER_DSN in .env.observability. Keep DATABASE_URL there until a follow-up moves the deploy check.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    DevOpsSecuritySecurity, access control, and data exposure

    Type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions