#!/usr/bin/env bash # cyclone-init.sh — first-time host bootstrap for a production Cyclone deploy. # # Generates /etc/cyclone/secrets/{db.key,admin_username,admin_pw} with # openssl rand -hex 32, sets ownership + permissions, and prints the # admin password ONCE for the operator to capture. # # Idempotent: refuses to overwrite existing secrets unless --force. # # Usage: # bash scripts/cyclone-init.sh # generate if missing # bash scripts/cyclone-init.sh --force # overwrite (destroys old data!) set -euo pipefail SECRETS_DIR="${CYCLONE_SECRETS_DIR:-/etc/cyclone/secrets}" FORCE=0 [[ "${1:-}" == "--force" ]] && FORCE=1 if [[ $EUID -ne 0 ]]; then echo "ERROR: cyclone-init.sh must run as root (it writes to ${SECRETS_DIR})." >&2 exit 1 fi if [[ -f "${SECRETS_DIR}/db.key" && $FORCE -eq 0 ]]; then echo "Secrets already exist at ${SECRETS_DIR}. Re-run with --force to overwrite." >&2 exit 0 fi mkdir -p "${SECRETS_DIR}" chmod 700 "${SECRETS_DIR}" # 64-hex-char (256-bit) random key for SQLCipher + cookie signing. openssl rand -hex 32 > "${SECRETS_DIR}/db.key" # Admin username defaults to "admin" unless CYCLONE_ADMIN_USERNAME is set. ADMIN_USER="${CYCLONE_ADMIN_USERNAME:-admin}" printf '%s' "${ADMIN_USER}" > "${SECRETS_DIR}/admin_username" # Memorable-but-random admin password — printed once. ADMIN_PW="$(openssl rand -base64 18 | tr -d '/+=' | head -c 20)Aa1!" printf '%s' "${ADMIN_PW}" > "${SECRETS_DIR}/admin_pw" chmod 600 "${SECRETS_DIR}"/* chown -R root:root "${SECRETS_DIR}" cat <:8080/login and log in as '${ADMIN_USER}'. Change the password from /admin/users immediately. ================================================================ EOF