Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 34 additions & 6 deletions scripts/enroll.sh
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,9 @@ gen_password() { head -c 32 /dev/urandom | base64 | tr -d '/+=' | head -c 24; }

write_enroll_nix() {
local signing_pub="$1" harmonia_pub_key="$2"
cat > "${ENROLL_NIX}" <<NIX
# Write to a temp file then atomically rename so a partial write never leaves
# a syntactically broken enroll.nix that silently poisons the next rebuild.
cat > "${ENROLL_NIX}.tmp" <<NIX
# Generated by scripts/enroll.sh — device-specific, gitignored.
# Re-run scripts/enroll.sh to regenerate.
{ ... }:
Expand All @@ -87,6 +89,7 @@ write_enroll_nix() {
nix.settings.trusted-public-keys = [ "${harmonia_pub_key}" ];
}
NIX
mv "${ENROLL_NIX}.tmp" "${ENROLL_NIX}"
ok "Wrote ${ENROLL_NIX}"
}

Expand Down Expand Up @@ -125,6 +128,8 @@ step 0 "Preflight checks"

# Disk space: require at least 100 GB free on the partition holding the repo
DISK_FREE_KB=$(df -k "${REPO_ROOT}" | awk 'NR==2 {print $4}')
[[ "${DISK_FREE_KB}" =~ ^[0-9]+$ ]] || \
die "Could not parse available disk space from 'df -k ${REPO_ROOT}' (got: '${DISK_FREE_KB}')"
DISK_FREE_GB=$(( DISK_FREE_KB / 1024 / 1024 ))
[[ $DISK_FREE_GB -ge 100 ]] || \
die "Insufficient disk space: ${DISK_FREE_GB} GB free, 100 GB required"
Expand Down Expand Up @@ -196,6 +201,7 @@ else
ok "Generated ${AGE_KEY} ($(elapsed))"
fi
AGE_PUBKEY=$(age-keygen -y "${AGE_KEY}")
[[ -n "${AGE_PUBKEY}" ]] || die "age-keygen produced empty public key from ${AGE_KEY} — key file may be corrupt"
echo "${AGE_PUBKEY}" > "${AGE_PUB}"
info "Public key: ${AGE_PUBKEY}"

Expand Down Expand Up @@ -247,9 +253,20 @@ else
fi

KATELLO_PASSWORD=$(cat "${KATELLO_ADMIN_PW_FILE}")
[[ -n "${KATELLO_PASSWORD}" ]] || \
die "Katello password file is empty: ${KATELLO_ADMIN_PW_FILE}
Regenerate: rm -f ${KATELLO_ADMIN_PW_FILE} ${COMPOSE_ENV} && sudo bash scripts/enroll.sh"

docker-compose -f "${COMPOSE_FILE}" --env-file "${COMPOSE_ENV}" up -d
info "Containers started. Waiting for foreman-installer (10-15 min)..."

# Verify that compose actually started containers — `up -d` exits 0 even if
# containers crash-loop immediately. Count running containers for this project.
_KATELLO_RUNNING=$(docker ps --filter "name=katello" --format "{{.Names}}" 2>/dev/null | wc -l | tr -d ' ')
[[ "${_KATELLO_RUNNING}" -ge 1 ]] || \
die "docker compose up -d returned 0 but no katello containers are running.
Diagnose: docker compose -f ${COMPOSE_FILE} logs
Then retry: sudo bash scripts/enroll.sh"
info "Containers started (${_KATELLO_RUNNING} katello container(s) running). Waiting for foreman-installer (10-15 min)..."
info "Follow: docker compose -f ${COMPOSE_FILE} logs -f foreman-katello"

MAX_WAIT="${FOREMAN_WAIT_TIMEOUT:-1800}"; ELAPSED=0; INTERVAL=15
Expand Down Expand Up @@ -306,7 +323,14 @@ if [[ "${_needs_encrypt}" -eq 1 ]]; then
PLAINTEXT=$(mktemp /tmp/sourceos-secrets-XXXXXX.yaml)
trap "rm -f ${PLAINTEXT}" EXIT
printf 'katello-password: "%s"\n' "${KATELLO_PASSWORD}" > "${PLAINTEXT}"
SOPS_AGE_RECIPIENTS="${AGE_PUBKEY}" sops --encrypt "${PLAINTEXT}" > "${SECRETS_YAML}"
# Write to a temp file on the same filesystem so the final mv is atomic.
# A plain `> ${SECRETS_YAML}` would truncate the file before sops runs;
# if sops then fails the file is empty and subsequent re-runs skip encryption.
_SECRETS_TMP=$(mktemp "${SOURCEOS_DIR}/secrets-XXXXXX.yaml.tmp")
chmod 600 "${_SECRETS_TMP}"
trap "rm -f ${PLAINTEXT} ${_SECRETS_TMP}" EXIT
SOPS_AGE_RECIPIENTS="${AGE_PUBKEY}" sops --encrypt "${PLAINTEXT}" > "${_SECRETS_TMP}"
mv "${_SECRETS_TMP}" "${SECRETS_YAML}"
chmod 600 "${SECRETS_YAML}"
rm -f "${PLAINTEXT}"; trap - EXIT
ok "Encrypted secrets written to ${SECRETS_YAML} ($(elapsed))"
Expand All @@ -333,6 +357,9 @@ else
fi

HARMONIA_PUBKEY=$(cat "${HARMONIA_PUB}")
[[ -n "${HARMONIA_PUBKEY}" ]] || \
die "harmonia public key file is empty or malformed: ${HARMONIA_PUB}
Delete both and re-run: rm -f ${HARMONIA_KEY} ${HARMONIA_PUB} && sudo bash scripts/enroll.sh"
info "harmonia public key: ${HARMONIA_PUBKEY}"

# ── Step 8: minisign key pair + nix-cache-info signature ──────────────────────
Expand Down Expand Up @@ -361,13 +388,14 @@ SIGNING_PUBKEY=$(grep -v '^untrusted comment' "${MINISIGN_PUB}" | head -1)
Delete and re-run: rm -f ${MINISIGN_PUB} ${MINISIGN_SEC} && sudo bash scripts/enroll.sh"
info "Signing public key: ${SIGNING_PUBKEY}"

# Write nix-cache-info and sign it. nginx serves the .minisig file alongside
# harmonia so sourceos-syncd can verify the cache before pulling packages.
cat > "${MINISIGN_CACHE_INFO}" <<EOF
# Write nix-cache-info atomically then sign it. nginx serves the .minisig file
# alongside harmonia so sourceos-syncd can verify the cache before pulling.
cat > "${MINISIGN_CACHE_INFO}.tmp" <<EOF
StoreDir: /nix/store
WantMassQuery: 1
Priority: 40
EOF
mv "${MINISIGN_CACHE_INFO}.tmp" "${MINISIGN_CACHE_INFO}"

minisign -S -s "${MINISIGN_SEC}" -m "${MINISIGN_CACHE_INFO}" -x "${MINISIGN_CACHE_INFO_SIG}" \
|| die "minisign signing failed — check that ${MINISIGN_SEC} passphrase is empty"
Expand Down