Watch
1
0
Fork
You've already forked RedFlag
0

feat: mint desktop-self tokens on agent update (UPDATE-002)

This commit is contained in:
Fimeg 2026-06-11 08:24:46 -04:00
commit b2319e15ed
3 changed files with 83 additions and 11 deletions

View file

@ -28,6 +28,7 @@ type AgentUpdateHandler struct {
nonceService *services.UpdateNonceService
agentHandler *AgentHandler
securitySettings *services.SecuritySettingsService // optional; reads policy.require_nonce
capabilityTokens *queries.CapabilityTokenQueries // optional; enables desktop-self minting (UPDATE-002)
}
// NewAgentUpdateHandler creates a new agent update handler
@ -49,6 +50,57 @@ func (h *AgentUpdateHandler) SetSecuritySettings(s *services.SecuritySettingsSer
h.securitySettings = s
}
// SetCapabilityTokenQueries enables desktop-self token minting (UPDATE-002).
// Nil leaves agent updates working without tray delivery.
func (h *AgentUpdateHandler) SetCapabilityTokenQueries(q *queries.CapabilityTokenQueries) {
h.capabilityTokens = q
}
// mintDesktopSelfToken creates and stores a desktop-self capability token for
// the given agent+version so the agent's polling loop picks it up and upgrades
// the systray app. Nil capabilityTokens (not wired) or a missing desktop
// package for this version are not errors — the agent update proceeds without
// desktop delivery.
func (h *AgentUpdateHandler) mintDesktopSelfToken(agentID uuid.UUID, version string) error {
if h.capabilityTokens == nil {
return nil
}
desktopPkg, err := h.agentUpdateQueries.GetUpdatePackageByVersion(version, "desktop-linux", "amd64")
if err != nil || desktopPkg == nil {
return nil // no desktop package for this version — normal
}
closure := []capability.ClosureEntry{{
Name: "redflag-desktop",
Version: version,
SHA256: desktopPkg.Checksum,
Source: "desktop-self",
}}
now := time.Now().UTC()
token := &capability.Token{
Version: capability.Version,
TokenID: uuid.Must(uuid.NewV4()).String(),
AgentID: agentID.String(),
PackageType: "desktop-self",
Operation: "upgrade",
Closure: closure,
IssuedAt: now.Unix(),
NotBefore: now.Unix(),
ExpiresAt: now.Add(24 * time.Hour).Unix(), // desktop restart may lag
}
if err := h.signingService.SignCapabilityToken(token); err != nil {
return fmt.Errorf("sign desktop-self token: %w", err)
}
if err := h.capabilityTokens.Insert(token, uuid.Nil); err != nil {
return fmt.Errorf("store desktop-self token: %w", err)
}
log.Printf("[INFO] [server] [agent_update] desktop_self_token_minted agent_id=%s version=%s token_id=%s", agentID, version, token.TokenID)
return nil
}
// mintAgentSelfToken signs an agent-self capability token authorizing the
// privileged helper to swap the agent binary to the given version/checksum. The
// helper verifies this signature and the new binary's hash before installing.
@ -294,6 +346,12 @@ func (h *AgentUpdateHandler) UpdateAgent(c *gin.Context) {
return
}
// Mint a desktop-self token so the agent's polling loop also upgrades the
// systray app. Non-fatal: the agent update proceeds without desktop delivery.
if err := h.mintDesktopSelfToken(agentIDUUID, req.Version); err != nil {
log.Printf("[WARNING] [server] [agent_update] desktop_self_token_failed agent_id=%s error=%v", agentIDUUID, err)
}
// Create update command for agent
commandType := "update_agent"
commandParams := map[string]interface{}{
@ -466,6 +524,11 @@ func (h *AgentUpdateHandler) BulkUpdateAgents(c *gin.Context) {
continue
}
// Desktop-self token: non-fatal, delivered via the capability-token poll.
if err := h.mintDesktopSelfToken(agentID, req.Version); err != nil {
log.Printf("[WARNING] [server] [agent_update] desktop_self_token_failed agent_id=%s error=%v", agentID, err)
}
// Create update command
command := &models.AgentCommand{
ID: uuid.Must(uuid.NewV4()),