security

package
v0.1.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 10, 2026 License: Apache-2.0 Imports: 17 Imported by: 0

README ΒΆ

πŸ” pkg/security β€” Enterprise Secret Management

Zero-Trust Secret Resolution for production-grade AI agent deployments.

Overview

The security package provides a centralized, pluggable secret management layer that replaces scattered os.Getenv calls with a single SecretProvider interface. Secrets can be resolved from cloud-native secret stores (GCP Secret Manager, AWS Secrets Manager, Azure Key Vault, HashiCorp Vault, etc.) or fall back to environment variables for local development β€” all without changing a single line of application code.

Built on gocloud.dev/runtimevar, the package supports any backend with a Go CDK URL scheme.

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                       SecretProvider                              β”‚
β”‚                    (interface: GetSecret)                         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                              β”‚                                    β”‚
β”‚     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”       β”‚       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”‚
β”‚     β”‚  envProvider    β”‚       β”‚       β”‚  Manager             β”‚     β”‚
β”‚     β”‚                β”‚       β”‚       β”‚                      β”‚     β”‚
β”‚     β”‚  os.Getenv()   β”‚       β”‚       β”‚  runtimevar URL β†’    β”‚     β”‚
β”‚     β”‚  zero-config   β”‚       β”‚       β”‚  lazy open + cache   β”‚     β”‚
β”‚     β”‚  backward      β”‚       β”‚       β”‚  env fallback        β”‚     β”‚
β”‚     β”‚  compatible    β”‚       β”‚       β”‚                      β”‚     β”‚
β”‚     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜       β”‚       β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β”‚
β”‚                              β”‚              β”‚                     β”‚
β”‚                              β”‚     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”          β”‚
β”‚                              β”‚     β”‚ gocloud.dev       β”‚          β”‚
β”‚                              β”‚     β”‚ runtimevar        β”‚          β”‚
β”‚                              β”‚     β”‚                   β”‚          β”‚
β”‚                              β”‚     β”‚ β€’ GCP SM          β”‚          β”‚
β”‚                              β”‚     β”‚ β€’ AWS SM          β”‚          β”‚
β”‚                              β”‚     β”‚ β€’ Azure KV        β”‚          β”‚
β”‚                              β”‚     β”‚ β€’ File            β”‚          β”‚
β”‚                              β”‚     β”‚ β€’ Constant (test) β”‚          β”‚
β”‚                              β”‚     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Quick Start

Local Development (zero-config)

No configuration needed. The default envProvider reads from environment variables:

sp := security.NewEnvProvider()
val, err := sp.GetSecret(ctx, "OPENAI_API_KEY")
// equivalent to os.Getenv("OPENAI_API_KEY")
Production (cloud secret store)

Configure the [security] section in your .genie.toml:

[security]

[security.secrets]
OPENAI_API_KEY    = "gcpsecretmanager://projects/my-project/secrets/openai-key?decoder=string"
ANTHROPIC_API_KEY = "awssecretsmanager://my-secret?region=us-east-1&decoder=string"
LANGFUSE_SECRET   = "azurekeyvault://my-vault.vault.azure.net/secrets/langfuse?decoder=string"

Then in Go:

mgr := security.NewManager(cfg.Security)
defer mgr.Close()

// Resolves from GCP Secret Manager, AWS, Azure, etc.
val, err := mgr.GetSecret(ctx, "OPENAI_API_KEY")

// Unmapped secrets fall back to os.Getenv
val, err := mgr.GetSecret(ctx, "SOME_LOCAL_VAR")

Components

SecretProvider (interface)
type SecretProvider interface {
    GetSecret(ctx context.Context, name string) (string, error)
}

The core contract. Every component that needs a secret depends on this interface, making it trivial to mock in tests.

envProvider
  • Created via NewEnvProvider()
  • Wraps os.Getenv β€” always returns "" with nil error for missing vars
  • Default for local development and backward compatibility
Manager
  • Created via NewManager(cfg Config) *Manager
  • Resolves secrets from gocloud.dev/runtimevar URL mappings
  • Lazy loading: Variables are opened on first access and cached
  • Thread-safe: Uses sync.RWMutex with double-check locking
  • Env fallback: Unmapped secret names fall through to os.Getenv
  • Must close: Call Close() to release runtimevar connections
Config
type Config struct {
    Secrets map[string]string `yaml:"secrets" toml:"secrets"`
}

Maps secret names to runtimevar URL strings.

Supported Backends

Backend URL Scheme Example
GCP Secret Manager gcpsecretmanager:// gcpsecretmanager://projects/p/secrets/s?decoder=string
AWS Secrets Manager awssecretsmanager:// awssecretsmanager://my-secret?region=us-east-1&decoder=string
AWS Parameter Store awsparamstore:// awsparamstore://my-param?region=us-east-1&decoder=string
Azure Key Vault azurekeyvault:// azurekeyvault://vault.vault.azure.net/secrets/name?decoder=string
etcd etcd:// etcd://my-key?decoder=string
File (watch) file:// file:///etc/secrets/api-key?decoder=string
Constant (testing) constant:// constant://?val=test-value&decoder=string

Note: Each backend requires the corresponding Go CDK driver import in your main.go or an init package. Without the import, runtimevar.OpenVariable will return "no driver registered" at runtime.

Required Driver Imports

Add blank imports for the backends you use:

import (
    // Always available (tests + local dev)
    _ "gocloud.dev/runtimevar/constantvar"

    // Production backends β€” import only what you need:
    _ "gocloud.dev/runtimevar/gcpsecretmanager"  // GCP
    _ "gocloud.dev/runtimevar/awssecretsmanager" // AWS Secrets Manager
    _ "gocloud.dev/runtimevar/awsparamstore"     // AWS Parameter Store
    // _ "gocloud.dev/runtimevar/azurekeyvault"   // Azure (requires additional setup)
    // _ "gocloud.dev/runtimevar/etcdvar"         // etcd
    _ "gocloud.dev/runtimevar/filevar"            // File-based (watched)
)

The constantvar driver is imported by manager.go automatically. Cloud drivers are not imported by default to avoid pulling in cloud SDKs that projects may not need.

Testing

Using the constant:// driver
cfg := security.Config{
    Secrets: map[string]string{
        "MY_SECRET": "constant://?val=test-value&decoder=string",
    },
}
mgr := security.NewManager(cfg)
defer mgr.Close()

val, err := mgr.GetSecret(ctx, "MY_SECRET")
// val == "test-value"
Using counterfeiter fakes
//go:generate go tool counterfeiter -generate

mockSP := &fakes.FakeSecretProvider{}
mockSP.GetSecretReturns("my-api-key", nil)

val, _ := mockSP.GetSecret(ctx, "OPENAI_API_KEY")
// val == "my-api-key"

File Index

File Purpose
provider.go SecretProvider interface + counterfeiter annotation
manager.go Manager β€” runtimevar-backed provider with caching
env_provider.go envProvider β€” zero-config os.Getenv fallback
config.go Config struct for YAML/TOML deserialization
fakes.go Counterfeiter go:generate directive
security_test.go Ginkgo tests (13 specs)
init_test.go Ginkgo suite bootstrap

Documentation ΒΆ

Overview ΒΆ

Package security provides a centralized SecretProvider abstraction backed by gocloud.dev/runtimevar. It replaces scattered os.Getenv calls with a single injectable interface that can resolve secrets from GCP Secret Manager, AWS Secrets Manager, etcd, HashiCorp Vault, local files, or plain environment variables β€” controlled entirely by URL scheme.

A singleton SecretProvider is created during app.Bootstrap and injected into every component that needs secrets. Without this package, each component would independently read os.Getenv, making it impossible to use external secret stores without modifying every call site.

Index ΒΆ

Constants ΒΆ

View Source
const (
	// MinRSAKeyBits is the minimum RSA key size (bits) per NIST through 2030.
	MinRSAKeyBits = 2048
	// MinECDSAKeyBits is the minimum ECDSA curve size (bits) per NIST through 2030.
	MinECDSAKeyBits = 224
)

NIST SP 800-131A (2012) minimum key lengths and algorithm requirements through 2030. Defaults in this package meet those requirements.

Variables ΒΆ

This section is empty.

Functions ΒΆ

This section is empty.

Types ΒΆ

type Config ΒΆ

type Config struct {
	// Secrets maps logical secret names (matching the env var names used
	// throughout the codebase, e.g. "OPENAI_API_KEY") to runtimevar URLs.
	// Any secret name NOT present in this map falls back to os.Getenv.
	// Examples:
	//   "OPENAI_API_KEY": "gcpsecretmanager://projects/p/secrets/openai-key?decoder=string"
	//   "ANTHROPIC_API_KEY": "awssecretsmanager://anthropic-api-key?region=us-east-2&decoder=string"
	//   "SLACK_BOT_TOKEN": "file:///run/secrets/slack-token?decoder=string"
	Secrets map[string]string `yaml:"secrets,omitempty" toml:"secrets,omitempty"`

	// Crypto configures key lengths and algorithm policy (NIST 2030; weak algorithms are always disabled).
	Crypto CryptoConfig `yaml:"crypto,omitempty" toml:"crypto,omitempty"`
}

Config holds the configuration for the secret provider and cryptographic policy. When present in the Genie config, it controls how secrets are resolved at runtime. Without this configuration, the application falls back to reading all secrets from environment variables via os.Getenv.

func (Config) Provider ΒΆ

func (c Config) Provider(ctx context.Context) SecretProvider

type CryptoConfig ΒΆ

type CryptoConfig struct{}

CryptoConfig holds cryptographic policy used by TLS clients and tools. Secure development is the only mode: weak algorithms and small key lengths are always disabled (NIST 2030 minimums).

func DefaultCryptoConfig ΒΆ

func DefaultCryptoConfig() CryptoConfig

DefaultCryptoConfig returns a NIST 2030–compliant default (TLS 1.2+, strong ciphers only).

func (CryptoConfig) TLSConfig ΒΆ

func (c CryptoConfig) TLSConfig() *tls.Config

TLSConfig returns a *tls.Config suitable for TLS clients (HTTP, IMAP, etc.). It enforces minimum TLS 1.2 and strong cipher suites only. All selected cipher suites use ephemeral key agreement (ECDHE), providing perfect forward secrecy so that compromise of a long-term key does not reveal past session keys. Callers must not modify the returned config.

type EnvProviderOption ΒΆ

type EnvProviderOption func(*envProvider)

EnvProviderOption configures an envProvider at construction time.

func WithSecretLookupAuditEnv ΒΆ

func WithSecretLookupAuditEnv(fn func(ctx context.Context, req GetSecretRequest)) EnvProviderOption

WithSecretLookupAudit sets a callback invoked whenever a secret is successfully looked up (GetSecret returns a non-empty value). Use it to audit secret access; the callback receives the logical secret name only, never the value.

type GetSecretRequest ΒΆ

type GetSecretRequest struct {
	Name   string
	Reason string
}

type Manager ΒΆ

type Manager struct {
	// contains filtered or unexported fields
}

Manager is the primary SecretProvider implementation. It resolves secrets from gocloud.dev/runtimevar backends (GCP Secret Manager, AWS Secrets Manager, etcd, files, etc.) and falls back to os.Getenv for any secret name that has no explicit runtimevar URL mapping.

On construction, Manager eagerly resolves every configured secret in a background goroutine and caches the raw values in a ttlcache.Item that is refreshed every rawCacheTTL/2. GetSecret reads from this in-memory cache and applies gjson path extraction on the fly, making individual lookups essentially free after the first fetch.

Without this struct, the application would have no way to dynamically fetch secrets from cloud secret stores at runtime β€” every secret would need to be baked into the process environment before startup.

func NewManager ΒΆ

func NewManager(ctx context.Context, cfg Config, opts ...ManagerOption) *Manager

NewManager creates a Manager from the given Config and optional options. When secrets are configured, a background goroutine eagerly resolves all runtimevar URLs and keeps them fresh via ttlcache.Item.KeepItFresh. If Config.Secrets is empty, the Manager still works β€” it just falls back to os.Getenv for every lookup.

func (*Manager) Close ΒΆ

func (m *Manager) Close() error

Close cancels the background refresh goroutine and releases all open runtimevar.Variable instances. Safe to call multiple times. After Close, GetSecret still works β€” it re-resolves secrets on demand via the ttlcache.Item retriever.

func (*Manager) GetSecret ΒΆ

func (m *Manager) GetSecret(ctx context.Context, req GetSecretRequest) (string, error)

GetSecret retrieves the plaintext value of the named secret. It first checks whether a runtimevar URL mapping exists for the given name. If so, it reads the pre-resolved raw value from the in-memory cache and applies gjson path extraction when the URL contains "&path=<gjson-path>". If no mapping exists, it falls back to os.Getenv with optional gjson extraction via "?path=<gjson-path>" in the secret name.

Errors are returned when the cache cannot be populated, or a requested gjson path does not exist in the JSON. A missing env var returns "" with no error β€” matching the existing behavior callers depend on.

type ManagerOption ΒΆ

type ManagerOption func(*Manager)

ManagerOption configures a Manager at construction time.

func WithSecretLookupAudit ΒΆ

func WithSecretLookupAudit(fn func(ctx context.Context, req GetSecretRequest)) ManagerOption

WithSecretLookupAudit sets a callback invoked whenever a secret is successfully looked up (GetSecret returns a value). Use it to audit secret access; the callback receives the logical secret name only, never the value.

type SecretProvider ΒΆ

type SecretProvider interface {
	// GetSecret retrieves the plaintext value of the named secret.
	// If the secret is not found in the configured backend, the
	// implementation may fall back to os.Getenv(name).
	// Returns an empty string (not an error) when the secret is
	// simply absent β€” callers treat missing secrets as unconfigured
	// features, not failures.
	GetSecret(ctx context.Context, req GetSecretRequest) (string, error)
}

SecretProvider defines the interface for retrieving secret values by name. All secret resolution in the application flows through this interface, enabling portable secret backends (GCP, AWS, Vault, env, etc.) without changing consuming code.

Without this interface, every component that needs a secret would directly couple to os.Getenv or a specific cloud SDK, making it impossible to swap backends or test secret-dependent code in isolation.

func NewEnvProvider ΒΆ

func NewEnvProvider(opts ...EnvProviderOption) SecretProvider

NewEnvProvider creates a SecretProvider that reads all secrets from environment variables. This is the default provider used when no runtimevar mappings are configured. Pass WithSecretLookupAuditEnv to audit successful lookups (e.g. for compliance).

Directories ΒΆ

Path Synopsis
Package auth – oidc.go implements the generic OIDC browser login flow using golang.org/x/oauth2 and coreos/go-oidc.
Package auth – oidc.go implements the generic OIDC browser login flow using golang.org/x/oauth2 and coreos/go-oidc.
Package keyring: device keychain storage for arbitrary key-value secrets.
Package keyring: device keychain storage for arbitrary key-value secrets.
Code generated by counterfeiter.
Code generated by counterfeiter.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL