Files
dejima-guard/docker-compose.example.yml
branderson 9c71beeea2 Say which machine each setting is about, and stop defaulting to root
Two reports from setting this up, and the second is a real defect rather
than a documentation one.

DEJIMA_GUARD_HOST READ AS THE HOST THE GUARD RUNS ON. It meant the one it
watches - the wrong end of the only connection this program makes. A name
that needs a comment to be read correctly is the wrong name, so it is
devbox_host and devbox_user now, under a heading that says everything in that
group describes the machine at the other end.

AND devbox_user DEFAULTED TO ROOT, on a machine whose own runbook builds it
with no root password and expects sudo from an ordinary account. The default
was wrong for every devbox built the documented way. There is no default now:
falling back to nothing would have ssh use the local username, which in a
container is an account that does not exist over there, so it says which is
missing instead.

Every setting is documented twice over: a table in the README saying what
goes in it, and etc/config.example with the reasoning attached. The compose
example carries a comment per variable rather than a bare list, since that is
the file somebody actually edits.

Three tests worth having beyond the rename: that no setting is named so it
could mean this machine, that a missing devbox_user is refused rather than
guessed, and that the two SSH commands still match the strings
dejima-devbox-hardener's dejima-guard-shell whitelists - one contract across
two repositories, and the failure if it drifts is a collection that fails and
gets reported as the devbox being unreachable.

`bats test/` 26 passing, 3 new.
2026-08-28 16:44:12 -07:00

104 lines
4.4 KiB
YAML

# The guard, as one service.
#
# AN EXAMPLE, not a compose file. Copy it to docker-compose.yml, edit it there,
# and start that - the copy is gitignored so your host and your URLs do not end
# up in a commit.
#
# Do not use it as an override. `docker compose up` reads docker-compose.yml
# and docker-compose.override.yml together and merges them, so an override
# leaves these example values in play rather than replacing them.
#
# As written it will not start: the host below is a placeholder the guard
# refuses, so this can never become a live config by being copied and
# forgotten.
services:
dejima-guard:
build: .
# image: gitbox.example.invalid/dejima/dejima-guard:latest
container_name: dejima-guard
restart: unless-stopped
environment:
# --- the machine being WATCHED, not the one this runs on ------------
#
# The devbox's hostname or address, as this container reaches it.
# Replace the placeholder: the guard refuses to start on CHANGE-ME.
DEJIMA_GUARD_DEVBOX_HOST: CHANGE-ME.example.invalid
# The account ON THE DEVBOX to log in as. Not root, and there is no
# default: the devbox runbook builds a machine with no root password
# and expects sudo from an ordinary account. The guard connects as that
# account and elevates for the two commands it runs.
DEJIMA_GUARD_DEVBOX_USER: your-devbox-account
# The private key, inside this container, to connect with. Mounted as a
# secret below rather than baked into the image.
DEJIMA_GUARD_SSH_KEY: /run/secrets/guard_key
# Where the devbox's host key is remembered. On the state volume,
# because a container filesystem is new every run - and without it,
# every collection fails host-key verification and reports the devbox
# unreachable. Already set in the image; here so it is visible.
DEJIMA_GUARD_SSH_KNOWN_HOSTS: /var/lib/dejima-guard/known_hosts
# --- where alerts go -----------------------------------------------
#
# Its OWN ntfy topic, not the one dejima pushes to. That URL lives on
# the devbox, so root there can publish to it: a compromised box could
# drown the topic or forge an all-clear. The token for this one goes in
# the mounted config, not here.
DEJIMA_GUARD_NTFY_URL: https://ntfy.example.invalid/devbox-alerts
# --- the dead-man's switch ------------------------------------------
#
# GET after a cycle that completed clean - an Uptime Kuma push monitor
# is the documented shape. Without it, a guard that dies looks exactly
# like a guard with nothing to report.
DEJIMA_GUARD_HEARTBEAT_URL: https://kuma.example.invalid/api/push/abcdef
# --- this container --------------------------------------------------
#
# Seconds between collections. Sets the dead-man's switch granularity
# and how long an intruder has to notice the monitoring and scrub.
DEJIMA_GUARD_INTERVAL: "900"
# Anything secret goes in the config file rather than here. Everything
# above is visible in `docker inspect` and to anything that can read
# /proc; a mounted secret is not. The guard reads the environment first
# and the file second, so this is the value that overrides.
DEJIMA_GUARD_CONF: /run/secrets/guard_config
# Mounted at /run/secrets/<name>, which is why CONF points there rather
# than at /etc. A `target:` naming an absolute path is a swarm feature and
# does not do what it looks like it does under plain `docker compose`.
secrets:
- guard_key
- guard_config
volumes:
# The baseline, the last report, and known_hosts. Losing this is not a
# clean slate: it is a guard that compares clean against everything and
# cannot verify the host it connects to.
- guard-state:/var/lib/dejima-guard
# It opens two outbound connections and writes one directory. Nothing here
# is a hardship, and this container holds an SSH key to a machine whose
# threat model says it may be owned.
read_only: true
tmpfs:
- /tmp
cap_drop: [ALL]
security_opt:
- no-new-privileges:true
# NO PORTS. Not "none needed today" - the reason a container is an
# acceptable home for this is that nothing can connect to it.
volumes:
guard-state:
secrets:
guard_key:
file: ./secrets/guard_key # mode 0600, the private key
guard_config:
file: ./secrets/config # ntfy_token, and anything else secret