Files
homelab/scripts/trust-letsencrypt-staging.sh
T
k.rennerandClaude Opus 4.7 097197c412 chore: remove orphan manifests, harden CI fail-paths, drop stale CLAUDE.md
- Delete kubernetes/apps/authentik/postgres.yaml: orphan CNPG Cluster with
  bootstrap.initdb and no plugin declaration; would compete with the
  working ak-pg in authentik-db/ if accidentally applied.
- Delete kubernetes/apps/owncloud/: subtree never wired into Flux (no
  Kustomization in clusters/homelab/ targets it), would not deploy.
- Drop CLAUDE.md: superseded by ~/.claude/CLAUDE.md global instructions.
- rebuild-k8s.yml: track per-module retry failures and exit 1 if any
  module didn't recover; add set -e to disk-wipe step. Stops masking.
- import-cluster-access.sh: drop "|| true" on git checkout so missing
  access files surface immediately instead of silently continuing.
- Add scripts/trust-letsencrypt-staging.sh: helper to install Let's
  Encrypt staging roots into system or Firefox trust stores.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 18:37:04 +02:00

171 lines
4.8 KiB
Bash
Executable File

#!/usr/bin/env bash
# Install Let's Encrypt staging root CAs into a trust store so tools
# accept staging-issued certificates. Idempotent.
#
# Usage:
# scripts/trust-letsencrypt-staging.sh [install|remove] [--target system|firefox|all]
#
# Targets:
# system — /etc/ca-certificates/trust-source/anchors (Arch/Manjaro)
# or /usr/local/share/ca-certificates (Debian/Ubuntu).
# Trusted by curl, openssl, wget, Python, Go. Needs sudo.
# firefox — every Firefox profile under ~/.mozilla/firefox/*/cert9.db
# plus Flatpak/Snap profiles. User-level; do NOT run as root.
# all — both. Invoke twice (with sudo for system, as user for firefox).
set -euo pipefail
ROOTS=(
"https://letsencrypt.org/certs/staging/letsencrypt-stg-root-x1.pem"
"https://letsencrypt.org/certs/staging/letsencrypt-stg-root-x2.pem"
)
cmd=install
target=system
while [[ $# -gt 0 ]]; do
case "$1" in
install|remove) cmd=$1; shift ;;
--target) target=${2:-}; shift 2 ;;
--target=*) target=${1#*=}; shift ;;
-h|--help) sed -n '2,14p' "$0"; exit 0 ;;
*) echo "unknown arg: $1" >&2; exit 2 ;;
esac
done
[[ "$target" =~ ^(system|firefox|all)$ ]] || { echo "invalid --target: $target" >&2; exit 2; }
tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' EXIT
# Download once; used by both targets.
download_roots() {
for url in "${ROOTS[@]}"; do
name=$(basename "$url" .pem)
if [[ ! -s "$tmpdir/${name}.pem" ]]; then
curl -fsSL "$url" -o "$tmpdir/${name}.pem"
fi
done
}
run_system() {
if [[ $EUID -ne 0 ]]; then
echo "target=system: must run as root (writes to /etc)" >&2
exit 1
fi
if [[ -d /etc/ca-certificates/trust-source/anchors ]]; then
anchor_dir=/etc/ca-certificates/trust-source/anchors
ext=.pem
update=(update-ca-trust extract)
elif [[ -d /usr/local/share/ca-certificates ]]; then
anchor_dir=/usr/local/share/ca-certificates
ext=.crt
update=(update-ca-certificates)
else
echo "no known CA anchor directory" >&2
exit 1
fi
case "$cmd" in
install)
download_roots
changed=0
for url in "${ROOTS[@]}"; do
name=$(basename "$url" .pem)
dest="$anchor_dir/${name}${ext}"
if [[ -f "$dest" ]]; then
echo "system: already installed $dest"
continue
fi
cp "$tmpdir/${name}.pem" "$dest"
chmod 644 "$dest"
echo "system: installed $dest"
changed=1
done
[[ $changed -eq 1 ]] && "${update[@]}"
;;
remove)
changed=0
for url in "${ROOTS[@]}"; do
name=$(basename "$url" .pem)
dest="$anchor_dir/${name}${ext}"
[[ -f "$dest" ]] || continue
rm -f "$dest"
echo "system: removed $dest"
changed=1
done
[[ $changed -eq 1 ]] && "${update[@]}" || echo "system: nothing to remove"
;;
esac
}
run_firefox() {
if [[ $EUID -eq 0 ]]; then
echo "target=firefox: do NOT run as root (needs to write to user's profile)" >&2
exit 1
fi
command -v certutil >/dev/null || {
echo "certutil not found — install the 'nss' package (Arch: pacman -S nss)" >&2
exit 1
}
# Find every Firefox profile (directory containing cert9.db) across
# known Firefox install flavors: native, XDG-config location some
# distro builds use, Flatpak, and Snap.
search_dirs=(
"$HOME/.mozilla/firefox"
"$HOME/.config/mozilla/firefox"
"$HOME/.var/app/org.mozilla.firefox/.mozilla/firefox"
"$HOME/snap/firefox/common/.mozilla/firefox"
)
existing=()
for d in "${search_dirs[@]}"; do [[ -d "$d" ]] && existing+=("$d"); done
if [[ ${#existing[@]} -eq 0 ]]; then
echo "firefox: no Firefox config directory found in any of:" >&2
printf " %s\n" "${search_dirs[@]}" >&2
exit 1
fi
mapfile -t profiles < <(
find "${existing[@]}" -maxdepth 2 -type f -name cert9.db 2>/dev/null \
| xargs -r -n1 dirname
)
if [[ ${#profiles[@]} -eq 0 ]]; then
echo "firefox: no profiles with cert9.db found" >&2
exit 1
fi
[[ "$cmd" == "install" ]] && download_roots
for prof in "${profiles[@]}"; do
echo "firefox: profile $prof"
for url in "${ROOTS[@]}"; do
name=$(basename "$url" .pem)
case "$cmd" in
install)
if certutil -d "sql:$prof" -L -n "$name" >/dev/null 2>&1; then
echo " already installed: $name"
continue
fi
certutil -d "sql:$prof" -A -t "C,," -n "$name" -i "$tmpdir/${name}.pem"
echo " installed: $name"
;;
remove)
if certutil -d "sql:$prof" -L -n "$name" >/dev/null 2>&1; then
certutil -d "sql:$prof" -D -n "$name"
echo " removed: $name"
fi
;;
esac
done
done
echo "firefox: restart Firefox to pick up changes"
}
case "$target" in
system) run_system ;;
firefox) run_firefox ;;
all) run_system; run_firefox ;;
esac