Watch
1
0
Fork
You've already forked RedFlag
0
RedFlag/scripts/provision-standalone-authority.sh
Fimeg e8b7742422 feat: standalone local authority — helper mint mode + local approval flow
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.
2026-06-10 09:04:06 -04:00

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"