#!/bin/bash # mcp-reader-shell — restricted SSH command for the homelab-mcp service. # # Authorized in /root/.ssh/authorized_keys on hubris via: # command="/usr/local/bin/mcp-reader-shell",restrict ssh-ed25519 AAAA... mcp-reader@homelab-mcp # # `restrict` disables PTY/agent/forwarding/X11. This wrapper then validates # $SSH_ORIGINAL_COMMAND against a strict read-only allowlist before running # it. Anything outside the allowlist (interactive shell, file writes, pct # start/stop/destroy, etc.) is refused. # # Distributed via the homelab-context sync — symlink: # /usr/local/bin/mcp-reader-shell -> /opt/homelab-context/mcp/mcp-reader-shell # so updates land on the next 5-min pull without a manual re-install. # # Argument shapes allowed (Bash glob, after rejecting shell metacharacters): # systemctl is-active # systemctl is-enabled # journalctl -u [-n N] [--no-pager] # pct list # pct status # pct config # pct exec -- systemctl is-active # pct exec -- systemctl is-enabled # pct exec -- journalctl -u [-n N] [--no-pager] # # Logs each call to syslog via `logger`. Deny entries are warnings. set -euo pipefail set -f # disable glob expansion when we exec the command CMD="${SSH_ORIGINAL_COMMAND:-}" deny() { logger -t mcp-reader -p auth.warning "DENY from=${SSH_CLIENT:-?}: ${CMD:-}" echo "mcp-reader: command not allowed" >&2 exit 1 } if [ -z "$CMD" ]; then deny fi # Reject any shell metacharacter that would let an attacker chain or escape # from the patterns below. if [[ "$CMD" =~ [\;\&\|\>\<\`\$\\\(\)\{\}\*\?\~\!] ]]; then deny fi case "$CMD" in "systemctl is-active "*|"systemctl is-enabled "*) ;; "journalctl -u "*) ;; "pct list") ;; "pct status "*|"pct config "*) ;; "pct exec "*" -- systemctl is-active "*) ;; "pct exec "*" -- systemctl is-enabled "*) ;; "pct exec "*" -- journalctl -u "*) ;; *) deny ;; esac logger -t mcp-reader -p auth.info "ALLOW from=${SSH_CLIENT:-?}: $CMD" exec $CMD