Sign Sunshine certs with a 1Password-backed root CA

Replaces Sunshine's self-signed cert with one minted from a private root CA
whose key material lives in 1Password. Every host running install.sh fetches
the CA via 'op read', mints itself a host cert with SANs for <hostname>.lan
and the current LAN IP, and installs the CA into the system trust store.

Bootstrap (run once, anywhere)
- scripts/cert-bootstrap.sh: generates a 4096-bit RSA root CA (10y validity),
  uploads it as a Secure Note titled "Omarchy-Stream Root CA" in the Private
  vault with two fields: cert (text) and key (concealed). Refuses to overwrite
  an existing item without --force.

Per-host (lib/certs.sh)
- fetch_and_install_certs: reads op://Private/Omarchy-Stream Root CA/{cert,key}
  to a tmpfs-staged temp dir (XDG_RUNTIME_DIR), mints a host cert via openssl
  with serverAuth + clientAuth EKU, drops cert/key at ~/.config/sunshine/
  credentials/{cacert,cakey}.pem, installs the CA at
  /etc/ca-certificates/trust-source/anchors/omarchy-stream-ca.pem and runs
  update-ca-trust.
- Idempotent: skips re-mint when on-disk cert is signed by the current CA,
  has the expected SANs, and isn't within 30 days of expiry. Override with
  FORCE_CERTS=1 or --force-certs.

install.sh
- Adds --no-certs, --force-certs flags; sources lib/certs.sh; runs cert step
  after permissions/config and before firewall so the service restart at the
  end of install picks up the new cert.

client/install-macos.sh
- After installing Moonlight, if `op` is available and signed in, fetches the
  CA and adds it as a trusted root to /Library/Keychains/System.keychain via
  `security add-trusted-cert -d -r trustRoot`. Skips cleanly when op isn't
  ready.

uninstall.sh
- Adds --remove-ca-trust to delete the system trust anchor. By default the
  CA is left in place since other tools may rely on it.

verify.sh
- Adds checks for: cert signed by omarchy-stream CA, cert >30 days from
  expiry, CA present in system trust store.

Docs
- README "Trusted TLS certs via 1Password" section: bootstrap flow, per-host
  flow, client trust matrix (Linux / macOS / iOS / Android / Apple TV),
  re-pairing note (first cert install on a host invalidates pinned Moonlight
  fingerprints), config env vars.
- client/README gains per-platform CA-trust install steps with concrete
  `op read` + platform-specific commands.
This commit is contained in:
2026-05-18 10:43:33 -06:00
parent 171ade4ff1
commit e878b392e4
8 changed files with 513 additions and 5 deletions

View File

@@ -101,6 +101,30 @@ verify_install() {
fi
fi
local sunshine_cert="$HOME/.config/sunshine/credentials/cacert.pem"
if [[ -f "$sunshine_cert" ]]; then
local issuer
issuer="$(openssl x509 -in "$sunshine_cert" -noout -issuer 2>/dev/null || true)"
if grep -qF 'omarchy-stream' <<<"$issuer"; then
ok "Sunshine cert is signed by the omarchy-stream CA"
if openssl x509 -in "$sunshine_cert" -checkend $((30 * 86400)) -noout >/dev/null 2>&1; then
ok "Sunshine cert valid for >30 days"
else
warn "Sunshine cert expires within 30 days — re-run install.sh to renew"
fi
else
info "Sunshine cert is Sunshine's self-signed default (no CA chain)"
fi
else
info "Sunshine cert not present yet (will be generated on first start)"
fi
if [[ -f /etc/ca-certificates/trust-source/anchors/omarchy-stream-ca.pem ]]; then
ok "omarchy-stream CA installed in system trust store"
else
info "omarchy-stream CA not in system trust store (only matters if --no-certs was used)"
fi
if systemctl --user is-active --quiet sunshine.service; then
ok "sunshine.service is active"
if ss -ltn 2>/dev/null | grep -q ':47990 '; then