55 lines
1.3 KiB
Go
55 lines
1.3 KiB
Go
package smartconfig
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
vaultapi "github.com/hashicorp/vault/api"
|
|
)
|
|
|
|
// VaultResolver retrieves secrets from HashiCorp Vault.
|
|
// Usage: ${VAULT:secret/data/myapp:password}
|
|
type VaultResolver struct{}
|
|
|
|
// Resolve retrieves the secret value from Vault.
|
|
func (r *VaultResolver) Resolve(value string) (string, error) {
|
|
config := vaultapi.DefaultConfig()
|
|
client, err := vaultapi.NewClient(config)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to create Vault client: %w", err)
|
|
}
|
|
|
|
// Expect format: "path:key" e.g., "secret/data/myapp:password"
|
|
parts := strings.SplitN(value, ":", 2)
|
|
if len(parts) != 2 {
|
|
return "", fmt.Errorf("invalid Vault path format, expected PATH:KEY")
|
|
}
|
|
|
|
path := parts[0]
|
|
key := parts[1]
|
|
|
|
secret, err := client.Logical().Read(path)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to read secret from Vault: %w", err)
|
|
}
|
|
|
|
if secret == nil || secret.Data == nil {
|
|
return "", fmt.Errorf("no secret found at path %s", path)
|
|
}
|
|
|
|
// Handle KV v2 format
|
|
data, ok := secret.Data["data"].(map[string]interface{})
|
|
if ok {
|
|
if val, ok := data[key].(string); ok {
|
|
return val, nil
|
|
}
|
|
}
|
|
|
|
// Handle KV v1 format
|
|
if val, ok := secret.Data[key].(string); ok {
|
|
return val, nil
|
|
}
|
|
|
|
return "", fmt.Errorf("key %s not found in secret", key)
|
|
}
|