Add agentvault with a seed-outpost subcommand
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful

Interactive agents are classifier-blocked from plumbing credentials through
a shell, so seeding an Authentik outpost token into Vault KV needs to happen
inside one binary invocation that never exposes the secret.

- Add cmd/agentvault, a fourth CLI sharing the agentpr Vault AppRole login
  (role_id only, VAULT_ADDR/AGENT_APPROLE_ROLE_ID defaults unchanged).
- Add `agentvault seed-outpost`: read the Authentik API token from
  kv/service/authentik/agent-api-token (field `token`, falling back to
  `api_token`), exact-match the outpost by name via the instances search,
  fetch its key from /api/v3/core/tokens/<identifier>/view_key/ and write it
  to --dest-path under --dest-key.
- Print only the outpost name, token identifier, dest path and new KV
  version; keep secret material out of results, errors and logs.
- Distinguish the failure stages (login, KV read denied, outpost missing,
  view_key, KV write denied) with ErrVaultDenied/ErrVaultNotFound/
  ErrOutpostNotFound sentinels and actionable messages.
- Add internal/agent vaultkv.go (AppRole-authenticated KV-v2 client) and
  authentik.go (outpost search + view_key) for reuse by future flows.
- Cover the happy path, idempotent re-run, field fallback and every failure
  mode with httptest servers, including a leak check on error strings.
- Wire agentvault into the Makefile, build-rpm.sh, nfpm contents, release
  cross-builds/assets, README and AGENTS.md.
This commit is contained in:
2026-08-29 21:03:31 +10:00
parent 60b08f1198
commit 61bb464e32
13 changed files with 982 additions and 16 deletions
+17 -5
View File
@@ -1,8 +1,8 @@
// Package agent holds the plumbing shared by the agent-tools CLIs (agentpr and
// watchpr): obtaining a Gitea token via Vault AppRole, talking to the Gitea
// API, parsing PR references, and deciding when a watched PR changed
// meaningfully. Both tools acquire their Gitea token the same way, so that
// logic lives here once.
// Package agent holds the plumbing shared by the agent-tools CLIs (agentpr,
// watchpr, agentws and agentvault): the Vault AppRole login and its KV-v2
// client, talking to the Gitea and Authentik APIs, parsing PR references, and
// deciding when a watched PR changed meaningfully. Every tool authenticates to
// Vault the same way, so that logic lives here once.
package agent
import (
@@ -23,6 +23,9 @@ const (
// DefaultAgentLogin is the Gitea login of the agent whose own comments are
// ignored by watchpr. Overridable via AGENT_LOGIN.
DefaultAgentLogin = "unkin-agent"
// DefaultAuthentikURL is the Authentik base URL used when AUTHENTIK_URL is
// unset. identity.unkin.net has no DNS record; the k8s name is the real one.
DefaultAuthentikURL = "https://identity.k8s.syd1.au.unkin.net"
)
// VaultAddr returns the configured Vault address (env VAULT_ADDR or the default).
@@ -59,6 +62,15 @@ func AgentLogin() string {
return DefaultAgentLogin
}
// AuthentikURL returns the configured Authentik base URL (env AUTHENTIK_URL or
// the default).
func AuthentikURL() string {
if v := os.Getenv("AUTHENTIK_URL"); v != "" {
return v
}
return DefaultAuthentikURL
}
var (
tokenOnce sync.Once
tokenValue string