Documentation
¶
Overview ¶
Package secrets provides a flexible way to retrieve secrets from various providers with context-aware functionality for timeout and cancellation support.
The package includes built-in providers for retrieving secrets from:
- Environment variables (env://)
- Files (file://)
- Command output (cmd://)
- LastPass password manager (lp://)
The package also supports custom provider registration.
Each secret is identified by a URL-like string namec the Secret Locator (SL). The format is "provider://location" where:
provider: is the scheme for a registered provider (built-in providers include "env", "file", "cmd", and "lp")
location: is specific to the provider (environment variable name, file path, command, or LastPass ID)
Basic Usage ¶
Retrieve a secret using the GetSecret function:
// Get a secret from an environment variable secret, err := secrets.GetSecret("env://API_KEY") if err != nil { return err }
Retrieve a secret value using the GetSecretValue function:
// Get a secret from an environment variable apiKey, err := secrets.GetSecretValue("env://API_KEY") if err != nil { return err }
Context-Aware Functions ¶
For more control over timeouts and cancellation, use the WithContext variants:
// Use a custom context for secret retrieval ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() secret, err := secrets.GetSecretWithContext(ctx, "env://API_KEY") if err != nil { return err } // Or with direct value retrieval apiKey, err := secrets.GetSecretValueWithContext(ctx, "env://API_KEY") if err != nil { return err }
Note: The non-context functions use a default timeout of 10 seconds. The default timeout can be changed by setting the `DefaultTimeout` variable.
Direct SecretLocator Usage ¶
Alternatively, create the secret locator directly:
sl := secrets.NewSecretLocator(secrets.ProviderFile, "/path/to/secret") // Use with context ctx := context.Background() secret, err := sl.Provider.RetrieveSecret(ctx, sl.Location) if err != nil { return err } ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() // Retrieve the secret with the custom timeout secret, err := secrets.GetSecretWithContext(ctx, "lp://My-Secret-Note/Password") if err != nil { // Handle timeout or other errors if errors.Is(err, context.DeadlineExceeded) { log.Fatal("Secret retrieval timed out") } log.Fatalf("Failed to retrieve secret: %v", err) }
## Cancellation
Cancel the operation based on external events:
// Create a cancellable context ctx, cancel := context.WithCancel(context.Background()) // Set up cancellation based on some condition go func() { // Monitor some condition <-someConditionChannel // Cancel the secret retrieval when condition is met cancel() }() // Attempt to retrieve the secret secret, err := secrets.GetSecretWithContext(ctx, "cmd://aws secretsmanager get-secret-value") if err != nil { if errors.Is(err, context.Canceled) { log.Println("Secret retrieval was canceled") } // Handle other errors }
## Inheriting Context from Parent Operations
Propagate context from parent operations:
func processWithSecrets(ctx context.Context) error { // Use the parent context for secret retrieval apiKey, err := secrets.GetSecretValueWithContext(ctx, "env://API_KEY") if err != nil { return fmt.Errorf("failed to get API key: %w", err) } // Use the secret in subsequent operations return callExternalAPI(ctx, apiKey) }
Provider Factory System ¶
The package uses a factory pattern to create provider instances:
1. Providers are registered with a scheme and a factory function 2. The factory function creates a provider instance 3. When GetSecret is called with a URL, the system:
- Parses the SL to extract the scheme and location
- Looks up the provider factory for the scheme
- Creates a provider instance using the factory
- Retrieves the secret using the provider
This extensible design allows for seamless integration of custom secret providers without modifying the core package code.
Creating Custom Providers ¶
You can extend the secret provider system by implementing the Provider interface and registering your custom provider:
// Implement the Provider interface type customProvider struct { location string } func (p *customProvider) RetrieveSecret(ctx context.Context, loc secrets.Location) (*secrets.Secret, error) { p.location = string(loc) // Custom implementation to retrieve the secret // ... secret := getMySecret(p.location) return secrets.NewSecret([]byte(secret), secrets.WithProvider(p.String()), secrets.WithLocation(p.location)), nil } func (p *customProvider) String() string { return "custom" } // NewCustomProvider creates a new custom provider. func NewCustomProvider() *customProvider { return &customProvider{} } // Register the custom provider during initialization func init() { secrets.RegisterProvider("custom", func() Provider { return NewCustomProvider() }) }
After registration, your custom provider can be used with the same API:
secret, err := secrets.GetSecret("custom://some-location")
Integration with Cobra CLI ¶
Example of adding flags to a Cobra command that accept secrets:
import ( "context" "errors" "time" "github.com/spf13/cobra" "github.com/neticdk/go-common/pkg/cli/secrets" ) func NewCommand() *cobra.Command { var secretValue string var timeout int cmd := &cobra.Command{ Use: "your-command", Short: "Command that uses secrets", RunE: func(cmd *cobra.Command, args []string) error { // Use default timeout behavior value, err := secrets.GetSecretValue(secretValue) if err != nil { return err } // Or use context for custom timeout ctx, cancel := context.WithTimeout(cmd.Context(), time.Duration(timeout)*time.Second) defer cancel() value, err = secrets.GetSecretValueWithContext(ctx, secretValue) if err != nil { return err } // Use the secret value in your command // ... return nil }, } // Add timeout flag cmd.Flags().IntVar(&timeout, "timeout", 10, "Timeout in seconds for secret retrieval") // Add flags that accept secrets cmd.Flags().StringVar(&secretValue, "api-key", "", `API key (supports secret references like "env://API_KEY", "file:///path/to/secret", "cmd://command to execute", "lp://lastpass-id", or any custom registered provider)`) return cmd }
Provider-Specific Details ¶
Environment Variables (env://):
- Location is the name of the environment variable
- Example: env://API_KEY
Files (file://):
- Location is the absolute path to the file
- Example: file:///etc/secrets/api-key
Commands (cmd://):
- Location is the command to execute
- The command's output to stdout is used as the secret
- Example: cmd://aws secretsmanager get-secret-value --secret-id my-secret --query SecretString --output text
LastPass (lp://):
- Location is the LastPass item ID
- Requires the LastPass CLI (lpass) to be installed and configured
- Example: lp://My-Secret-Note/Password
Index ¶
- Constants
- Variables
- func GetSecretValue(rawSL string) (string, error)
- func GetSecretValueWithContext(ctx context.Context, rawSL string) (string, error)
- func NewCmdProvider() *cmdProvider
- func NewEnvProvider() *envProvider
- func NewFileProvider() *fileProvider
- func NewLastPassProvider() *lastPassProvider
- func RegisterProvider(scheme Scheme, factory ProviderFactory)
- type Location
- type Provider
- type ProviderFactory
- type Scheme
- type Secret
- type SecretLocator
- type SecretOption
Constants ¶
const SchemeCmd = "cmd"
const SchemeEnv = "env"
const (
SchemeFile = "file"
)
const SchemeLastPass = "lp"
const (
SchemeUnknown = "unknown"
)
Variables ¶
var DefaultTimeout = 10 * time.Second
Functions ¶
func GetSecretValue ¶
GetSecretValue retrieves the value of a secret. The secret locator is in the form: provider://location It sets a default timeout for the operation. See DefaultTimeout.
func GetSecretValueWithContext ¶
GetSecretValueWithContext retrieves the value of a secret. The secret locator is in the form: provider://location
func NewCmdProvider ¶
func NewCmdProvider() *cmdProvider
NewCmdProvider creates a new command provider.
func NewEnvProvider ¶
func NewEnvProvider() *envProvider
NewEnvProvider creates a new environment variable provider.
func NewFileProvider ¶
func NewFileProvider() *fileProvider
NewFileProvider creates a new file provider.
func NewLastPassProvider ¶
func NewLastPassProvider() *lastPassProvider
NewLastPassProvider creates a new LastPass provider.
func RegisterProvider ¶
func RegisterProvider(scheme Scheme, factory ProviderFactory)
Register a new provider type
Types ¶
type Provider ¶
type Provider interface { // RetrieveSecret retrieves a secret from the provider. // It does the actual work of retrieving the secret.. RetrieveSecret(context.Context, Location) (*Secret, error) // Scheme returns the scheme for the provider. Scheme() Scheme }
Provider is an interface that provides a secret.
func NewProvider ¶
NewProvider creates a new provider instance
type ProviderFactory ¶
type ProviderFactory func() Provider
Factory function type for creating providers
type Scheme ¶
type Scheme string
Scheme is a string that represents the scheme of a secret provider.
type Secret ¶
type Secret struct { // Value is the secret value. Value []byte // contains filtered or unexported fields }
Secret stores a secret value along with some data about the secret.
func GetSecret ¶
GetSecret retrieves a secret. The secret locator is in the form: provider://location It sets a default timeout for the operation. See DefaultTimeout.
func GetSecretWithContext ¶
GetSecretWithContext retrieves a secret. The secret locator is in the form: provider://location
func NewSecret ¶
func NewSecret(value []byte, opts ...SecretOption) *Secret
NewSecret creates a new secret.
func (*Secret) DestroyValue ¶
func (s *Secret) DestroyValue()
DestroySecret destroys the secret value.
func (*Secret) GetLocation ¶
GetLocation returns the location of the secret locator.
type SecretLocator ¶
type SecretLocator struct { // Provider implements the provider for the secret. Provider Provider // Scheme is the scheme of the secret provider. Scheme Scheme // Location is the location of the secret within the provider. Location Location }
SecretLocator is a reference to a secret.
func NewSecretLocator ¶
func NewSecretLocator(scheme Scheme, location Location) (*SecretLocator, error)
NewSecretLocator creates a new secret locator.
func Parse ¶
func Parse(rawSL string) (*SecretLocator, error)
Parse parses a secret locator string into a SecretLocator struct.
func (*SecretLocator) GetSecret ¶
func (sl *SecretLocator) GetSecret(ctx context.Context) (*Secret, error)
GetSecret retrieves the secret from the provider. It's a convenience method for retrieving the secret.
func (*SecretLocator) GetSecretValue ¶
func (sl *SecretLocator) GetSecretValue(ctx context.Context) (string, error)
GetSecretValue retrieves the secret value from the provider.
func (*SecretLocator) String ¶
func (sl *SecretLocator) String() string
String returns the string representation of the secret locator.
func (*SecretLocator) Validate ¶
func (i *SecretLocator) Validate() error
Validate validates the secret locator.
type SecretOption ¶
type SecretOption func(*Secret)
SecretOption is a function that configures a secret.
func WithLocator ¶
func WithLocator(sl *SecretLocator) SecretOption
WithLocator sets the secret locator of the secret.