Skip to content

feat: Add vault-fetcher CLI tool (Phase 2)#380

Draft
Alan-Cha wants to merge 1 commit into
feat/vault-integrationfrom
feat/vault-fetcher-cli
Draft

feat: Add vault-fetcher CLI tool (Phase 2)#380
Alan-Cha wants to merge 1 commit into
feat/vault-integrationfrom
feat/vault-fetcher-cli

Conversation

@Alan-Cha

@Alan-Cha Alan-Cha commented May 7, 2026

Copy link
Copy Markdown
Member

Summary

Phase 2 of the Vault pattern: vault-fetcher CLI tool for fetching secrets from Vault at pod startup.

Stacked on: #379 (Phase 1 - authlib/vault library)

This PR builds the init container that uses the authlib/vault library to fetch secrets and write them to files.

What This Adds

vault-fetcher CLI Tool

A standalone binary that runs as an init container:

  1. Reads configuration (which secrets to fetch)
  2. Authenticates to Vault using SPIFFE JWT-SVID
  3. Fetches secrets using authlib/vault
  4. Writes secrets to files with secure permissions
  5. Exits (application container starts)

Features

  • Uses authlib/vault — All Vault operations via Phase 1 library
  • Multiple auth methods — JWT (SPIFFE), Kubernetes SA, token
  • Batch fetching — Fetch multiple secrets in one run
  • Flexible output — Individual files, env file, JSON
  • Secure by default — File mode 0600, runs as non-root (UID 65532)
  • Retry logic — 3 auth attempts with exponential backoff
  • Env var overrides — VAULT_ADDR, VAULT_ROLE, etc.
  • Fail-fast — Clear errors, non-zero exit codes

Files Added

  • `vault-fetcher/main.go` (330 lines) — CLI implementation
  • `vault-fetcher/Dockerfile` — Multi-stage distroless image (~20MB)
  • `vault-fetcher/README.md` (450+ lines) — Complete documentation
  • `vault-fetcher/config.yaml.example` — Example configuration
  • `vault-fetcher/go.mod`, `go.sum` — Go module
  • `go.work` — Updated workspace

Total: ~916 lines added

Usage Example

Configuration

```yaml

/etc/vault-fetcher/config.yaml

vault:
address: "https://vault.example.com"
auth_method: "jwt"
role: "github-agent-role"

secrets:

  • path: "secret/data/github/token"
    field: "token"
    output: "/shared/secrets/github-token"
    mode: "0600"
    ```

Deployment

```yaml
apiVersion: v1
kind: Pod
spec:
initContainers:

  • name: vault-fetcher
    image: ghcr.io/kagenti/kagenti-extensions/vault-fetcher:latest
    args:
    • --config=/etc/vault-fetcher/config.yaml
      volumeMounts:
    • name: spiffe-svid
      mountPath: /opt
    • name: shared-secrets
      mountPath: /shared/secrets
    • name: vault-fetcher-config
      mountPath: /etc/vault-fetcher

containers:

  • name: app
    volumeMounts:
    • name: shared-secrets
      mountPath: /shared/secrets
      readOnly: true

    App reads /shared/secrets/github-token

```

Output

```bash
[vault-fetcher] Starting vault-fetcher v0.1.0
[vault-fetcher] Loaded config from /etc/vault-fetcher/config.yaml
[vault-fetcher] Created Vault client (address=https://vault.example.com, auth_method=jwt)
[vault-fetcher] Successfully authenticated to Vault
[vault-fetcher] [1/1] Fetching secret: secret/data/github/token (field: token)
[vault-fetcher] [1/1] Secret fetched (lease: 3600s)
[vault-fetcher] [1/1] Written to: /shared/secrets/github-token
[vault-fetcher] All secrets fetched successfully
```

Testing

```bash
$ cd authbridge/vault-fetcher
$ go build -o vault-fetcher .
$ ./vault-fetcher --version
vault-fetcher version 0.1.0
```

Stacked PR Structure

```
main
└─> feat/vault-integration (PR #379) ← Phase 1: authlib/vault library
└─> feat/vault-fetcher-cli (THIS PR) ← Phase 2: vault-fetcher CLI
```

Merge order:

  1. Merge feat: Add Vault integration library to authlib #379 first (Phase 1 library)
  2. Then this PR will be updated to target `main`
  3. Then merge this PR (Phase 2 CLI)

Next Steps (Not in This PR)

  • Phase 3: Webhook integration (kagenti-operator)
  • Phase 4: Demo scenario, CI/CD pipeline

Review Notes

Key areas to review:

  1. Code quality: Is the CLI implementation clean and maintainable?
  2. Error handling: Are error messages clear and actionable?
  3. Configuration: Is the YAML schema intuitive?
  4. Security: File permissions, container security, secret handling
  5. Documentation: Is the README clear for users?

Questions for reviewers:

  • Should we add more validation in config loading?
  • Any concerns about the retry logic approach?
  • Should we support watching for config changes (vs one-shot)?

Assisted-By: Claude (Anthropic AI) noreply@anthropic.com

Implement standalone CLI tool for fetching secrets from Vault at pod startup.

This completes Phase 2 of the Vault pattern implementation. vault-fetcher
is an init container that:
- Authenticates to Vault using SPIFFE JWT-SVID (or K8s SA, or token)
- Fetches configured secrets from Vault
- Writes secrets to files with secure permissions
- Supports multiple output formats (individual files, env file, JSON)

Features:
- Uses authlib/vault library for all Vault operations
- Configuration via YAML file with env var overrides
- Retry logic for authentication (3 attempts with backoff)
- Fail-fast with clear error messages
- Minimal container image (distroless, ~20MB)
- Runs as non-root (UID 65532)

Files added:
- vault-fetcher/main.go (330 lines) — CLI implementation
- vault-fetcher/config.yaml.example — Example configuration
- vault-fetcher/Dockerfile — Multi-stage distroless image
- vault-fetcher/README.md (450+ lines) — Complete documentation
- vault-fetcher/go.mod, go.sum — Go module files
- go.work — Updated workspace to include vault-fetcher

Usage:
  vault-fetcher --config=/etc/vault-fetcher/config.yaml

Next steps (Phase 3):
- Webhook integration for automatic injection
- Demo scenario with real agent
- CI/CD pipeline for container image

Ref: #vault-pattern
Stacked on: #379

Signed-off-by: Alan Cha <Alan.cha1@ibm.com>
@Alan-Cha

Alan-Cha commented May 7, 2026

Copy link
Copy Markdown
Member Author

Closing to recreate as proper stacked PR using gh-stack

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants