cdae16562a
Typo in the issuer org for newly minted client certs. Existing certs are unaffected (Caddy validates against the root CA's public key, not subject text). Future certs issued via this script will carry the corrected O=Shahondin1624. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
101 lines
3.6 KiB
Bash
Executable File
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:-Shahondin1624}"
|
|
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."
|