// Command agentvault runs deterministic Vault flows for agents in a single // invocation, so credentials are never plumbed through a shell. It authenticates // with the same Vault AppRole as agentpr (role_id only, no secret_id). // // agentvault seed-outpost --outpost --dest-path package main import ( "fmt" "os" "git.unkin.net/unkin/agent-tools/internal/agent" "github.com/spf13/cobra" ) var version = "dev" func main() { if err := newRootCmd().Execute(); err != nil { os.Exit(1) } } // newRootCmd builds the agentvault command tree. Separated from main so tests // can execute it against httptest servers. func newRootCmd() *cobra.Command { root := &cobra.Command{ Use: "agentvault", Short: "Run deterministic Vault flows as the agent AppRole.", Long: "agentvault performs self-contained Vault flows for agents: it logs in with the\nagent AppRole and moves secret material between systems without ever printing it.", Version: version, SilenceUsage: true, } root.SetVersionTemplate("{{.Version}}\n") root.AddCommand(newSeedOutpostCmd(), newVersionCmd()) return root } func newSeedOutpostCmd() *cobra.Command { opts := agent.SeedOutpostOptions{} cmd := &cobra.Command{ Use: "seed-outpost", Short: "Copy an Authentik outpost token into Vault KV", Long: "Read the Authentik API token from Vault KV, resolve the named outpost's\n" + "token_identifier, fetch its key and write it to a Vault KV path. Re-running\n" + "writes a new KV version. The token value is never printed or logged.", SilenceUsage: true, RunE: func(cmd *cobra.Command, args []string) error { opts.VaultAddr = agent.VaultAddr() opts.RoleID = agent.RoleID() res, err := agent.SeedOutpost(opts) if err != nil { return err } out := cmd.OutOrStdout() _, _ = fmt.Fprintf(out, "outpost: %s\n", res.Outpost) _, _ = fmt.Fprintf(out, "token_identifier: %s\n", res.TokenIdentifier) _, _ = fmt.Fprintf(out, "dest: %s/%s\n", res.KVMount, res.DestPath) _, _ = fmt.Fprintf(out, "version: %d\n", res.Version) return nil }, } f := cmd.Flags() f.StringVar(&opts.Outpost, "outpost", "", "Authentik outpost name (required)") f.StringVar(&opts.DestPath, "dest-path", "", "KV-v2 path to write the token to, e.g. kubernetes/namespace/authentik/default/outpost-token (required)") f.StringVar(&opts.DestKey, "dest-key", agent.DefaultDestKey, "Field to write the token under") f.StringVar(&opts.KVMount, "kv-mount", agent.DefaultKVMount, "KV-v2 mount holding both the API token and the destination") f.StringVar(&opts.TokenPath, "token-path", agent.DefaultOutpostTokenPath, "KV-v2 path of the Authentik API token") f.StringVar(&opts.AuthentikURL, "authentik-url", agent.AuthentikURL(), "Authentik base URL") _ = cmd.MarkFlagRequired("outpost") _ = cmd.MarkFlagRequired("dest-path") return cmd } func newVersionCmd() *cobra.Command { return &cobra.Command{ Use: "version", Short: "Print the version", Run: func(cmd *cobra.Command, args []string) { fmt.Println(version) }, SilenceUsage: true, } }