envsecret

package module
v0.0.0-...-c450ac1 Latest Latest
Warning

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

Go to latest
Published: Jul 19, 2019 License: MIT Imports: 8 Imported by: 0

README

pkg envsecret

tl;dr Exposes envsecret.Process, designed to follow envconfig.Process and retrieve secrets from stores such as Vault and AWS Secrets Manager.

Summary

Package secret wraps interaction with secret stores and is designed to complement envconfig, though it can work independently. Local, Vault and AWS Secrets Manager stores are included, but custom stores can implement envsecret.Store.

Define a configuration specification suitable for envconfig, but use an implementation envsecret.Secret in place of any remotely stored secrets. The package provides a few implementations for common use cases, e.g. envsecret.String, envsecret.Login, and others.

Instead of configuring the secret value in the environment variable, configure it's secret identifier, as defined by the given secret store: e.g. an ARN if using AWS Secrets Manager or a Vault secret path.

Processing by envconfig should populate each secret's identifier by by implementing envconfig.Decoder. Then, envsecret.Process will retrieve each requested secret and populate it according to it's Decode method.

By default, secrets are not required. This means an error will only be returned if a secret is marked as required:"true" in the configuration struct tags.

Example

Given the following environment configuration and secrets configured in AWS Secrets Manager:

APP_DEBUG=true
AWS_REGION=us-west-2
APP_SOME_SECRET=somesecret-name-in-aws
APP_REQUIRED_SECRET=requiredsecret-name-in-aws
APP_ANOTHER_SECRET=somesecret-name-in-aws
APP_PUBLIC_KEY=mykeypair
APP_CREDENTIALS=database-credentials-123

The code below will populate the configuration struct with the requested secret values:

// Configure a specification struct for envconfig.
type Config struct {

    // Non-secret types can be mixed freely with secret types.
    Debug  bool
    Region string `envconfig:"AWS_REGION" default:"us-east-1"`

    // SomeSecret will default to the "value" key found in the secret 
    // named "somesecret-name-in-aws"
    SomeSecret envsecret.String `split_words:"true"`

    // RequiredSecret will cause an error if its key "requiredsecret-name-in-aws" 
    // is not present in the config.
    RequiredSecret envsecret.String `split_words:"true" required:"true"`

    // AnotherSecret will also use the secret found at "somesecret-name-in-aws" 
    // but with a different key than the default, "some_other_key"
    AnotherSecret envsecret.String `split_words:"true" secret_keys:"some_other_key"`

    // The PublicKey type expects a base64 encoded key from which 
    // to construct an *rsa.PublicKey.
    PublicKey envsecret.PublicKey `split_words:"true"`

    // Login provides a username and password pair.
    Credentials envsecret.Login
}

func main() {

    // Process as normal with envconfig. This will populate the secret store
    // identifiers necessary for secret retrieval.
    var config Config
    envconfig.MustProcess("app", &config)

    // Set up a secret Store, in this case AWS Secrets Manager.
    awsSession, _ := session.NewSession(aws.NewConfig().WithRegion(config.Region))
    sm := secretsmanager.New(awsSession)
    secretStore := envsecret.NewSecretsManager(sm)

    // Retrieve the secrets from the Store and populate the config 
    // with their secret values.
    envsecret.MustProcess(&config, secretStore)

    // Types implementing Secret determine how to populate themselves via 
    // their implementation of Decode. For example, the envsecret.String type 
    // populates a Value field with the secret string value, and envsecret.PublicKey 
    // populates a Key field with a constructed rsa.PublicKey.
    fmt.Println(config.SomeSecret.Value)
    fmt.Println(config.PublicKey.Key)
}

Documentation

Overview

Example
package main

import (
	"fmt"
	"os"

	"github.com/kelseyhightower/envconfig"

	"github.com/gavincabbage/envsecret"
	secretstore "github.com/gavincabbage/envsecret/store/local"
)

func init() {
	_ = os.Setenv("APP_DEBUG", "true")
	_ = os.Setenv("AWS_REGION", "us-west-2")
	_ = os.Setenv("APP_SOME_SECRET", "somesecret-name-in-aws")
	_ = os.Setenv("APP_REQUIRED_SECRET", "requiredsecret-name-in-aws")
	_ = os.Setenv("APP_ANOTHER_SECRET", "somesecret-name-in-aws")
	_ = os.Setenv("APP_PUBLIC_KEY", "mykeypair")
	_ = os.Setenv("APP_CREDENTIALS", "database-credentials-123")
}

// config is a specification struct for environment secrets.
type config struct {
	// Non-secret types can be mixed freely with secret types.
	Debug  bool
	Region string `envconfig:"AWS_REGION" default:"us-east-1"`

	// SomeSecret will default to the "value" key found in the secret named "somesecret-name-in-aws"
	SomeSecret envsecret.String `split_words:"true"`

	// RequiredSecret will cause an error if its key "requiredsecret-name-in-aws" is not present in the config.
	RequiredSecret envsecret.String `split_words:"true" required:"true"`

	// AnotherSecret will also use the secret found at "somesecret-name-in-aws" but with a different
	// key than the default, "value"
	AnotherSecret envsecret.String `split_words:"true" secret_keys:"some_other_key"`

	// The PublicKey type expects a base64 encoded key and will construct an *rsa.PublicKey from it.
	PublicKey envsecret.PublicKey `split_words:"true"`

	// Login provides a username and password pair.
	Credentials envsecret.Login
}

func main() {

	// Process as normal with envconfig. This will populate the identifiers necessary for secret retrieval.
	var c config
	envconfig.MustProcess("app", &c)

	// TODO this example isn't particularly meaningful if we use a local store, but a real store also isn't practical so...? maybe mock one to illustrate?
	// Set up a secret Store, in this case a dummy local store.
	secretStore := secretstore.New()

	// Retrieve the secrets from the Store and populate the config with their secret values.
	envsecret.MustProcess(&c, secretStore)

	// Types implementing Secret determine how to populate themselves via their implementation of Decode.
	// For example, the envsecret.String type populates a Value field with the secret string value,
	// and envsecret.PublicKey populates a Key field with a constructed rsa.PublicKey.
	fmt.Println(c.SomeSecret.Value)
	fmt.Println(c.PublicKey.Key)
}
Output:

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrMissingID         = errors.New("requires a non-empty retrieval identifier")
	ErrRequiresStructPtr = errors.New("requires a pointer to a config specification struct")
	ErrMaxOneKey         = errors.New("secret type requires at most one override key")
	ErrNoOverride        = errors.New("secret type does not allow key overrides")
)

Functions

func MustProcess

func MustProcess(spec interface{}, store Store)

MustProcess calls Process and panics on any error.

func Process

func Process(spec interface{}, store Store) error

Process takes a pointer to a configuration specification and populates Secrets in the underlying struct.

Types

type Base

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

Base type for all Secret implementations exposed by this package.

func NewBase

func NewBase(id string) Base

NewBase returns a new Base secret with the given identifier.

func (*Base) Decode

func (s *Base) Decode(value string) error

Decode implements envconfig.Decoder and populates id.

func (*Base) ID

func (s *Base) ID() string

ID returns the secret's identifier in the secret store being used, e.g. an ARN if using AWS Secrets Manager or a Vault secret path.

type Login

type Login struct {
	Base
	Username string
	Password string
}

Login contains a username and password.

func NewLogin

func NewLogin(id string) Login

NewLogin builds a new DatabaseLogin type secret with the given id.

func (*Login) Decode

func (l *Login) Decode(secrets map[string]interface{}) error

Decode implements Secret and populates the database credentials and host information.

type Map

type Map struct {
	Base
	Values map[string]interface{}
}

Map contains a map of secret strings, possibly filtered by an allowList.

func NewMap

func NewMap(id string) Map

NewMap builds a new Map type secret with the given id.

func (*Map) Decode

func (m *Map) Decode(secrets map[string]interface{}) error

Decode implements Secret and populates Values with the secret map.

type PrivateKey

type PrivateKey struct {
	Base
	Key *rsa.PrivateKey
}

PrivateKey contains a secret RSA private key.

func NewPrivateKey

func NewPrivateKey(id string) PrivateKey

NewPrivateKey builds a new PrivateKey type secret with the given id.

func (*PrivateKey) Decode

func (k *PrivateKey) Decode(secrets map[string]interface{}) error

Decode implements Secret and populates Key with the constructed private key.

type PublicKey

type PublicKey struct {
	Base
	Key *rsa.PublicKey
}

PublicKey contains an RSA public key.

func NewPublicKey

func NewPublicKey(id string) PublicKey

NewPublicKey builds a new PublicKey type secret with the given id.

func (*PublicKey) Decode

func (k *PublicKey) Decode(secrets map[string]interface{}) error

Decode implements Secret and populates Key with the constructed public key.

type Secret

type Secret interface {
	// Decode should populate the secret based on the values in the string map returned by
	// the secrets provider. For example, PublicKey constructs an *rsa.PublicKey
	// from the raw base64 encoded string it looks for in the map.
	Decode(map[string]interface{}) error
	// ID should return the identifier of the secret to be retrieved.
	ID() string
}

Secret is the interface considered by Process. Custom secret types can implement this interface to be populated by Process.

type Store

type Store interface {
	// Get should return the map of secret values for the given identifier.
	Get(string) (map[string]interface{}, error)
}

Store of secrets.

type String

type String struct {
	Base
	Value string
}

String is a general purpose secret and holds a single string value.

func NewString

func NewString(id string) String

NewString builds a new String type secret with the given id.

func (*String) Decode

func (s *String) Decode(secrets map[string]interface{}) error

Decode implements Secret and populates Key with the secret string.

Directories

Path Synopsis
store

Jump to

Keyboard shortcuts

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