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.