Commit Graph

5 Commits

Author SHA1 Message Date
Fabian @ Blax Software 0262f677c1 A users.d/ drop-in directory for live-read authorized keys
sshd now consults /etc/bastion/users.d/*.pub on every authentication
attempt via AuthorizedKeysCommand, so adding or removing a user
takes effect immediately without restarting the container — just
drop `alice.pub` (or any *.pub file) into the host-bound dir,
sshd picks it up on the next login.

Implementation:
- /usr/local/bin/bastion-list-keys: minimal POSIX-sh script that
  cats $AUTHORIZED_KEYS_DIR/*.pub. Runs as the agent user (per
  AuthorizedKeysCommandUser), reads world-readable pubkeys.
- sshd_config: AuthorizedKeysCommand alongside the existing
  AuthorizedKeysFile — both checked, so the boot-merged
  file (AUTHORIZED_KEYS_HOST/_REPO) still works for single-file UX.
- start-container: 'zero key sources' is now a WARN, not a fatal.
  Bastion comes up empty; SSH attempts fail with 'publickey denied'
  until you drop a key. Lets users `docker compose up` first and
  add keys later.

Bug fix on the way through: `grep -c` exits non-zero when no
lines match, which under `set -eu` killed the boot script
silently after '[2/5] Authorized keys...'. Switched to
`awk … | wc -l` which exits 0 cleanly on empty input.

README updated with the new source priority and env var.
2026-05-28 12:31:51 +02:00
Fabian @ Blax Software 58492c80ec A HTTP basic auth + git/ssh tooling for deploy.sh-shape FORCE_COMMANDs
HTTP basic auth via HTTP_BASIC_AUTH=user:password. Mutually
exclusive with HTTP_TOKEN (basic takes precedence if both set).

Implementation note: busybox httpd strips Authorization: Basic
headers before invoking CGI scripts (it expects to handle basic
auth itself), so a CGI-side check for Basic doesn't work. We let
busybox handle Basic via its -c httpd.conf rule (`/cgi-bin/:user:pass`)
and keep the CGI-side Bearer check for HTTP_TOKEN. httpd.conf is
chowned to the agent user because httpd drops privileges before
reading -c.

Image additions for canonical deploy.sh patterns:
- git (apk add git) — for git pull/tag/push.
- openssh-client (apk add openssh-client) — provides /usr/bin/ssh,
  which git invokes for ssh:// remote transports. Without it
  `git push origin` fails with 'error: cannot run ssh: No such
  file or directory'.
- HOME=/home/agent exported in the force-command wrapper — busybox
  httpd doesn't set HOME for CGI, leaving git/ssh/xdg lookups
  pointing at /root and producing 'Permission denied' warnings.

README updated with HTTP_BASIC_AUTH env var, URL syntax examples,
and the mutual-exclusion note.
2026-05-28 12:11:20 +02:00
Fabian @ Blax Software 8c5b89b5af I unlock agent account + use /bin/sh shell so ForceCommand actually fires
Two OpenSSH-on-alpine quirks caught by a real ssh attempt:

1) alpine's `adduser -D` leaves shadow password as `!`, which
   OpenSSH 9.x treats as 'account locked' and refuses even for
   pubkey auth (logs: 'User agent not allowed because account is
   locked'). Sed-replace `!` with `*` post-create — no password
   set, but account NOT locked.

2) Setting the login shell to /sbin/nologin defeats ForceCommand,
   because sshd executes the forced command as
   `<login-shell> -c "<command>"`. nologin then prints
   'This account is not available' and exits. Use /bin/sh instead;
   the security boundary is ForceCommand + sshd_config, not the
   shell — clients cannot bypass ForceCommand to ask for an
   interactive shell.

README security section updated to reflect both points.
2026-05-28 11:35:54 +02:00
Fabian @ Blax Software 74b3983ff4 A optional HTTP listener + polished README
- HTTP path: opt-in via $HTTP_TOKEN; busybox httpd binds $HTTP_PORT
  (default 8080) and serves /cgi-bin/run, which validates the
  'Authorization: Bearer …' header and exec's the same force-command
  wrapper SSH uses. Output streams chunked. GET and POST both work.
  Without HTTP_TOKEN the bastion stays SSH-only.

- README rewritten with shields.io badges, two complete quickstart
  examples (WordPress drop-in + nginx config-reload webhook), inline
  comments on every yaml line marking required/optional, traefik
  integration in both examples, and star-history footer matching
  Blax OSS convention.

- Dockerfile: add busybox-extras (the httpd applet was split out of
  the core busybox binary in alpine 3.21); EXPOSE 8080; document
  HTTP_TOKEN/HTTP_PORT env vars.
2026-05-28 11:25:34 +02:00
Fabian @ Blax Software 86b8966130 A initial docker-bastion image
Minimal SSH bastion (alpine + openssh-server + docker-cli) that
authenticates by key and runs exactly one preconfigured command
(FORCE_COMMAND) per session. authorized_keys can be merged from
both a host-mounted source and a repo-mounted source. Host keys
persist via /etc/ssh/keys volume; docker socket group membership
is aligned at boot.
2026-05-28 10:50:06 +02:00