sec: fail-closed verification + server-issued enrollment (v1.4.0)

Implements protocol v1.2.0.

verifySignature() returned true when no public_key was configured,
logging "skipping signature verification". The SSO installer never wrote
a public_key, so a default install executed reboot, service_restart,
configure_ldap, arbitrary_bash and update_binary UNVERIFIED from anything
that could reach its socket. An agent that cannot verify now refuses.

Canonicalization also disagreed with the server. Go's encoding/json
escapes <, > and & by default; JSON.stringify does not. Any payload
containing them hashed differently on each side and failed verification
-- for arbitrary_bash that is most real scripts (`>` redirection, `&&`).
Now uses json.Encoder with SetEscapeHTML(false), trailing newline
trimmed.

The SSO now rejects tokens it did not issue. Handles its close codes
(4001/4002/4003/4004) and backs off 5 minutes on an enrollment failure
instead of retrying every 5s forever. The connect log no longer prints
the URL, which carried ?token=.

install.sh gains --public-key and warns when none is configured.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-05 18:43:26 -04:00
parent 51750d01ec
commit d47d08ecab
9 changed files with 406 additions and 26 deletions
+23 -1
View File
@@ -50,6 +50,7 @@ install_sssd_deps() {
# 2. Argument Parsing
URL=""
TOKEN=""
PUBLIC_KEY=""
B64_CONFIG=""
INSTALL_SSSD=0
@@ -63,6 +64,14 @@ while [[ $# -gt 0 ]]; do
TOKEN="$2"
shift 2
;;
# Base64 of the SSO's raw Ed25519 public key. The agent verifies high-risk
# commands (reboot, configure_ldap, arbitrary_bash, update_binary) against
# it and REFUSES them when it is absent, so an install without this key can
# stream telemetry but cannot be acted on.
--public-key)
PUBLIC_KEY="$2"
shift 2
;;
--install-sssd|--ldap)
INSTALL_SSSD=1
shift
@@ -79,7 +88,10 @@ if [ -z "$B64_CONFIG" ] && [ -z "$URL" ] || [ -z "$B64_CONFIG" ] && [ -z "$TOKEN
error "Missing required configuration. Either provide a base64 encoded config, or both --url and --token."
echo "Usage examples:"
echo " sh install.sh \"BASE64_CONFIG\""
echo " sh install.sh --url \"https://sso.local\" --token \"secret-token\" --install-sssd"
echo " sh install.sh --url \"https://sso.local\" --token \"ISSUED_TOKEN\" --public-key \"BASE64_KEY\" --install-sssd"
echo ""
echo "The token must be issued by the SSO (Directory -> Install Agent enrolls"
echo "the host and mints it). Tokens the server did not issue are rejected."
exit 1
fi
@@ -104,6 +116,7 @@ else
cat <<EOF > "$CONFIG_FILE"
server_url: "$URL"
auth_token: "$TOKEN"
public_key: "$PUBLIC_KEY"
location: "unknown"
capabilities:
telemetry: true
@@ -115,6 +128,15 @@ EOF
fi
chmod 600 "$CONFIG_FILE"
# An agent with no public_key cannot verify signed commands and will refuse
# every one of them. That is the safe default, but it is silent at run time, so
# say it plainly here where the operator is watching.
if ! grep -qE '^public_key:[[:space:]]*"[^"]+"' "$CONFIG_FILE" 2>/dev/null; then
log "WARNING: no public_key configured — this agent will report telemetry but"
log " REFUSE reboot / configure_ldap / arbitrary_bash / update_binary."
log " Re-run with --public-key \"<base64 key>\" (shown at enrollment)."
fi
# 4b. Ensure SSSD dependencies are installed if configure_ldap is enabled
if [ "$INSTALL_SSSD" -eq 1 ] || grep -q -i "configure_ldap:\s*true" "$CONFIG_FILE" 2>/dev/null; then
install_sssd_deps