From 5821eafb05ab79cb84ba1530485f812d91d8a423 Mon Sep 17 00:00:00 2001 From: rcourtman Date: Mon, 17 Aug 2026 06:36:52 +0100 Subject: [PATCH] fix(api): log when the agent command config gate suppresses remote commands The gate that mirrors command-channel admission (commandConfigAllowedForToken) runs on every agent report, before the agent ever attempts channel registration. When it refuses, the server silently serves commandsEnabled=false, the agent never tries to register, so none of the channel rejection warnings fire, and an operator who enabled lifecycle management and granted agent:exec sees "Remote commands: Not enabled" with nothing in any log. Emit the refusal and the binding metadata it was judged against so the mismatch is diagnosable from journalctl. Refs #1728 Contract-Neutral: diagnostic logging only in agent command config gate, no behavioral or public contract change --- internal/api/agent_ingest.go | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/internal/api/agent_ingest.go b/internal/api/agent_ingest.go index ab17a305d..59bd51373 100644 --- a/internal/api/agent_ingest.go +++ b/internal/api/agent_ingest.go @@ -551,6 +551,11 @@ func commandConfigAllowedForToken(record *config.APITokenRecord, host models.Hos return true } if !record.HasScope(config.ScopeAgentExec) { + log.Warn(). + Str("token_id", record.ID). + Str("agent_id", host.ID). + Str("hostname", host.Hostname). + Msg("Suppressing enabled remote commands for agent: token missing required scope agent:exec") return false } @@ -558,7 +563,23 @@ func commandConfigAllowedForToken(record *config.APITokenRecord, host models.Hos // commands are enabled when its channel registration would be rejected // strands the host on "Remote control blocked" (the agent reports // CommandsEnabled=true forever while no channel can be admitted). - return evaluateAgentExecBinding(record, host.ID, host.Hostname).admit + if !evaluateAgentExecBinding(record, host.ID, host.Hostname).admit { + // This gate runs before the agent ever attempts command-channel + // registration, so without a log here a refused binding leaves no + // trace anywhere: the agent never learns commands were requested and + // the channel rejection warnings never fire (#1728). + log.Warn(). + Str("token_id", record.ID). + Str("agent_id", host.ID). + Str("hostname", host.Hostname). + Str("bound_agent_id", strings.TrimSpace(record.Metadata["bound_agent_id"])). + Str("bound_hostname", strings.TrimSpace(record.Metadata["bound_hostname"])). + Str("install_type", strings.TrimSpace(record.Metadata["install_type"])). + Str("issued_via", strings.TrimSpace(record.Metadata["issued_via"])). + Msg("Suppressing enabled remote commands for agent: exec binding would reject this token and agent identity") + return false + } + return true } func (h *UnifiedAgentHandlers) ensureAgentTokenMatch(w http.ResponseWriter, r *http.Request, agentID string) bool {