Add SSH CA integration: signed user + host certs, ssh-login helper
Turn the ssh/ engine into an SSH CA for cert-based access: - ssh/ roles: "user" (8h user certs, principal-restricted) and "host" (long-lived host certs); mount max-lease-ttl raised for host certs - scripts/ssh-login.sh: sign a fresh user cert via a scoped ssh/sign/user token (API, no bao binary) and connect — no authorized_keys on targets - ca/openbao-ssh-ca.pub: the SSH CA public key (for TrustedUserCAKeys and client @cert-authority trust) - README: usage, host onboarding, client trust - gitignore generated per-host artifacts/ First host wired + verified end-to-end: 192.168.0.26 (pifour) — lutz cert login and host-cert verification both confirmed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
41
scripts/ssh-login.sh
Executable file
41
scripts/ssh-login.sh
Executable file
@@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env bash
|
||||
# Sign the local SSH key with OpenBAO's SSH CA (role "user"), then connect.
|
||||
# The cert is short-lived (8h) and refreshed on every call — no long-lived
|
||||
# authorized_keys on the target.
|
||||
#
|
||||
# ssh-login [user@]host [extra ssh args...] # sign + connect
|
||||
# ssh-login --sign-only [user@]host # just refresh the cert
|
||||
#
|
||||
# Auth to OpenBAO uses a scoped, periodic token (ssh/sign/user only).
|
||||
set -euo pipefail
|
||||
|
||||
ADDR="${BAO_ADDR:-http://127.0.0.1:8200}"
|
||||
TOKF="${OPENBAO_SSH_TOKEN_FILE:-$HOME/.config/openbao/ssh-sign.token}"
|
||||
ROLE="${OPENBAO_SSH_ROLE:-user}"
|
||||
KEY="${OPENBAO_SSH_KEY:-$HOME/.ssh/id_ed25519}"
|
||||
CERT="${KEY}-cert.pub"
|
||||
TTL="${OPENBAO_SSH_TTL:-8h}"
|
||||
|
||||
sign_only=0
|
||||
[ "${1:-}" = "--sign-only" ] && { sign_only=1; shift; }
|
||||
target="${1:?usage: ssh-login [--sign-only] [user@]host [ssh args]}"; shift || true
|
||||
|
||||
if [[ "$target" == *@* ]]; then principal="${target%@*}"; host="${target#*@}"
|
||||
else principal="${OPENBAO_SSH_USER:-lutz}"; host="$target"; fi
|
||||
|
||||
[ -r "$TOKF" ] || { echo "ssh-login: no OpenBAO token at $TOKF" >&2; exit 1; }
|
||||
[ -r "${KEY}.pub" ] || { echo "ssh-login: no public key at ${KEY}.pub" >&2; exit 1; }
|
||||
|
||||
req="$(PUB="$(cat "${KEY}.pub")" PRIN="$principal" TTL="$TTL" python3 -c \
|
||||
'import json,os;print(json.dumps({"public_key":os.environ["PUB"],"valid_principals":os.environ["PRIN"],"ttl":os.environ["TTL"]}))')"
|
||||
resp="$(printf '%s' "$req" | curl -sS --max-time 10 -H "X-Vault-Token: $(cat "$TOKF")" \
|
||||
--data @- "${ADDR}/v1/ssh/sign/${ROLE}")"
|
||||
signed="$(printf '%s' "$resp" | jq -r '.data.signed_key // empty')"
|
||||
[ -n "$signed" ] || { echo "ssh-login: signing failed: $(printf '%s' "$resp" | jq -c '.errors // .')" >&2; exit 1; }
|
||||
|
||||
printf '%s' "$signed" > "$CERT"; chmod 644 "$CERT"
|
||||
exp="$(ssh-keygen -L -f "$CERT" 2>/dev/null | awk '/Valid:/{ $1=""; print }')"
|
||||
echo "ssh-login: signed cert for '${principal}' (valid:${exp})" >&2
|
||||
|
||||
[ "$sign_only" = 1 ] && exit 0
|
||||
exec ssh -i "$KEY" -o CertificateFile="$CERT" "${principal}@${host}" "$@"
|
||||
Reference in New Issue
Block a user