FEAT-003 core (design: RAF/security/06-standalone-authority.md, approved 2026-06-10). On a host with no fleet server, the trust boundary preserved is root-vs-unprivileged: a root-owned 0600 Ed25519 key signs capability tokens via a new privileged helper invocation; redflag-local membership lets you request a mint, never perform one. Helper gains the mint subcommand: validates forward-only ops, mintable-type allowlist (no agent-self), host agent-id bind, closure shape (64-hex sha256 fail-closed), hard-coded 15-minute gate-evidence freshness with future-dating rejection, override-reason requirement for vulnerable/unreachable/overridden verdicts, duplicate request_id dedupe, journal-before-emission. --init-key / --retire-key manage the authority lifecycle (retire = the fleet-join swap). Deny taxonomy 22-25. Round-trip test proves a minted token passes the execute path's own verification and parses as the wire CapabilityToken. Agent gains POST /v1/actions/approve-update (single-flight, 409/503 mapping): fleet-mode refusal, dnf/apt dry-run closure resolve + hash pin (no pin, no mint), best-effort OSV.dev closure check with honest verdicts (unreachable is never silent-clear), mint via sudo systemd-run mirroring the execute grant, then the unchanged verify+execute path. Provisioning script sets up the journal dir (root:redflag-local 2750 setgid), mint request dir, key init, and the pinned mint sudoers line.
69 lines
3.3 KiB
Shell
Executable file
69 lines
3.3 KiB
Shell
Executable file
#!/bin/bash
|
|
# provision-standalone-authority.sh — set up the local mint authority on a
|
|
# STANDALONE host (no fleet server). Design: RAF/security/06-standalone-authority.md.
|
|
# Build tracking: docs/tasks/FEAT-003-standalone-local-authority.md.
|
|
#
|
|
# Run as root, after the base agent install (agent user, redflag-local group,
|
|
# helper binary, helper sudoers). The future native installers (.rpm/.deb/AUR)
|
|
# call this for standalone installs; fleet installs must NOT run it — fleet
|
|
# hosts have no local authority. Fleet join later runs:
|
|
# redflag-helper mint --retire-key
|
|
#
|
|
# Idempotent: safe to re-run. The key init refuses to overwrite an existing
|
|
# authority by design (retire first).
|
|
|
|
set -euo pipefail
|
|
|
|
AGENT_USER="redflag-agent"
|
|
LOCAL_GROUP="redflag-local"
|
|
HELPER_BIN="/usr/local/bin/redflag-helper"
|
|
JOURNAL_DIR="/var/lib/redflag/journal"
|
|
MINT_REQUEST_DIR="/var/lib/redflag/agent/mint"
|
|
TOKENS_DIR="/var/lib/redflag/agent/tokens"
|
|
SUDOERS_FILE="/etc/sudoers.d/redflag-agent-mint"
|
|
|
|
log() { echo "[INFO] [provision] [standalone-authority] $*"; }
|
|
fail() { echo "[ERROR] [provision] [standalone-authority] $*" >&2; exit 1; }
|
|
|
|
[ "$(id -u)" -eq 0 ] || fail "must run as root"
|
|
[ -x "$HELPER_BIN" ] || fail "helper binary missing at $HELPER_BIN — run the base install first"
|
|
id "$AGENT_USER" &>/dev/null || fail "agent user $AGENT_USER missing — run the base install first"
|
|
getent group "$LOCAL_GROUP" &>/dev/null || fail "group $LOCAL_GROUP missing — run the base install first"
|
|
|
|
# Journal dir: root-owned, setgid redflag-local, group-readable. Files the
|
|
# helper writes 0640 inherit the group via setgid, so the unprivileged agent
|
|
# (a group member) can serve journal entries over the local API while the dir
|
|
# stays root-writable only.
|
|
install -d -m 2750 -o root -g "$LOCAL_GROUP" "$JOURNAL_DIR"
|
|
log "journal dir ready: $JOURNAL_DIR (root:$LOCAL_GROUP 2750)"
|
|
|
|
# Mint request dir: agent writes requests, root (helper) reads them.
|
|
install -d -m 0750 -o "$AGENT_USER" -g "$LOCAL_GROUP" "$MINT_REQUEST_DIR"
|
|
log "mint request dir ready: $MINT_REQUEST_DIR"
|
|
|
|
# Tokens dir should already exist from the base install; ensure it does.
|
|
[ -d "$TOKENS_DIR" ] || install -d -m 0700 -o "$AGENT_USER" -g "$AGENT_USER" "$TOKENS_DIR"
|
|
|
|
# Local authority keypair. --init-key writes the private seed 0600 root at
|
|
# /etc/redflag/authority_local.key and installs the public half into the
|
|
# helper keyring so the execute path trusts what mint signs. Refuses to
|
|
# overwrite an existing authority.
|
|
if [ -f /etc/redflag/authority_local.key ]; then
|
|
log "local authority already provisioned — leaving key untouched"
|
|
else
|
|
"$HELPER_BIN" mint --init-key
|
|
log "local authority created"
|
|
fi
|
|
|
|
# Sudoers: the agent user may invoke exactly the mint command shape, mirroring
|
|
# the execute-path grant. Request and token paths are pinned to their dirs.
|
|
cat > "$SUDOERS_FILE" <<EOF
|
|
# RedFlag standalone authority — mint invocation (FEAT-003).
|
|
# The agent user may request a mint; the gates + root-owned key decide.
|
|
$AGENT_USER ALL=(root) NOPASSWD: /usr/bin/systemd-run --wait --property=ProtectSystem=no -- $HELPER_BIN mint --request-file $MINT_REQUEST_DIR/* --token-out $TOKENS_DIR/*
|
|
EOF
|
|
chmod 0440 "$SUDOERS_FILE"
|
|
visudo -c -f "$SUDOERS_FILE" >/dev/null || fail "sudoers validation failed for $SUDOERS_FILE"
|
|
log "sudoers installed: $SUDOERS_FILE"
|
|
|
|
log "standalone authority provisioned — fleet join later must run: $HELPER_BIN mint --retire-key"
|