# formulas/rent-a-human-caddy-ssh.toml — Provision SSH key for Caddy log collection # # "Rent a Human" — walk the operator through provisioning a purpose-limited # SSH keypair so collect-engagement.sh can fetch Caddy access logs remotely. # # The key uses a `command=` restriction so it can ONLY cat the access log. # No interactive shell, no port forwarding, no agent forwarding. # # Parent vision issue: #426 # Sprint: website-observability-wire-up (ops PR #10) # Consumed by: site/collect-engagement.sh (issue #745) name = "rent-a-human-caddy-ssh" description = "Provision a purpose-limited SSH keypair for remote Caddy log collection" version = 1 # ── Step 1: Generate keypair ───────────────────────────────────────────────── [[steps]] id = "generate-keypair" title = "Generate a dedicated ed25519 keypair" description = """ Generate a purpose-limited SSH keypair for Caddy log collection. Run on your local machine (NOT the Caddy host): ``` ssh-keygen -t ed25519 -f caddy-collect -N '' -C 'disinto-collect-engagement' ``` This produces two files: - caddy-collect (private key — goes into the vault) - caddy-collect.pub (public key — goes onto the Caddy host) Do NOT set a passphrase (-N '') — the factory runs unattended. """ # ── Step 2: Install public key on Caddy host ───────────────────────────────── [[steps]] id = "install-public-key" title = "Install the public key on the Caddy host with command= restriction" needs = ["generate-keypair"] description = """ Install the public key on the Caddy host with a strict command= restriction so this key can ONLY read the access log. 1. SSH into the Caddy host as the user who owns /var/log/caddy/access.log. 2. Open (or create) ~/.ssh/authorized_keys: mkdir -p ~/.ssh && chmod 700 ~/.ssh nano ~/.ssh/authorized_keys 3. Add this line (all on ONE line — do not wrap): command="cat /var/log/caddy/access.log",no-port-forwarding,no-X11-forwarding,no-agent-forwarding ssh-ed25519 AAAA... disinto-collect-engagement Replace "AAAA..." with the contents of caddy-collect.pub. To build the line automatically: echo "command=\"cat /var/log/caddy/access.log\",no-port-forwarding,no-X11-forwarding,no-agent-forwarding $(cat caddy-collect.pub)" 4. Set permissions: chmod 600 ~/.ssh/authorized_keys What the restrictions do: - command="cat /var/log/caddy/access.log" Forces this key to only execute `cat /var/log/caddy/access.log`, regardless of what the client requests. - no-port-forwarding — blocks SSH tunnels - no-X11-forwarding — blocks X11 - no-agent-forwarding — blocks agent forwarding If the access log is at a different path, update the command= restriction AND set CADDY_ACCESS_LOG in the factory environment to match. """ # ── Step 3: Add private key to vault secrets ───────────────────────────────── [[steps]] id = "store-private-key" title = "Add the private key as CADDY_SSH_KEY secret" needs = ["generate-keypair"] description = """ Store the private key in the factory's encrypted secrets store. 1. Add the private key using `disinto secrets add`: cat caddy-collect | disinto secrets add CADDY_SSH_KEY This encrypts the key with age and stores it as secrets/CADDY_SSH_KEY.enc. 2. IMPORTANT: After storing, securely delete the local private key file: shred -u caddy-collect 2>/dev/null || rm -f caddy-collect rm -f caddy-collect.pub The public key is already installed on the Caddy host; the private key now lives only in secrets/CADDY_SSH_KEY.enc. Never commit the private key to any git repository. """ # ── Step 4: Configure Caddy host address ───────────────────────────────────── [[steps]] id = "store-caddy-host" title = "Add the Caddy host details as secrets" needs = ["install-public-key"] description = """ Store the Caddy connection details so collect-engagement.sh knows where to SSH. 1. Add each value using `disinto secrets add`: echo 'disinto.ai' | disinto secrets add CADDY_SSH_HOST echo 'debian' | disinto secrets add CADDY_SSH_USER echo '/var/log/caddy/access.log' | disinto secrets add CADDY_ACCESS_LOG Replace values with the actual SSH host, user, and log path for your setup. """ # ── Step 5: Test the connection ────────────────────────────────────────────── [[steps]] id = "test-connection" title = "Verify the SSH key works and returns the access log" needs = ["install-public-key", "store-private-key", "store-caddy-host"] description = """ Test the end-to-end connection before the factory tries to use it. 1. From the factory host (or anywhere with the private key), run: ssh -i caddy-collect -o StrictHostKeyChecking=accept-new user@caddy-host Expected behavior: - Outputs the contents of /var/log/caddy/access.log - Disconnects immediately (command= restriction forces this) If you already shredded the local key, decode it from the vault: echo "$CADDY_SSH_KEY" | base64 -d > /tmp/caddy-collect-test chmod 600 /tmp/caddy-collect-test ssh -i /tmp/caddy-collect-test -o StrictHostKeyChecking=accept-new user@caddy-host rm -f /tmp/caddy-collect-test 2. Verify the output is Caddy structured JSON (one JSON object per line): ssh -i /tmp/caddy-collect-test user@caddy-host | head -1 | jq . You should see fields like: ts, request, status, duration. 3. If the connection fails: - Permission denied → check authorized_keys format (must be one line) - Connection refused → check sshd is running on the Caddy host - Empty output → check /var/log/caddy/access.log exists and is readable by the SSH user - "jq: error" → Caddy may be using Combined Log Format instead of structured JSON; check Caddy's log configuration 4. Once verified, the factory's collect-engagement.sh can use this key to fetch logs remotely via: ssh -i $CADDY_HOST """