A disposable jail container that bind-mounts only one directory; the
bastion's FORCE_COMMAND drops every SSH session into an interactive shell
inside it. The jail's own root fs is throwaway image data, so the only host
data reachable over the session is the mounted directory. Documents the
docker-socket tradeoff and the read-only / no-socket hardened variants.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a second operating mode alongside FORCE_COMMAND: the client supplies
the command and it runs only if it matches a regex allowlist
(ALLOWED_COMMANDS / mounted allowed-commands.list), optionally behind a
trusted COMMAND_PREFIX. Matching is whole-line anchored (grep -Eqx) and
multi-line requests are rejected; execution is shell-free word-split, so
; | & $() are literal args and a sloppy rule can't become injection.
Works over SSH (SSH_ORIGINAL_COMMAND) and HTTP (X-Bastion-Command).
FORCE_COMMAND mode is unchanged and remains the default when no allowlist
is set; config is read from boot-written files since sshd does not pass
the daemon env to a ForceCommand session.
- scripts/bastion-broker: the allowlist gate + no-shell exec
- scripts/start-container: mode detection, broker wrapper + CGI, banner
- Dockerfile / config/sshd_config: wire in the broker, document env vars
- examples/docker-mailserver: ready-to-run broker config + allowlist