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.
104 lines
4.4 KiB
YAML
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
|