Rotate operator secrets into agenix and deepen caches
This commit is contained in:
parent
7039bf5aad
commit
03415e579b
28 changed files with 526 additions and 126 deletions
78
Scripts/_burrow-secrets.sh
Normal file
78
Scripts/_burrow-secrets.sh
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
BURROW_SECRET_TMPFILES=()
|
||||
|
||||
burrow_cleanup_secret_tmpfiles() {
|
||||
local path
|
||||
for path in "${BURROW_SECRET_TMPFILES[@]:-}"; do
|
||||
[[ -n "${path}" ]] && rm -f "${path}" >/dev/null 2>&1 || true
|
||||
done
|
||||
BURROW_SECRET_TMPFILES=()
|
||||
}
|
||||
|
||||
burrow_decrypt_age_secret_to_temp() {
|
||||
local repo_root="$1"
|
||||
local secret_path="$2"
|
||||
local tmp_file
|
||||
|
||||
if [[ ! -f "${secret_path}" ]]; then
|
||||
echo "age secret not found: ${secret_path}" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
tmp_file="$(mktemp "${TMPDIR:-/tmp}/burrow-secret.XXXXXX")"
|
||||
nix --extra-experimental-features "nix-command flakes" run "${repo_root}#agenix" -- -d "${secret_path}" > "${tmp_file}"
|
||||
chmod 600 "${tmp_file}"
|
||||
BURROW_SECRET_TMPFILES+=("${tmp_file}")
|
||||
printf '%s\n' "${tmp_file}"
|
||||
}
|
||||
|
||||
burrow_resolve_secret_file() {
|
||||
local repo_root="$1"
|
||||
local explicit_path="$2"
|
||||
local intake_path="$3"
|
||||
local age_path="$4"
|
||||
local fallback_path="${5:-}"
|
||||
|
||||
if [[ -n "${explicit_path}" ]]; then
|
||||
if [[ ! -s "${explicit_path}" ]]; then
|
||||
echo "required file missing or empty: ${explicit_path}" >&2
|
||||
return 1
|
||||
fi
|
||||
printf '%s\n' "${explicit_path}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ -n "${intake_path}" && -s "${intake_path}" ]]; then
|
||||
printf '%s\n' "${intake_path}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ -n "${age_path}" && -f "${age_path}" ]]; then
|
||||
burrow_decrypt_age_secret_to_temp "${repo_root}" "${age_path}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ -n "${fallback_path}" && -s "${fallback_path}" ]]; then
|
||||
printf '%s\n' "${fallback_path}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
burrow_encrypt_secret_from_file() {
|
||||
local repo_root="$1"
|
||||
local secret_path="$2"
|
||||
local source_path="$3"
|
||||
|
||||
if [[ ! -s "${source_path}" ]]; then
|
||||
echo "secret source missing or empty: ${source_path}" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
SECRET_SOURCE_FILE="${source_path}" \
|
||||
EDITOR="${repo_root}/Scripts/agenix-load-file.sh" \
|
||||
nix --extra-experimental-features "nix-command flakes" run "${repo_root}#agenix" -- -e "${secret_path}"
|
||||
}
|
||||
|
|
@ -3,6 +3,8 @@ set -euo pipefail
|
|||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
# shellcheck source=Scripts/_burrow-secrets.sh
|
||||
source "${SCRIPT_DIR}/_burrow-secrets.sh"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
|
|
@ -10,27 +12,33 @@ Usage: Scripts/bootstrap-forge-intake.sh [options]
|
|||
|
||||
Copy the minimum Burrow forge bootstrap secrets onto the target host under
|
||||
/var/lib/burrow/intake with the ownership expected by the NixOS services.
|
||||
Legacy path only: the current forge runtime consumes agenix secrets directly.
|
||||
|
||||
Options:
|
||||
--host <user@host> SSH target (default: root@git.burrow.net)
|
||||
--ssh-key <path> SSH private key used to reach the host
|
||||
(default: intake/agent_at_burrow_net_ed25519)
|
||||
(default: secrets/forgejo/agent-ssh-key.age, then intake/)
|
||||
--password-file <path> Forgejo admin bootstrap password file
|
||||
(default: intake/forgejo_pass_contact_at_burrow_net.txt)
|
||||
(default: secrets/forgejo/admin-password.age, then intake/)
|
||||
--agent-key-file <path> Agent SSH private key copied for runner bootstrap
|
||||
(default: intake/agent_at_burrow_net_ed25519)
|
||||
(default: secrets/forgejo/agent-ssh-key.age, then intake/)
|
||||
--no-verify Skip remote ls/stat verification after install
|
||||
-h, --help Show this help text
|
||||
EOF
|
||||
}
|
||||
|
||||
HOST="${BURROW_FORGE_HOST:-root@git.burrow.net}"
|
||||
SSH_KEY="${BURROW_FORGE_SSH_KEY:-${REPO_ROOT}/intake/agent_at_burrow_net_ed25519}"
|
||||
PASSWORD_FILE="${BURROW_FORGE_PASSWORD_FILE:-${REPO_ROOT}/intake/forgejo_pass_contact_at_burrow_net.txt}"
|
||||
AGENT_KEY_FILE="${BURROW_FORGE_AGENT_KEY_FILE:-${REPO_ROOT}/intake/agent_at_burrow_net_ed25519}"
|
||||
SSH_KEY="${BURROW_FORGE_SSH_KEY:-}"
|
||||
PASSWORD_FILE="${BURROW_FORGE_PASSWORD_FILE:-}"
|
||||
AGENT_KEY_FILE="${BURROW_FORGE_AGENT_KEY_FILE:-}"
|
||||
KNOWN_HOSTS_FILE="${BURROW_FORGE_KNOWN_HOSTS_FILE:-${HOME}/.cache/burrow/forge-known_hosts}"
|
||||
VERIFY=1
|
||||
|
||||
cleanup() {
|
||||
burrow_cleanup_secret_tmpfiles
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--host)
|
||||
|
|
@ -67,12 +75,29 @@ done
|
|||
|
||||
mkdir -p "$(dirname "${KNOWN_HOSTS_FILE}")"
|
||||
|
||||
for path in "${SSH_KEY}" "${PASSWORD_FILE}" "${AGENT_KEY_FILE}"; do
|
||||
if [[ ! -s "${path}" ]]; then
|
||||
echo "required file missing or empty: ${path}" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
SSH_KEY="$(
|
||||
burrow_resolve_secret_file \
|
||||
"${REPO_ROOT}" \
|
||||
"${SSH_KEY}" \
|
||||
"${REPO_ROOT}/intake/agent_at_burrow_net_ed25519" \
|
||||
"${REPO_ROOT}/secrets/forgejo/agent-ssh-key.age" \
|
||||
"${HOME}/.ssh/agent_at_burrow_net_ed25519"
|
||||
)"
|
||||
PASSWORD_FILE="$(
|
||||
burrow_resolve_secret_file \
|
||||
"${REPO_ROOT}" \
|
||||
"${PASSWORD_FILE}" \
|
||||
"${REPO_ROOT}/intake/forgejo_pass_contact_at_burrow_net.txt" \
|
||||
"${REPO_ROOT}/secrets/forgejo/admin-password.age"
|
||||
)"
|
||||
AGENT_KEY_FILE="$(
|
||||
burrow_resolve_secret_file \
|
||||
"${REPO_ROOT}" \
|
||||
"${AGENT_KEY_FILE}" \
|
||||
"${REPO_ROOT}/intake/agent_at_burrow_net_ed25519" \
|
||||
"${REPO_ROOT}/secrets/forgejo/agent-ssh-key.age" \
|
||||
"${HOME}/.ssh/agent_at_burrow_net_ed25519"
|
||||
)"
|
||||
|
||||
ssh_opts=(
|
||||
-i "${SSH_KEY}"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ set -euo pipefail
|
|||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
# shellcheck source=Scripts/_burrow-secrets.sh
|
||||
source "${SCRIPT_DIR}/_burrow-secrets.sh"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
|
|
@ -12,17 +14,22 @@ Run a post-boot verification pass against the Burrow forge host.
|
|||
|
||||
Options:
|
||||
--host <user@host> SSH target (default: root@git.burrow.net)
|
||||
--ssh-key <path> SSH private key (default: intake/agent_at_burrow_net_ed25519)
|
||||
--ssh-key <path> SSH private key (default: secrets/forgejo/agent-ssh-key.age, then intake/)
|
||||
--expect-nsc Fail if forgejo-nsc services are not active
|
||||
-h, --help Show this help text
|
||||
EOF
|
||||
}
|
||||
|
||||
HOST="${BURROW_FORGE_HOST:-root@git.burrow.net}"
|
||||
SSH_KEY="${BURROW_FORGE_SSH_KEY:-${REPO_ROOT}/intake/agent_at_burrow_net_ed25519}"
|
||||
SSH_KEY="${BURROW_FORGE_SSH_KEY:-}"
|
||||
KNOWN_HOSTS_FILE="${BURROW_FORGE_KNOWN_HOSTS_FILE:-${HOME}/.cache/burrow/forge-known_hosts}"
|
||||
EXPECT_NSC=0
|
||||
|
||||
cleanup() {
|
||||
burrow_cleanup_secret_tmpfiles
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--host)
|
||||
|
|
@ -51,10 +58,17 @@ done
|
|||
|
||||
mkdir -p "$(dirname "${KNOWN_HOSTS_FILE}")"
|
||||
|
||||
if [[ ! -f "${SSH_KEY}" ]]; then
|
||||
echo "forge SSH key not found: ${SSH_KEY}" >&2
|
||||
SSH_KEY="$(
|
||||
burrow_resolve_secret_file \
|
||||
"${REPO_ROOT}" \
|
||||
"${SSH_KEY}" \
|
||||
"${REPO_ROOT}/intake/agent_at_burrow_net_ed25519" \
|
||||
"${REPO_ROOT}/secrets/forgejo/agent-ssh-key.age" \
|
||||
"${HOME}/.ssh/agent_at_burrow_net_ed25519"
|
||||
)" || {
|
||||
echo "forge SSH key could not be resolved" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
ssh \
|
||||
-i "${SSH_KEY}" \
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
# shellcheck source=Scripts/_burrow-secrets.sh
|
||||
source "${SCRIPT_DIR}/_burrow-secrets.sh"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: Scripts/cloudflare-upsert-a-record.sh --zone <zone> --name <fqdn> --ipv4 <address> [options]
|
||||
|
|
@ -13,7 +18,7 @@ Options:
|
|||
--name <fqdn> Fully-qualified DNS record name
|
||||
--ipv4 <address> IPv4 address for the A record
|
||||
--token-file <path> Cloudflare API token file
|
||||
default: intake/cloudflare-token.txt
|
||||
default: secrets/cloudflare/api-token.age, then intake/cloudflare-token.txt
|
||||
--ttl <seconds|auto> Record TTL, or auto
|
||||
default: auto
|
||||
--proxied <true|false> Whether to proxy through Cloudflare
|
||||
|
|
@ -25,10 +30,15 @@ EOF
|
|||
ZONE_NAME=""
|
||||
RECORD_NAME=""
|
||||
IPV4=""
|
||||
TOKEN_FILE="intake/cloudflare-token.txt"
|
||||
TOKEN_FILE="${CLOUDFLARE_TOKEN_FILE:-}"
|
||||
TTL_VALUE="auto"
|
||||
PROXIED="false"
|
||||
|
||||
cleanup() {
|
||||
burrow_cleanup_secret_tmpfiles
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--zone)
|
||||
|
|
@ -71,11 +81,16 @@ if [[ -z "${ZONE_NAME}" || -z "${RECORD_NAME}" || -z "${IPV4}" ]]; then
|
|||
usage >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [[ ! -f "${TOKEN_FILE}" ]]; then
|
||||
echo "Cloudflare token file not found: ${TOKEN_FILE}" >&2
|
||||
TOKEN_FILE="$(
|
||||
burrow_resolve_secret_file \
|
||||
"${REPO_ROOT}" \
|
||||
"${TOKEN_FILE}" \
|
||||
"${REPO_ROOT}/intake/cloudflare-token.txt" \
|
||||
"${REPO_ROOT}/secrets/cloudflare/api-token.age"
|
||||
)" || {
|
||||
echo "Cloudflare token file could not be resolved" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ ! "${IPV4}" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
|
||||
echo "Invalid IPv4 address: ${IPV4}" >&2
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|||
|
||||
# shellcheck source=Scripts/_burrow-flake.sh
|
||||
source "${SCRIPT_DIR}/_burrow-flake.sh"
|
||||
# shellcheck source=Scripts/_burrow-secrets.sh
|
||||
source "${SCRIPT_DIR}/_burrow-secrets.sh"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
|
|
@ -18,7 +20,7 @@ Defaults:
|
|||
|
||||
Environment:
|
||||
BURROW_FORGE_HOST root@git.burrow.net
|
||||
BURROW_FORGE_SSH_KEY intake/agent_at_burrow_net_ed25519
|
||||
BURROW_FORGE_SSH_KEY explicit path, otherwise secrets/forgejo/agent-ssh-key.age
|
||||
EOF
|
||||
}
|
||||
|
||||
|
|
@ -28,6 +30,7 @@ ALLOW_DIRTY=0
|
|||
BURROW_FLAKE_TMPDIRS=()
|
||||
|
||||
cleanup() {
|
||||
burrow_cleanup_secret_tmpfiles
|
||||
burrow_cleanup_flake_tmpdirs
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
|
@ -71,21 +74,17 @@ if [[ ${ALLOW_DIRTY} -ne 1 ]] && [[ -n "$(git status --short)" ]]; then
|
|||
fi
|
||||
|
||||
FORGE_HOST="${BURROW_FORGE_HOST:-root@git.burrow.net}"
|
||||
FORGE_SSH_KEY="${BURROW_FORGE_SSH_KEY:-}"
|
||||
|
||||
if [[ -z "${FORGE_SSH_KEY}" ]]; then
|
||||
if [[ -f "${REPO_ROOT}/intake/agent_at_burrow_net_ed25519" ]]; then
|
||||
FORGE_SSH_KEY="${REPO_ROOT}/intake/agent_at_burrow_net_ed25519"
|
||||
else
|
||||
FORGE_SSH_KEY="${HOME}/.ssh/agent_at_burrow_net_ed25519"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ ! -f "${FORGE_SSH_KEY}" ]]; then
|
||||
echo "Forge SSH key not found at ${FORGE_SSH_KEY}." >&2
|
||||
echo "Set BURROW_FORGE_SSH_KEY or place the agent key in intake/." >&2
|
||||
FORGE_SSH_KEY="$(
|
||||
burrow_resolve_secret_file \
|
||||
"${REPO_ROOT}" \
|
||||
"${BURROW_FORGE_SSH_KEY:-}" \
|
||||
"${REPO_ROOT}/intake/agent_at_burrow_net_ed25519" \
|
||||
"${REPO_ROOT}/secrets/forgejo/agent-ssh-key.age" \
|
||||
"${HOME}/.ssh/agent_at_burrow_net_ed25519"
|
||||
)" || {
|
||||
echo "Unable to resolve the forge SSH key." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
FORGE_KNOWN_HOSTS_FILE="${BURROW_FORGE_KNOWN_HOSTS_FILE:-${HOME}/.cache/burrow/forge-known_hosts}"
|
||||
mkdir -p "$(dirname "${FORGE_KNOWN_HOSTS_FILE}")"
|
||||
|
|
|
|||
|
|
@ -6,12 +6,14 @@ REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|||
|
||||
# shellcheck source=Scripts/_burrow-flake.sh
|
||||
source "${SCRIPT_DIR}/_burrow-flake.sh"
|
||||
# shellcheck source=Scripts/_burrow-secrets.sh
|
||||
source "${SCRIPT_DIR}/_burrow-secrets.sh"
|
||||
|
||||
DEFAULT_CONFIG="burrow-forge"
|
||||
DEFAULT_FLAKE="."
|
||||
DEFAULT_LOCATION="hel1"
|
||||
DEFAULT_ARCHITECTURE="x86"
|
||||
DEFAULT_TOKEN_FILE="${REPO_ROOT}/intake/hetzner-api-token.txt"
|
||||
DEFAULT_TOKEN_FILE=""
|
||||
|
||||
CONFIG="${HCLOUD_IMAGE_CONFIG:-${DEFAULT_CONFIG}}"
|
||||
FLAKE="${HCLOUD_IMAGE_FLAKE:-${DEFAULT_FLAKE}}"
|
||||
|
|
@ -30,6 +32,13 @@ NIX_BUILD_FLAGS=()
|
|||
BURROW_FLAKE_TMPDIRS=()
|
||||
LOCAL_STORE_DIR=""
|
||||
|
||||
cleanup() {
|
||||
burrow_cleanup_secret_tmpfiles
|
||||
burrow_cleanup_flake_tmpdirs
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: Scripts/hcloud-upload-nixos-image.sh [options]
|
||||
|
|
@ -42,7 +51,7 @@ Options:
|
|||
--location <code> Hetzner location for the temporary upload server (default: hel1)
|
||||
--architecture <x86|arm> CPU architecture of the image (default: x86)
|
||||
--server-type <name> Hetzner server type for the temporary upload server
|
||||
--token-file <path> Hetzner API token file (default: intake/hetzner-api-token.txt)
|
||||
--token-file <path> Hetzner API token file (default: secrets/hetzner/api-token.age, then intake/hetzner-api-token.txt)
|
||||
--artifact-path <path> Prebuilt raw image artifact to upload directly
|
||||
--output-hash <hash> Stable hash label for --artifact-path uploads
|
||||
--builder-spec <string> Complete builders string passed to nix build
|
||||
|
|
@ -125,6 +134,17 @@ while [[ $# -gt 0 ]]; do
|
|||
esac
|
||||
done
|
||||
|
||||
TOKEN_FILE="$(
|
||||
burrow_resolve_secret_file \
|
||||
"${REPO_ROOT}" \
|
||||
"${TOKEN_FILE}" \
|
||||
"${REPO_ROOT}/intake/hetzner-api-token.txt" \
|
||||
"${REPO_ROOT}/secrets/hetzner/api-token.age"
|
||||
)" || {
|
||||
echo "Hetzner API token file could not be resolved" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
burrow_cleanup_flake_tmpdirs
|
||||
if [[ -n "${LOCAL_STORE_DIR}" && -d "${LOCAL_STORE_DIR}" ]]; then
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@
|
|||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
# shellcheck source=Scripts/_burrow-secrets.sh
|
||||
source "${SCRIPT_DIR}/_burrow-secrets.sh"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
|
|
@ -31,7 +34,7 @@ Options:
|
|||
-h, --help Show this help text.
|
||||
|
||||
Environment:
|
||||
HCLOUD_TOKEN_FILE Defaults to intake/hetzner-api-token.txt
|
||||
HCLOUD_TOKEN_FILE Defaults to secrets/hetzner/api-token.age, then intake/hetzner-api-token.txt
|
||||
EOF
|
||||
}
|
||||
|
||||
|
|
@ -43,10 +46,15 @@ IMAGE="ubuntu-24.04"
|
|||
CONFIG="burrow-forge"
|
||||
FLAKE="."
|
||||
UPLOAD_LOCATION=""
|
||||
TOKEN_FILE="${HCLOUD_TOKEN_FILE:-intake/hetzner-api-token.txt}"
|
||||
TOKEN_FILE="${HCLOUD_TOKEN_FILE:-}"
|
||||
YES=0
|
||||
SSH_KEYS=("contact@burrow.net" "agent@burrow.net")
|
||||
|
||||
cleanup() {
|
||||
burrow_cleanup_secret_tmpfiles
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
if [[ $# -gt 0 ]]; then
|
||||
case "$1" in
|
||||
show|create|delete|recreate|build-image|create-from-image|recreate-from-image)
|
||||
|
|
@ -110,10 +118,16 @@ while [[ $# -gt 0 ]]; do
|
|||
esac
|
||||
done
|
||||
|
||||
if [[ ! -f "${TOKEN_FILE}" ]]; then
|
||||
echo "Hetzner API token file not found: ${TOKEN_FILE}" >&2
|
||||
TOKEN_FILE="$(
|
||||
burrow_resolve_secret_file \
|
||||
"${REPO_ROOT}" \
|
||||
"${TOKEN_FILE}" \
|
||||
"${REPO_ROOT}/intake/hetzner-api-token.txt" \
|
||||
"${REPO_ROOT}/secrets/hetzner/api-token.age"
|
||||
)" || {
|
||||
echo "Hetzner API token file could not be resolved" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ -z "${UPLOAD_LOCATION}" ]]; then
|
||||
UPLOAD_LOCATION="${LOCATION}"
|
||||
|
|
|
|||
|
|
@ -6,11 +6,13 @@ REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|||
|
||||
# shellcheck source=Scripts/_burrow-flake.sh
|
||||
source "${SCRIPT_DIR}/_burrow-flake.sh"
|
||||
# shellcheck source=Scripts/_burrow-secrets.sh
|
||||
source "${SCRIPT_DIR}/_burrow-secrets.sh"
|
||||
|
||||
CONFIG="${HCLOUD_IMAGE_CONFIG:-burrow-forge}"
|
||||
FLAKE="${HCLOUD_IMAGE_FLAKE:-.}"
|
||||
LOCATION="${HCLOUD_IMAGE_LOCATION:-hel1}"
|
||||
TOKEN_FILE="${HCLOUD_TOKEN_FILE:-${REPO_ROOT}/intake/hetzner-api-token.txt}"
|
||||
TOKEN_FILE="${HCLOUD_TOKEN_FILE:-}"
|
||||
NSC_SSH_HOST="${NSC_SSH_HOST:-ssh.ord2.namespace.so}"
|
||||
NSC_MACHINE_TYPE="${NSC_MACHINE_TYPE:-linux/amd64:32x64}"
|
||||
NSC_BUILDER_DURATION="${NSC_BUILDER_DURATION:-4h}"
|
||||
|
|
@ -26,6 +28,13 @@ EXTRA_LABELS=()
|
|||
BURROW_FLAKE_TMPDIRS=()
|
||||
BUILDER_ID=""
|
||||
|
||||
cleanup() {
|
||||
burrow_cleanup_secret_tmpfiles
|
||||
burrow_cleanup_flake_tmpdirs
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: Scripts/nsc-build-and-upload-image.sh [options]
|
||||
|
|
@ -37,7 +46,7 @@ Options:
|
|||
--config <name> images.<name>-raw output to build (default: burrow-forge)
|
||||
--flake <path> Flake path to build from (default: .)
|
||||
--location <code> Hetzner upload location (default: hel1)
|
||||
--token-file <path> Hetzner API token file (default: intake/hetzner-api-token.txt)
|
||||
--token-file <path> Hetzner API token file (default: secrets/hetzner/api-token.age, then intake/hetzner-api-token.txt)
|
||||
--machine-type <type> Namespace machine type (default: linux/amd64:32x64)
|
||||
--ssh-host <host> Namespace SSH endpoint (default: ssh.ord2.namespace.so)
|
||||
--duration <ttl> Namespace builder lifetime (default: 4h)
|
||||
|
|
@ -126,6 +135,17 @@ while [[ $# -gt 0 ]]; do
|
|||
esac
|
||||
done
|
||||
|
||||
TOKEN_FILE="$(
|
||||
burrow_resolve_secret_file \
|
||||
"${REPO_ROOT}" \
|
||||
"${TOKEN_FILE}" \
|
||||
"${REPO_ROOT}/intake/hetzner-api-token.txt" \
|
||||
"${REPO_ROOT}/secrets/hetzner/api-token.age"
|
||||
)" || {
|
||||
echo "Hetzner API token file could not be resolved" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
if [[ -n "${BUILDER_ID}" && -n "${NSC_BIN}" ]]; then
|
||||
"${NSC_BIN}" destroy "${BUILDER_ID}" --force >/dev/null 2>&1 || true
|
||||
|
|
|
|||
|
|
@ -6,31 +6,35 @@ REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|||
|
||||
# shellcheck source=Scripts/_burrow-flake.sh
|
||||
source "${SCRIPT_DIR}/_burrow-flake.sh"
|
||||
# shellcheck source=Scripts/_burrow-secrets.sh
|
||||
source "${SCRIPT_DIR}/_burrow-secrets.sh"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: Scripts/provision-forgejo-nsc.sh [options]
|
||||
|
||||
Generate Burrow forgejo-nsc runtime inputs in intake/ and optionally refresh the
|
||||
Namespace token from the currently logged-in namespace account.
|
||||
Generate Burrow forgejo-nsc runtime inputs and refresh the authoritative
|
||||
`secrets/forgejo/*.age` files, optionally refreshing the Namespace token from
|
||||
the currently logged-in namespace account.
|
||||
|
||||
Options:
|
||||
--host <user@host> SSH target used to mint the Forgejo PAT.
|
||||
Default: root@git.burrow.net
|
||||
--ssh-key <path> SSH private key for the forge host.
|
||||
Default: intake/agent_at_burrow_net_ed25519
|
||||
Default: secrets/forgejo/agent-ssh-key.age, then intake/
|
||||
--nsc-bin <path> Override the nsc binary.
|
||||
--no-refresh-token Reuse intake/forgejo_nsc_token.txt if it already exists.
|
||||
--no-refresh-token Reuse the existing encrypted Namespace token if it already exists.
|
||||
--token-name <name> Forgejo PAT name prefix (default: forgejo-nsc)
|
||||
--contact-user <name> Forgejo username used for PAT creation (default: contact)
|
||||
--scope-owner <name> Forgejo org/user owner for the default NSC scope (default: hackclub)
|
||||
--scope-name <name> Forgejo repository name for the default NSC scope (default: burrow)
|
||||
--write-intake Also write plaintext runtime inputs to intake/ for local debugging.
|
||||
-h, --help Show this help text.
|
||||
EOF
|
||||
}
|
||||
|
||||
HOST="${BURROW_FORGE_HOST:-root@git.burrow.net}"
|
||||
SSH_KEY="${BURROW_FORGE_SSH_KEY:-${REPO_ROOT}/intake/agent_at_burrow_net_ed25519}"
|
||||
SSH_KEY="${BURROW_FORGE_SSH_KEY:-}"
|
||||
NSC_BIN="${NSC_BIN:-}"
|
||||
KNOWN_HOSTS_FILE="${BURROW_FORGE_KNOWN_HOSTS_FILE:-${HOME}/.cache/burrow/forge-known_hosts}"
|
||||
REFRESH_TOKEN=1
|
||||
|
|
@ -39,8 +43,12 @@ CONTACT_USER="${FORGEJO_CONTACT_USER:-contact}"
|
|||
SCOPE_OWNER="${FORGEJO_SCOPE_OWNER:-hackclub}"
|
||||
SCOPE_NAME="${FORGEJO_SCOPE_NAME:-burrow}"
|
||||
BURROW_FLAKE_TMPDIRS=()
|
||||
WRITE_INTAKE=0
|
||||
TMP_DIR=""
|
||||
|
||||
cleanup() {
|
||||
[[ -n "${TMP_DIR}" ]] && rm -rf "${TMP_DIR}" >/dev/null 2>&1 || true
|
||||
burrow_cleanup_secret_tmpfiles
|
||||
burrow_cleanup_flake_tmpdirs
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
|
@ -79,6 +87,10 @@ while [[ $# -gt 0 ]]; do
|
|||
SCOPE_NAME="${2:?missing value for --scope-name}"
|
||||
shift 2
|
||||
;;
|
||||
--write-intake)
|
||||
WRITE_INTAKE=1
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
|
|
@ -97,13 +109,15 @@ burrow_require_cmd nix
|
|||
burrow_require_cmd ssh
|
||||
burrow_require_cmd python3
|
||||
|
||||
if [[ ! -f "${SSH_KEY}" ]]; then
|
||||
echo "forge SSH key not found: ${SSH_KEY}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "${REPO_ROOT}/intake"
|
||||
chmod 700 "${REPO_ROOT}/intake"
|
||||
SSH_KEY="$(
|
||||
burrow_resolve_secret_file \
|
||||
"${REPO_ROOT}" \
|
||||
"${SSH_KEY}" \
|
||||
"${REPO_ROOT}/intake/agent_at_burrow_net_ed25519" \
|
||||
"${REPO_ROOT}/secrets/forgejo/agent-ssh-key.age" \
|
||||
"${HOME}/.ssh/agent_at_burrow_net_ed25519"
|
||||
)"
|
||||
TMP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/burrow-forgejo-nsc.XXXXXX")"
|
||||
|
||||
flake_ref="$(burrow_prepare_flake_ref "${REPO_ROOT}")"
|
||||
if [[ -z "${NSC_BIN}" ]]; then
|
||||
|
|
@ -128,13 +142,16 @@ if [[ ! -x "${NSC_BIN}" ]]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
token_file="${REPO_ROOT}/intake/forgejo_nsc_token.txt"
|
||||
dispatcher_out="${REPO_ROOT}/intake/forgejo_nsc_dispatcher.yaml"
|
||||
autoscaler_out="${REPO_ROOT}/intake/forgejo_nsc_autoscaler.yaml"
|
||||
token_file="${TMP_DIR}/forgejo_nsc_token.txt"
|
||||
dispatcher_out="${TMP_DIR}/forgejo_nsc_dispatcher.yaml"
|
||||
autoscaler_out="${TMP_DIR}/forgejo_nsc_autoscaler.yaml"
|
||||
dispatcher_src="${REPO_ROOT}/services/forgejo-nsc/deploy/dispatcher.yaml"
|
||||
autoscaler_src="${REPO_ROOT}/services/forgejo-nsc/deploy/autoscaler.yaml"
|
||||
token_secret="${REPO_ROOT}/secrets/forgejo/nsc-token.age"
|
||||
dispatcher_secret="${REPO_ROOT}/secrets/forgejo/nsc-dispatcher-config.age"
|
||||
autoscaler_secret="${REPO_ROOT}/secrets/forgejo/nsc-autoscaler-config.age"
|
||||
|
||||
if [[ "${REFRESH_TOKEN}" -eq 1 || ! -s "${token_file}" ]]; then
|
||||
if [[ "${REFRESH_TOKEN}" -eq 1 ]]; then
|
||||
"${NSC_BIN}" auth check-login --duration 20m >/dev/null
|
||||
raw_token_file="$(mktemp)"
|
||||
trap 'rm -f "${raw_token_file}"; cleanup' EXIT
|
||||
|
|
@ -155,7 +172,13 @@ Path(os.environ["TOKEN_FILE"]).write_text(
|
|||
PY
|
||||
rm -f "${raw_token_file}"
|
||||
chmod 600 "${token_file}"
|
||||
elif [[ -s "${token_file}" ]]; then
|
||||
elif [[ -f "${token_secret}" ]]; then
|
||||
burrow_decrypt_age_secret_to_temp "${REPO_ROOT}" "${token_secret}" > "${token_file}"
|
||||
elif [[ -s "${REPO_ROOT}/intake/forgejo_nsc_token.txt" ]]; then
|
||||
cp "${REPO_ROOT}/intake/forgejo_nsc_token.txt" "${token_file}"
|
||||
fi
|
||||
|
||||
if [[ -s "${token_file}" ]]; then
|
||||
TOKEN_FILE="${token_file}" python3 - <<'PY'
|
||||
import json
|
||||
import os
|
||||
|
|
@ -271,6 +294,24 @@ PY
|
|||
|
||||
chmod 600 "${dispatcher_out}" "${autoscaler_out}"
|
||||
|
||||
echo "Rendered intake/forgejo_nsc_token.txt, intake/forgejo_nsc_dispatcher.yaml, and intake/forgejo_nsc_autoscaler.yaml."
|
||||
echo "Re-encrypt them into secrets/forgejo/{nsc-token,nsc-dispatcher-config,nsc-autoscaler-config}.age before deploying the forge host."
|
||||
burrow_encrypt_secret_from_file "${REPO_ROOT}" "${token_secret}" "${token_file}"
|
||||
burrow_encrypt_secret_from_file "${REPO_ROOT}" "${dispatcher_secret}" "${dispatcher_out}"
|
||||
burrow_encrypt_secret_from_file "${REPO_ROOT}" "${autoscaler_secret}" "${autoscaler_out}"
|
||||
|
||||
if [[ "${WRITE_INTAKE}" -eq 1 ]]; then
|
||||
mkdir -p "${REPO_ROOT}/intake"
|
||||
chmod 700 "${REPO_ROOT}/intake"
|
||||
cp "${token_file}" "${REPO_ROOT}/intake/forgejo_nsc_token.txt"
|
||||
cp "${dispatcher_out}" "${REPO_ROOT}/intake/forgejo_nsc_dispatcher.yaml"
|
||||
cp "${autoscaler_out}" "${REPO_ROOT}/intake/forgejo_nsc_autoscaler.yaml"
|
||||
chmod 600 \
|
||||
"${REPO_ROOT}/intake/forgejo_nsc_token.txt" \
|
||||
"${REPO_ROOT}/intake/forgejo_nsc_dispatcher.yaml" \
|
||||
"${REPO_ROOT}/intake/forgejo_nsc_autoscaler.yaml"
|
||||
fi
|
||||
|
||||
echo "Updated secrets/forgejo/{nsc-token,nsc-dispatcher-config,nsc-autoscaler-config}.age."
|
||||
if [[ "${WRITE_INTAKE}" -eq 1 ]]; then
|
||||
echo "Also refreshed intake/forgejo_nsc_{token,dispatcher,autoscaler} for local debugging."
|
||||
fi
|
||||
echo "Minted Forgejo PAT ${token_name} for ${CONTACT_USER} on ${HOST}."
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@ usage() {
|
|||
cat <<'EOF'
|
||||
Usage: Scripts/sync-forgejo-nsc-config.sh [options]
|
||||
|
||||
Copy Burrow forgejo-nsc runtime inputs from intake/ onto the forge host and
|
||||
Copy Burrow forgejo-nsc runtime inputs from age secrets or intake/ onto the forge host and
|
||||
restart the dispatcher/autoscaler units.
|
||||
|
||||
Options:
|
||||
--host <user@host> SSH target (default: root@git.burrow.net)
|
||||
--ssh-key <path> SSH private key (default: intake/agent_at_burrow_net_ed25519)
|
||||
--ssh-key <path> SSH private key (default: secrets/forgejo/agent-ssh-key.age, then intake/)
|
||||
--rotate-pat Re-render the intake files before syncing.
|
||||
--no-restart Copy files only.
|
||||
-h, --help Show this help text.
|
||||
|
|
@ -19,12 +19,21 @@ EOF
|
|||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
# shellcheck source=Scripts/_burrow-secrets.sh
|
||||
source "${SCRIPT_DIR}/_burrow-secrets.sh"
|
||||
|
||||
HOST="${BURROW_FORGE_HOST:-root@git.burrow.net}"
|
||||
SSH_KEY="${BURROW_FORGE_SSH_KEY:-${REPO_ROOT}/intake/agent_at_burrow_net_ed25519}"
|
||||
SSH_KEY="${BURROW_FORGE_SSH_KEY:-}"
|
||||
KNOWN_HOSTS_FILE="${BURROW_FORGE_KNOWN_HOSTS_FILE:-${HOME}/.cache/burrow/forge-known_hosts}"
|
||||
ROTATE_PAT=0
|
||||
NO_RESTART=0
|
||||
TMP_DIR=""
|
||||
|
||||
cleanup() {
|
||||
[[ -n "${TMP_DIR}" ]] && rm -rf "${TMP_DIR}" >/dev/null 2>&1 || true
|
||||
burrow_cleanup_secret_tmpfiles
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
|
|
@ -68,18 +77,41 @@ burrow_require_cmd() {
|
|||
burrow_require_cmd ssh
|
||||
burrow_require_cmd scp
|
||||
|
||||
if [[ ! -f "${SSH_KEY}" ]]; then
|
||||
echo "forge SSH key not found: ${SSH_KEY}" >&2
|
||||
exit 1
|
||||
fi
|
||||
SSH_KEY="$(
|
||||
burrow_resolve_secret_file \
|
||||
"${REPO_ROOT}" \
|
||||
"${SSH_KEY}" \
|
||||
"${REPO_ROOT}/intake/agent_at_burrow_net_ed25519" \
|
||||
"${REPO_ROOT}/secrets/forgejo/agent-ssh-key.age" \
|
||||
"${HOME}/.ssh/agent_at_burrow_net_ed25519"
|
||||
)"
|
||||
|
||||
if [[ "${ROTATE_PAT}" -eq 1 ]]; then
|
||||
"${SCRIPT_DIR}/provision-forgejo-nsc.sh" --host "${HOST}" --ssh-key "${SSH_KEY}"
|
||||
fi
|
||||
|
||||
token_file="${REPO_ROOT}/intake/forgejo_nsc_token.txt"
|
||||
dispatcher_file="${REPO_ROOT}/intake/forgejo_nsc_dispatcher.yaml"
|
||||
autoscaler_file="${REPO_ROOT}/intake/forgejo_nsc_autoscaler.yaml"
|
||||
TMP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/burrow-nsc-sync.XXXXXX")"
|
||||
token_file="$(
|
||||
burrow_resolve_secret_file \
|
||||
"${REPO_ROOT}" \
|
||||
"" \
|
||||
"${REPO_ROOT}/intake/forgejo_nsc_token.txt" \
|
||||
"${REPO_ROOT}/secrets/forgejo/nsc-token.age"
|
||||
)"
|
||||
dispatcher_file="$(
|
||||
burrow_resolve_secret_file \
|
||||
"${REPO_ROOT}" \
|
||||
"" \
|
||||
"${REPO_ROOT}/intake/forgejo_nsc_dispatcher.yaml" \
|
||||
"${REPO_ROOT}/secrets/forgejo/nsc-dispatcher-config.age"
|
||||
)"
|
||||
autoscaler_file="$(
|
||||
burrow_resolve_secret_file \
|
||||
"${REPO_ROOT}" \
|
||||
"" \
|
||||
"${REPO_ROOT}/intake/forgejo_nsc_autoscaler.yaml" \
|
||||
"${REPO_ROOT}/secrets/forgejo/nsc-autoscaler-config.age"
|
||||
)"
|
||||
|
||||
for path in "${token_file}" "${dispatcher_file}" "${autoscaler_file}"; do
|
||||
if [[ ! -s "${path}" ]]; then
|
||||
|
|
@ -96,12 +128,12 @@ ssh_opts=(
|
|||
)
|
||||
|
||||
remote_tmp="$(ssh "${ssh_opts[@]}" "${HOST}" "mktemp -d")"
|
||||
cleanup() {
|
||||
cleanup_remote() {
|
||||
if [[ -n "${remote_tmp:-}" ]]; then
|
||||
ssh "${ssh_opts[@]}" "${HOST}" "rm -rf '${remote_tmp}'" >/dev/null 2>&1 || true
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT
|
||||
trap 'cleanup_remote; cleanup' EXIT
|
||||
|
||||
scp "${ssh_opts[@]}" \
|
||||
"${token_file}" \
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue