Adds a pull-based cert distribution path so install-lab-host.sh can fetch its own leaf cert without operator intervention. Removes the ssh-from-Pi requirement that blocked elliott-lab. How the chicken-and-egg gets solved: a freshly wg-enrolled lab host already has WG access (gate kept by iptmonads at L4) and trusts the Caddy local CA (bundled in this repo at etc/caddy-root.crt). It makes a single TLS call to https://bootstrap.wg/v1/cert/<host_id> — no mTLS — gets back a tar of {ca.crt, leaf.pem, leaf.key}, extracts to /etc/cis490/certs/, and the shipper unblocks. Trust boundary is "reached :443 over WG"; no operator action needed. bootstrap/ app.py Starlette: GET /v1/cert/{host_id}, GET /v1/health. Validates host_id charset, rate-limits per source IP, logs every mint with the X-Real-IP Caddy injects. __main__.py uvicorn launcher; runs as root because the wg-pki CA private key is root-only. etc/cis490-bootstrap.service systemd unit on 127.0.0.1:8446 with ProtectSystem=strict + narrow ReadWritePaths=/var/lib/wg-pki. ProtectHome=no because systemd's read-only mode hides /home contents (the issuer script the wrapper exec's lives there). scripts/issue-cis490-client-cert-wrapper.sh Adapter the bootstrap service shells out to. Resolves the actual wg-pki issuer script across the three plausible install layouts (/opt/wg-pki, /home/max/wg-pki, /home/max/.env/wg-pki) so a single copy of the unit file works on any operator's box. Forces --out-dir to /var/lib/wg-pki/issued so writes stay inside the service's narrow ReadWritePaths. scripts/install-lab-host.sh After scaffolding lab-host.toml, if /etc/cis490/certs/lab-host.pem is absent, curls bootstrap.wg with --cacert etc/caddy-root.crt (no chicken-and-egg), extracts, chowns/chmods. Skips silently if bootstrap.wg is unreachable so manual hand-carry remains possible. scripts/install-receiver.sh Drops cis490-bootstrap.service alongside cis490-receiver and prints both as "enable --now" candidates. cis490-bootstrap is the thing that makes lab hosts self-provisioning. etc/caddy-root.crt Bundled copy of wg-pki's published Caddy local CA root, so the bootstrap fetch can verify TLS without depending on a wg-pki clone that may or may not be on the lab host yet. Verified live on the Pi: $ curl --cacert etc/caddy-root.crt https://bootstrap.wg/v1/cert/elliott-lab -o /tmp/x.tar HTTP 200 size=10240 $ tar tf /tmp/x.tar ca.crt elliott-lab.key elliott-lab.pem $ openssl verify -CAfile … elliott-lab.pem /tmp/.../elliott-lab.pem: OK $ openssl x509 -subject … -noout subject=CN=elliott-lab Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
50 lines
1.7 KiB
Bash
Executable file
50 lines
1.7 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Wrapper that re-points the wg-pki issuer script's relative-path
|
|
# assumption (PWD-derived publish dir, $REPO_ROOT/issued/) to the
|
|
# absolute /var/lib/wg-pki/issued/ that the bootstrap service uses.
|
|
#
|
|
# wg-pki ships the actual issuer at
|
|
# /home/max/.env/wg-pki/scripts/issue-cis490-client-cert.sh, which
|
|
# computes paths relative to its own location. This wrapper sets
|
|
# WG_PKI_STATE so the CA key is found in /var/lib/wg-pki, and forces
|
|
# --out-dir to a path under /var/lib so cis490-bootstrap (with
|
|
# ProtectHome=tmpfs) can write the resulting tarballs.
|
|
|
|
set -euo pipefail
|
|
|
|
# Resolve issuer path: prefer the install-time copy at /opt/wg-pki/,
|
|
# fall back to whatever wg-pki clone the operator has under /home.
|
|
ISSUER="${WG_PKI_ISSUER:-}"
|
|
if [[ -z "$ISSUER" ]]; then
|
|
for cand in \
|
|
/opt/wg-pki/scripts/issue-cis490-client-cert.sh \
|
|
/home/max/wg-pki/scripts/issue-cis490-client-cert.sh \
|
|
/home/max/.env/wg-pki/scripts/issue-cis490-client-cert.sh; do
|
|
if [[ -x "$cand" ]]; then ISSUER="$cand"; break; fi
|
|
done
|
|
fi
|
|
if [[ -z "$ISSUER" || ! -x "$ISSUER" ]]; then
|
|
echo "wrapper: no issue-cis490-client-cert.sh found; tried /opt/wg-pki, /home/max/wg-pki, /home/max/.env/wg-pki" >&2
|
|
exit 2
|
|
fi
|
|
OUT_ROOT="/var/lib/wg-pki/issued"
|
|
|
|
if [[ $# -lt 1 ]]; then
|
|
echo "usage: $0 <host_id> [--out-dir DIR] [--days N]" >&2
|
|
exit 2
|
|
fi
|
|
|
|
HOST_ID="$1"; shift
|
|
|
|
# Pull off any --out-dir already passed; we override.
|
|
EXTRA=()
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--out-dir) shift 2 ;; # drop, we set it ourselves
|
|
*) EXTRA+=("$1"); shift ;;
|
|
esac
|
|
done
|
|
|
|
mkdir -p "$OUT_ROOT/$HOST_ID"
|
|
exec env WG_PKI_STATE=/var/lib/wg-pki \
|
|
"$ISSUER" "$HOST_ID" --out-dir "$OUT_ROOT/$HOST_ID" "${EXTRA[@]}"
|