Stabilize forgejo namespace auth and secrets
This commit is contained in:
parent
5c0a9b3f54
commit
5b09f3a742
8 changed files with 59 additions and 49 deletions
|
|
@ -107,18 +107,25 @@ burrow_encrypt_secret_from_file() {
|
|||
local secret_path="$2"
|
||||
local source_path="$3"
|
||||
local agenix_path
|
||||
local identity_path
|
||||
local backup_file=""
|
||||
|
||||
if [[ ! -s "${source_path}" ]]; then
|
||||
echo "secret source missing or empty: ${source_path}" >&2
|
||||
return 1
|
||||
fi
|
||||
agenix_path="$(burrow_secret_repo_path "${repo_root}" "${secret_path}")"
|
||||
identity_path="$(burrow_agenix_identity_path "${repo_root}")"
|
||||
|
||||
if [[ -n "${identity_path}" ]]; then
|
||||
nix --extra-experimental-features "nix-command flakes" run "${repo_root}#agenix" -- -e "${agenix_path}" -i "${identity_path}" < "${source_path}"
|
||||
else
|
||||
nix --extra-experimental-features "nix-command flakes" run "${repo_root}#agenix" -- -e "${agenix_path}" < "${source_path}"
|
||||
if [[ -f "${secret_path}" ]]; then
|
||||
backup_file="$(mktemp "${TMPDIR:-/tmp}/burrow-secret-backup.XXXXXX")"
|
||||
cp "${secret_path}" "${backup_file}"
|
||||
fi
|
||||
rm -f "${secret_path}"
|
||||
|
||||
if ! nix --extra-experimental-features "nix-command flakes" run "${repo_root}#agenix" -- -e "${agenix_path}" < "${source_path}"; then
|
||||
if [[ -n "${backup_file}" && -f "${backup_file}" ]]; then
|
||||
mv "${backup_file}" "${secret_path}"
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
|
||||
[[ -n "${backup_file}" ]] && rm -f "${backup_file}"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -146,25 +146,36 @@ 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 ]]; then
|
||||
"${NSC_BIN}" auth check-login --duration 20m >/dev/null
|
||||
raw_token_file="$(mktemp)"
|
||||
trap 'rm -f "${raw_token_file}"; cleanup' EXIT
|
||||
"${NSC_BIN}" auth generate-dev-token --output_to "${raw_token_file}" >/dev/null
|
||||
RAW_NSC_TOKEN_FILE="${raw_token_file}" TOKEN_FILE="${token_file}" python3 - <<'PY'
|
||||
ssh \
|
||||
-i "${SSH_KEY}" \
|
||||
-o IdentitiesOnly=yes \
|
||||
-o UserKnownHostsFile="${KNOWN_HOSTS_FILE}" \
|
||||
-o StrictHostKeyChecking=accept-new \
|
||||
"${HOST}" \
|
||||
'sudo -u forgejo-nsc python3 - <<'"'"'PY'"'"'
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
raw = Path(os.environ["RAW_NSC_TOKEN_FILE"]).read_text(encoding="utf-8").strip()
|
||||
if not raw:
|
||||
raise SystemExit("generated Namespace token is empty")
|
||||
payload = {}
|
||||
|
||||
Path(os.environ["TOKEN_FILE"]).write_text(
|
||||
json.dumps({"bearer_token": raw}, indent=2) + "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
PY
|
||||
rm -f "${raw_token_file}"
|
||||
token_json = Path("/var/lib/forgejo-nsc/.config/ns/token.json")
|
||||
if token_json.exists():
|
||||
data = json.loads(token_json.read_text(encoding="utf-8"))
|
||||
session = str(data.get("session_token", "")).strip()
|
||||
if session:
|
||||
payload["session_token"] = session
|
||||
|
||||
token_cache = Path("/var/lib/forgejo-nsc/.config/ns/token.cache")
|
||||
if token_cache.exists():
|
||||
bearer = token_cache.read_text(encoding="utf-8").strip()
|
||||
if bearer:
|
||||
payload["bearer_token"] = bearer
|
||||
|
||||
if not payload:
|
||||
raise SystemExit("forgejo-nsc host does not have a usable Namespace session")
|
||||
|
||||
print(json.dumps(payload, indent=2))
|
||||
PY' > "${token_file}"
|
||||
chmod 600 "${token_file}"
|
||||
elif [[ -f "${token_secret}" ]]; then
|
||||
burrow_decrypt_age_secret_to_temp "${REPO_ROOT}" "${token_secret}" > "${token_file}"
|
||||
|
|
@ -186,7 +197,12 @@ try:
|
|||
except json.JSONDecodeError:
|
||||
parsed = None
|
||||
|
||||
if isinstance(parsed, dict) and isinstance(parsed.get("bearer_token"), str) and parsed["bearer_token"].strip():
|
||||
if isinstance(parsed, dict):
|
||||
bearer = parsed.get("bearer_token")
|
||||
session = parsed.get("session_token")
|
||||
if isinstance(bearer, str) and bearer.strip():
|
||||
raise SystemExit(0)
|
||||
if isinstance(session, str) and session.strip():
|
||||
raise SystemExit(0)
|
||||
|
||||
path.write_text(json.dumps({"bearer_token": raw}, indent=2) + "\n", encoding="utf-8")
|
||||
|
|
|
|||
|
|
@ -88,27 +88,9 @@ if [[ "${ROTATE_PAT}" -eq 1 ]]; then
|
|||
"${SCRIPT_DIR}/provision-forgejo-nsc.sh" --host "${HOST}" --ssh-key "${SSH_KEY}"
|
||||
fi
|
||||
|
||||
token_file="$(
|
||||
burrow_resolve_secret_file \
|
||||
"${REPO_ROOT}" \
|
||||
"" \
|
||||
"" \
|
||||
"${REPO_ROOT}/secrets/forgejo/nsc-token.age"
|
||||
)"
|
||||
dispatcher_file="$(
|
||||
burrow_resolve_secret_file \
|
||||
"${REPO_ROOT}" \
|
||||
"" \
|
||||
"" \
|
||||
"${REPO_ROOT}/secrets/forgejo/nsc-dispatcher-config.age"
|
||||
)"
|
||||
autoscaler_file="$(
|
||||
burrow_resolve_secret_file \
|
||||
"${REPO_ROOT}" \
|
||||
"" \
|
||||
"" \
|
||||
"${REPO_ROOT}/secrets/forgejo/nsc-autoscaler-config.age"
|
||||
)"
|
||||
token_file="${REPO_ROOT}/secrets/forgejo/nsc-token.age"
|
||||
dispatcher_file="${REPO_ROOT}/secrets/forgejo/nsc-dispatcher-config.age"
|
||||
autoscaler_file="${REPO_ROOT}/secrets/forgejo/nsc-autoscaler-config.age"
|
||||
|
||||
for path in "${token_file}" "${dispatcher_file}" "${autoscaler_file}"; do
|
||||
if [[ ! -s "${path}" ]]; then
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -158,10 +158,11 @@ instances:
|
|||
For Burrow, use `Scripts/provision-forgejo-nsc.sh` to mint the Forgejo PAT,
|
||||
generate a Namespace token from the logged-in Namespace account, and refresh
|
||||
`secrets/forgejo/{nsc-token,nsc-dispatcher-config,nsc-autoscaler-config}.age`.
|
||||
The token file is emitted as JSON with a `bearer_token` field so both the
|
||||
Compute API path and the `nsc` CLI fallback can consume the same secret
|
||||
material. The forge host consumes the encrypted secrets through agenix; avoid
|
||||
keeping local plaintext `intake/` copies around.
|
||||
The token file is emitted as JSON with a long-lived `session_token` plus the
|
||||
current `bearer_token`. The `nsc` CLI paths use the session-backed login flow,
|
||||
while the Compute API path can consume the bearer token directly. The forge
|
||||
host consumes the encrypted secrets through agenix; avoid keeping local
|
||||
plaintext `intake/` copies around.
|
||||
|
||||
Long-lived runtime state is now sourced from age-encrypted files:
|
||||
|
||||
|
|
|
|||
|
|
@ -197,6 +197,10 @@ func (d *Dispatcher) LaunchRunner(ctx context.Context, req LaunchRequest) (strin
|
|||
args = append(args, "--", "/bin/sh", "-c", script)
|
||||
|
||||
cmd := exec.CommandContext(ctx, d.opts.BinaryPath, args...)
|
||||
// The Linux `nsc run` path uses the CLI auth flow. Keep using the service
|
||||
// account's refreshed Namespace login session instead of forcing the
|
||||
// short-lived NSC_TOKEN_FILE bearer token into CLI requests.
|
||||
cmd.Env = nscCLIEnv()
|
||||
var buf bytes.Buffer
|
||||
cmd.Stdout = &buf
|
||||
cmd.Stderr = &buf
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue