Files
bucketeer/docker-compose.yml
T
StasonJatham e8f7d6bfdf ctcloud: CertSpotter-backed CT cloud-resource monitor
Single stateless Go process that consumes CT logs via embedded (patched)
CertSpotter internals, classifies cloud/database/storage DNS identifiers
in-process with a zero-allocation classifier, enriches via round-robin DNS
resolvers with CNAME following, and batch-writes useful findings to PostgreSQL.

- third_party/certspotter: 2-hunk IdentifierSink patch, no stdout/JSON/files
- internal/classify: AWS/Azure/GCP/Firebase/Cloudflare R2/DigitalOcean/Supabase/OCI
- internal/dns: sharded bounded TTL cache, multi-resolver load balancing + failover
- internal/pipeline: bounded queues, dedup, direct-priority load shedding
- internal/store: pgx batch writer, Postgres CT checkpoints (no cert contents)
- internal/probe: optional non-destructive reachability checks (off by default)
- Docker: distroless non-root, read-only rootfs, no app volume, resource limits
- tests enforce 0 allocs/op on the classifier and sink hot path
2026-09-22 13:13:10 +02:00

67 lines
2.1 KiB
YAML

# Local / single-host deployment.
#
# Secrets come from .env (gitignored). Copy .env.example to .env and set a
# strong POSTGRES_PASSWORD plus a matching DATABASE_URL before `docker compose
# up`. For a production database, point DATABASE_URL at a TLS-enabled endpoint,
# e.g. postgres://user:[email protected]:5432/ctcloud?sslmode=verify-full
#
# The compose bridge network is private; sslmode=disable is only acceptable here
# because the database is not reachable from outside the stack.
services:
postgres:
image: postgres:17-alpine
environment:
POSTGRES_USER: ${POSTGRES_USER:-ctcloud}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}
POSTGRES_DB: ${POSTGRES_DB:-ctcloud}
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-ctcloud} -d ${POSTGRES_DB:-ctcloud}"]
interval: 5s
timeout: 5s
retries: 12
mem_limit: 1g
cpus: 2.0
pids_limit: 256
logging:
driver: json-file
options: { max-size: "10m", max-file: "3" }
restart: unless-stopped
ctcloud:
build: .
depends_on:
postgres:
condition: service_healthy
environment:
DATABASE_URL: ${DATABASE_URL:?set DATABASE_URL in .env}
METRICS_ADDR: ":9090"
LOG_LEVEL: ${LOG_LEVEL:-info}
DNS_SERVERS: ${DNS_SERVERS:-1.1.1.1:53,8.8.8.8:53,1.0.0.1:53}
DNS_WORKERS: ${DNS_WORKERS:-16}
ACTIVE_PROBING: ${ACTIVE_PROBING:-false}
# START_AT_END: "true" # skip historical certificates on first run
# Metrics/health are bound to loopback only: scrape from the host or a
# sidecar, never expose them publicly (there is no auth on this listener).
ports:
- "127.0.0.1:9090:9090"
# No application volume: the container is stateless apart from PostgreSQL.
read_only: true
tmpfs:
- /tmp:size=16m,noexec,nosuid
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
mem_limit: 1g
cpus: 2.0
pids_limit: 512
logging:
driver: json-file
options: { max-size: "10m", max-file: "3" }
restart: unless-stopped
volumes:
pgdata: