Vault Config Loader
A complete application that authenticates to HashiCorp Vault (auto-detecting Kubernetes vs AppRole environment), reads secrets from KV v2, and populates a typed config struct for the application to use.
Input: Vault address and auth credentials (AppRole role/secret IDs or Kubernetes service account).
Output: A populated AppConfig struct with database, Redis, and API credentials ready for use.
package main
import (
"context"
"fmt"
"os"
vault "github.com/hashicorp/vault/api"
)
type AppConfig struct {
DBHost string
DBUser string
DBPassword string
DBName string
RedisURL string
APIKey string
}
func loginAppRole(client *vault.Client, roleID, secretID string) error {
data := map[string]interface{}{
"role_id": roleID,
"secret_id": secretID,
}
resp, err := client.Logical().Write("auth/approle/login", data)
if err != nil {
return fmt.Errorf("approle login: %w", err)
}
client.SetToken(resp.Auth.ClientToken)
return nil
}
func loginKubernetes(client *vault.Client, role string) error {
jwt, err := os.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/token")
if err != nil {
return fmt.Errorf("reading SA token: %w", err)
}
data := map[string]interface{}{
"jwt": string(jwt),
"role": role,
}
resp, err := client.Logical().Write("auth/kubernetes/login", data)
if err != nil {
return fmt.Errorf("k8s auth: %w", err)
}
client.SetToken(resp.Auth.ClientToken)
return nil
}
func loadConfig(ctx context.Context) (*AppConfig, error) {
// Initialize client
config := vault.DefaultConfig()
config.Address = os.Getenv("VAULT_ADDR")
client, err := vault.NewClient(config)
if err != nil {
return nil, err
}
// Authenticate with AppRole (CI/CD) or Kubernetes (pods)
if os.Getenv("KUBERNETES_SERVICE_HOST") != "" {
err = loginKubernetes(client, "myapp")
} else {
err = loginAppRole(client, os.Getenv("VAULT_ROLE_ID"), os.Getenv("VAULT_SECRET_ID"))
}
if err != nil {
return nil, fmt.Errorf("vault auth: %w", err)
}
// Read static secrets
dbSecrets, err := client.KVv2("secret").Get(ctx, "myapp/db")
if err != nil {
return nil, fmt.Errorf("read db secrets: %w", err)
}
redisSecrets, err := client.KVv2("secret").Get(ctx, "myapp/redis")
if err != nil {
return nil, fmt.Errorf("read redis secrets: %w", err)
}
// Build config struct
cfg := &AppConfig{
DBHost: dbSecrets.Data["host"].(string),
DBUser: dbSecrets.Data["username"].(string),
DBPassword: dbSecrets.Data["password"].(string),
DBName: dbSecrets.Data["dbname"].(string),
RedisURL: redisSecrets.Data["url"].(string),
APIKey: redisSecrets.Data["api_key"].(string),
}
return cfg, nil
}
func main() {
ctx := context.Background()
cfg, err := loadConfig(ctx)
if err != nil {
fmt.Fprintf(os.Stderr, "config load failed: %v\n", err)
os.Exit(1)
}
fmt.Printf("Connected to %s@%s/%s\n", cfg.DBUser, cfg.DBHost, cfg.DBName)
// Start application with cfg...
}