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 secret_path="$2"
|
||||||
local source_path="$3"
|
local source_path="$3"
|
||||||
local agenix_path
|
local agenix_path
|
||||||
local identity_path
|
local backup_file=""
|
||||||
|
|
||||||
if [[ ! -s "${source_path}" ]]; then
|
if [[ ! -s "${source_path}" ]]; then
|
||||||
echo "secret source missing or empty: ${source_path}" >&2
|
echo "secret source missing or empty: ${source_path}" >&2
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
agenix_path="$(burrow_secret_repo_path "${repo_root}" "${secret_path}")"
|
agenix_path="$(burrow_secret_repo_path "${repo_root}" "${secret_path}")"
|
||||||
identity_path="$(burrow_agenix_identity_path "${repo_root}")"
|
if [[ -f "${secret_path}" ]]; then
|
||||||
|
backup_file="$(mktemp "${TMPDIR:-/tmp}/burrow-secret-backup.XXXXXX")"
|
||||||
if [[ -n "${identity_path}" ]]; then
|
cp "${secret_path}" "${backup_file}"
|
||||||
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}"
|
|
||||||
fi
|
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"
|
autoscaler_secret="${REPO_ROOT}/secrets/forgejo/nsc-autoscaler-config.age"
|
||||||
|
|
||||||
if [[ "${REFRESH_TOKEN}" -eq 1 ]]; then
|
if [[ "${REFRESH_TOKEN}" -eq 1 ]]; then
|
||||||
"${NSC_BIN}" auth check-login --duration 20m >/dev/null
|
ssh \
|
||||||
raw_token_file="$(mktemp)"
|
-i "${SSH_KEY}" \
|
||||||
trap 'rm -f "${raw_token_file}"; cleanup' EXIT
|
-o IdentitiesOnly=yes \
|
||||||
"${NSC_BIN}" auth generate-dev-token --output_to "${raw_token_file}" >/dev/null
|
-o UserKnownHostsFile="${KNOWN_HOSTS_FILE}" \
|
||||||
RAW_NSC_TOKEN_FILE="${raw_token_file}" TOKEN_FILE="${token_file}" python3 - <<'PY'
|
-o StrictHostKeyChecking=accept-new \
|
||||||
|
"${HOST}" \
|
||||||
|
'sudo -u forgejo-nsc python3 - <<'"'"'PY'"'"'
|
||||||
import json
|
import json
|
||||||
import os
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
raw = Path(os.environ["RAW_NSC_TOKEN_FILE"]).read_text(encoding="utf-8").strip()
|
payload = {}
|
||||||
if not raw:
|
|
||||||
raise SystemExit("generated Namespace token is empty")
|
|
||||||
|
|
||||||
Path(os.environ["TOKEN_FILE"]).write_text(
|
token_json = Path("/var/lib/forgejo-nsc/.config/ns/token.json")
|
||||||
json.dumps({"bearer_token": raw}, indent=2) + "\n",
|
if token_json.exists():
|
||||||
encoding="utf-8",
|
data = json.loads(token_json.read_text(encoding="utf-8"))
|
||||||
)
|
session = str(data.get("session_token", "")).strip()
|
||||||
PY
|
if session:
|
||||||
rm -f "${raw_token_file}"
|
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}"
|
chmod 600 "${token_file}"
|
||||||
elif [[ -f "${token_secret}" ]]; then
|
elif [[ -f "${token_secret}" ]]; then
|
||||||
burrow_decrypt_age_secret_to_temp "${REPO_ROOT}" "${token_secret}" > "${token_file}"
|
burrow_decrypt_age_secret_to_temp "${REPO_ROOT}" "${token_secret}" > "${token_file}"
|
||||||
|
|
@ -186,7 +197,12 @@ try:
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
parsed = None
|
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)
|
raise SystemExit(0)
|
||||||
|
|
||||||
path.write_text(json.dumps({"bearer_token": raw}, indent=2) + "\n", encoding="utf-8")
|
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}"
|
"${SCRIPT_DIR}/provision-forgejo-nsc.sh" --host "${HOST}" --ssh-key "${SSH_KEY}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
token_file="$(
|
token_file="${REPO_ROOT}/secrets/forgejo/nsc-token.age"
|
||||||
burrow_resolve_secret_file \
|
dispatcher_file="${REPO_ROOT}/secrets/forgejo/nsc-dispatcher-config.age"
|
||||||
"${REPO_ROOT}" \
|
autoscaler_file="${REPO_ROOT}/secrets/forgejo/nsc-autoscaler-config.age"
|
||||||
"" \
|
|
||||||
"" \
|
|
||||||
"${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"
|
|
||||||
)"
|
|
||||||
|
|
||||||
for path in "${token_file}" "${dispatcher_file}" "${autoscaler_file}"; do
|
for path in "${token_file}" "${dispatcher_file}" "${autoscaler_file}"; do
|
||||||
if [[ ! -s "${path}" ]]; then
|
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,
|
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
|
generate a Namespace token from the logged-in Namespace account, and refresh
|
||||||
`secrets/forgejo/{nsc-token,nsc-dispatcher-config,nsc-autoscaler-config}.age`.
|
`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
|
The token file is emitted as JSON with a long-lived `session_token` plus the
|
||||||
Compute API path and the `nsc` CLI fallback can consume the same secret
|
current `bearer_token`. The `nsc` CLI paths use the session-backed login flow,
|
||||||
material. The forge host consumes the encrypted secrets through agenix; avoid
|
while the Compute API path can consume the bearer token directly. The forge
|
||||||
keeping local plaintext `intake/` copies around.
|
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:
|
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)
|
args = append(args, "--", "/bin/sh", "-c", script)
|
||||||
|
|
||||||
cmd := exec.CommandContext(ctx, d.opts.BinaryPath, args...)
|
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
|
var buf bytes.Buffer
|
||||||
cmd.Stdout = &buf
|
cmd.Stdout = &buf
|
||||||
cmd.Stderr = &buf
|
cmd.Stderr = &buf
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue