From: Alpha Chen Date: Thu, 23 Apr 2026 23:57:07 +0000 (+0000) Subject: Add host-side reference configs for SSH dispatch X-Git-Url: http://quire.kejadlen.dev/?a=commitdiff_plain;h=3503b46ba7b6499a28155a9aa53c9aac9babb3da;p=quire.git Add host-side reference configs for SSH dispatch sshd_config Match block, quire-dispatch script with repo path validation and git-command allowlist, and a README covering setup steps. These are reference configs versioned with the repo — the dispatch script will be replaced by quire exec in step 2. Assisted-by: GLM-5.1 via pi --- diff --git a/docs/host/README.md b/docs/host/README.md new file mode 100644 index 0000000..9863c5d --- /dev/null +++ b/docs/host/README.md @@ -0,0 +1,52 @@ +# Host setup for quire + +Reference configs for dispatching SSH connections into the quire container. + +## Files + +- `sshd_config` — drop into `/etc/ssh/sshd_config.d/` on the host +- `quire-dispatch` — copy to `/usr/local/bin/quire-dispatch` and `chmod +x` + +## Setup + +1. Create the git user on the host: + + sudo useradd --system --create-home git + +2. Add your public key: + + sudo mkdir -p /home/git/.ssh + sudo cp ~/.ssh/id_ed25519.pub /home/git/.ssh/authorized_keys + sudo chown -R git:git /home/git/.ssh + sudo chmod 700 /home/git/.ssh + sudo chmod 600 /home/git/.ssh/authorized_keys + +3. Install the dispatch script: + + sudo cp quire-dispatch /usr/local/bin/quire-dispatch + sudo chmod +x /usr/local/bin/quire-dispatch + +4. Install the sshd config: + + sudo cp sshd_config /etc/ssh/sshd_config.d/quire.conf + sudo systemctl reload sshd + +5. Start the quire container: + + docker run -d --name quire-container quire + +6. Test: + + git clone git@localhost:foo.git /tmp/test-clone + +## Notes + +The dispatch script is the security boundary between the host and the container. +It only allows `git-receive-pack`, `git-upload-pack`, and `git-upload-archive`. +Repo paths are validated: no `..` traversal, must end in `.git`, no double slashes. + +When `quire exec` is built (step 2), the ForceCommand will change to: + + ForceCommand docker exec -i quire-container quire exec "$SSH_ORIGINAL_COMMAND" + +and this dispatch script will be replaced by the quire binary's own allowlist. diff --git a/docs/host/quire-dispatch b/docs/host/quire-dispatch new file mode 100644 index 0000000..f169014 --- /dev/null +++ b/docs/host/quire-dispatch @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +# /usr/local/bin/quire-dispatch +# +# Invoked by sshd ForceCommand for the git user. +# Parses $SSH_ORIGINAL_COMMAND to extract the repo path and git command, +# then dispatches into the quire container via docker exec. +# +# $SSH_ORIGINAL_COMMAND examples: +# git-receive-pack '/foo.git' +# git-upload-pack '/foo.git' +# git-upload-archive '/foo.git' + +set -euo pipefail + +# shellcheck disable=SC2154 +cmd="${SSH_ORIGINAL_COMMAND:-}" + +if [[ -z "$cmd" ]]; then + echo "quire: interactive sessions are not supported" >&2 + exit 1 +fi + +# Extract the git subcommand (git-receive-pack, git-upload-pack, etc). +git_cmd="${cmd%% *}" + +# Extract the repo path argument. Git sends it as a quoted string, +# e.g. git-receive-pack '/foo.git'. Strip the leading command and +# surrounding quotes. +repo="${cmd#* }" +repo="${repo#\'}" +repo="${repo%\'}" +repo="${repo#/}" + +case "$git_cmd" in + git-receive-pack|git-upload-pack|git-upload-archive) + ;; + *) + echo "quire: unsupported command: $git_cmd" >&2 + exit 1 + ;; +esac + +# Validate repo path: no .., must end in .git, no double slashes. +if [[ "$repo" == *".."* ]] || [[ "$repo" != *.git ]] || [[ "$repo" == *//* ]]; then + echo "quire: invalid repository: $repo" >&2 + exit 1 +fi + +exec docker exec -i quire-container \ + /bin/sh -c "cd /var/quire/repos/${repo} && ${git_cmd} ." diff --git a/docs/host/sshd_config b/docs/host/sshd_config new file mode 100644 index 0000000..cefd73d --- /dev/null +++ b/docs/host/sshd_config @@ -0,0 +1,14 @@ +# /etc/ssh/sshd_config.d/quire.conf +# +# Drop this file into sshd_config.d (or append to sshd_config). +# Forces all connections as the git user through the quire dispatch script. +# No port forwarding, agent forwarding, X11, or PTY — these are git/quire +# connections, not interactive shells. + +Match User git + AuthorizedKeysFile /home/git/.ssh/authorized_keys + ForceCommand /usr/local/bin/quire-dispatch + AllowTcpForwarding no + AllowAgentForwarding no + X11Forwarding no + PermitTTY no