Files
pi-extensions/scripts/issue-client-cert.sh
T
shahondin1624 e58c78d21c Add setup scripts for onboarding new machines
- scripts/install-client.sh: bootstraps a pi client — fetches certs from
  the Caddy host via scp, rsyncs the extensions into ~/.pi/agent/, sets
  up SSH key-auth to the ai-server for admin commands, probes the mTLS
  /health endpoint to verify.
- scripts/issue-client-cert.sh: run on the Caddy host to mint a new
  device identity — generates key + CSR, signs with the local root CA,
  and emits both a modern p12 (AES-256) and a -legacy p12 (3DES/RC2-40)
  for NSS-based browsers.
- scripts/install-browser-certs.sh: imports certs into Brave Flatpak's
  isolated NSS DB, ~/.pki/nssdb for packaged Chromium-family browsers,
  each Firefox profile, optionally the system trust store, and
  optionally drops a Brave AutoSelectCertificateForUrls policy so the
  cert prompt stops appearing on every page load.

All three are idempotent, --help-aware, and accept env/flag overrides
for the hardcoded defaults.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 21:26:08 +02:00

101 lines
3.6 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# issue-client-cert.sh — Generate a new mTLS client identity signed by the
# local root CA. Run this ON THE CADDY HOST (where root-ca.key lives).
#
# Produces four files in $CERT_DIR, keyed on a per-device name:
# client-<name>.crt client cert (for pi CLI, nginx config, etc.)
# client-<name>.key client private key
# client-<name>.p12 modern bundle (PBES2/AES-256) — CLI use only
# client-<name>-legacy.p12 legacy bundle (3DES/RC2-40) — for browser import
#
# Usage:
# scripts/issue-client-cert.sh <device-name> [--force]
# <device-name> alphanumerics + dashes/underscores (e.g. laptop-alice)
# --cert-dir PATH override $CERT_DIR (default /mnt/ssdpool/@docker/caddy/certs)
# --days N validity (default 3650)
# --force overwrite existing files for this name
# --help show this message
set -euo pipefail
CERT_DIR="${CERT_DIR:-/mnt/ssdpool/@docker/caddy/certs}"
DAYS="${DAYS:-3650}"
ORG="${ORG:-ShahODin}"
COUNTRY="${COUNTRY:-DE}"
FORCE=0
NAME=""
usage() { sed -n '2,/^$/p' "$0" | sed 's/^#\{0,1\} \{0,1\}//'; exit 0; }
while [[ $# -gt 0 ]]; do
case "$1" in
--cert-dir) CERT_DIR="$2"; shift 2 ;;
--days) DAYS="$2"; shift 2 ;;
--force) FORCE=1; shift ;;
-h|--help) usage ;;
-*) echo "Unknown arg: $1" >&2; exit 1 ;;
*)
if [[ -z "$NAME" ]]; then NAME="$1"; shift
else echo "Extra positional arg: $1" >&2; exit 1; fi ;;
esac
done
[[ -n "$NAME" ]] || { echo "Usage: $0 <device-name>" >&2; exit 1; }
[[ "$NAME" =~ ^[A-Za-z0-9_-]+$ ]] || { echo "Invalid name: $NAME (alphanumerics, - and _ only)" >&2; exit 1; }
[[ -d "$CERT_DIR" ]] || { echo "Cert dir not found: $CERT_DIR" >&2; exit 1; }
cd "$CERT_DIR"
[[ -f root-ca.key && -f root-ca.pem ]] || { echo "root-ca.key and root-ca.pem must exist in $CERT_DIR" >&2; exit 1; }
for f in "client-${NAME}.key" "client-${NAME}.crt" "client-${NAME}.p12" "client-${NAME}-legacy.p12"; do
if [[ -e "$f" && $FORCE -eq 0 ]]; then
echo "Refusing to overwrite $CERT_DIR/$f (use --force)" >&2; exit 1
fi
done
echo "==> Generating 4096-bit RSA key + CSR for '$NAME'"
openssl genrsa -out "client-${NAME}.key" 4096 2>/dev/null
chmod 600 "client-${NAME}.key"
openssl req -new -key "client-${NAME}.key" -out "client-${NAME}.csr" \
-subj "/CN=${ORG} Client ${NAME}/O=${ORG}/C=${COUNTRY}"
echo "==> Signing with root CA"
openssl x509 -req -in "client-${NAME}.csr" \
-CA root-ca.pem -CAkey root-ca.key -CAcreateserial \
-out "client-${NAME}.crt" -days "$DAYS" 2>/dev/null
rm -f "client-${NAME}.csr"
echo "==> Bundling PKCS#12 (modern + legacy)"
openssl pkcs12 -export \
-out "client-${NAME}.p12" \
-inkey "client-${NAME}.key" \
-in "client-${NAME}.crt" \
-certfile root-ca.pem \
-name "${ORG} Client (${NAME})" \
-passout pass:
chmod 600 "client-${NAME}.p12"
openssl pkcs12 -legacy -export \
-out "client-${NAME}-legacy.p12" \
-inkey "client-${NAME}.key" \
-in "client-${NAME}.crt" \
-certfile root-ca.pem \
-name "${ORG} Client (${NAME}, legacy)" \
-passout pass:
chmod 600 "client-${NAME}-legacy.p12"
echo
echo "==> Done. Files in $CERT_DIR:"
ls -la "client-${NAME}".* | sed 's/^/ /'
echo
echo "To install on a pi client (run there):"
echo " mkdir -p ~/.pi/agent/certs"
echo " scp $(whoami)@$(hostname -f):${CERT_DIR}/client-${NAME}.crt ~/.pi/agent/certs/client.pem"
echo " scp $(whoami)@$(hostname -f):${CERT_DIR}/client-${NAME}.key ~/.pi/agent/certs/client-key.pem"
echo " scp $(whoami)@$(hostname -f):${CERT_DIR}/root-ca.pem ~/.pi/agent/certs/"
echo
echo "To import in a browser, fetch client-${NAME}-legacy.p12 and pass it to"
echo "scripts/install-browser-certs.sh on the client."