Documentation
¶
Overview ¶
Package secrets resolves the secrets a service document declares, by name.
A document never holds a secret: it names one (`valkey.passwordSecret: valkey/password`), and `secrets.source` says how a name is delivered (truvity/policy docs/contracts/config.md, section 5):
env the variable SLUIS_SECRET_<NAME>, the name upper-cased with every
character that is not a letter or a digit made an underscore:
`valkey/password` is SLUIS_SECRET_VALKEY_PASSWORD. For a local run.
file the file <root>/<name>, read on every use, so a rotated Secret
mounted by the platform takes effect without a restart.
ssm the SecureString <root>/private/config/<name> of AWS SSM Parameter
Store (layout v3, root `/sluis/<instance>`): every parameter under
the prefix is read at once, decrypted, and read again when it is
older than the refresh (five minutes), so a rotation reaches a
function within that.
The names are the layout's (docs/decisions/0036): `clients/<id>/secret`, `providers/google/<id>/client-id` and `client-secret`, `issuer/state-secret`, `recovery/password`, `directory/<id>/key`, `valkey/password`. Nothing here logs or returns a value in an error.
Index ¶
- Constants
- Variables
- func Check(name string) error
- func CheckEnvNames(names []string) error
- func CheckRoot(root string) error
- func ClientSecret(clientID string) string
- func EnvName(name string) string
- func Named(f *config.Serve) []string
- func ProviderClientID(provider string) string
- func ProviderClientSecret(provider string) string
- type Env
- type File
- type Legacy
- type Location
- type ParametersAPI
- type SSM
- type Source
Constants ¶
const ( KindEnv = "env" KindFile = "file" KindSSM = "ssm" )
The kinds of source, as `secrets.source` spells them.
const ConfigPrefix = "/private/config/"
ConfigPrefix is where, under an installation's root, the secrets a document names live: `<root>/private/config/<name>` (layout v3).
const DefaultMaxStale = 24 * time.Hour
DefaultMaxStale is how old a copy may grow while every read again fails before the source fails closed: an instance whose read access was revoked stops serving the secrets it read, a day later at the latest.
const DefaultRefresh = 5 * time.Minute
DefaultRefresh is how old the SSM source's copy may be before it is read again: a rotated secret reaches every instance within it.
const EnvPrefix = "SLUIS_SECRET_"
EnvPrefix begins every variable the env source reads.
Variables ¶
var ErrNotFound = errors.New("secrets: not delivered")
ErrNotFound is a name the source does not deliver.
Functions ¶
func Check ¶
Check refuses a name that is not one: segments of letters, digits, '.', '_' and '-', separated by '/', none empty and none a dot or two.
func CheckEnvNames ¶
CheckEnvNames refuses names two of which the env source would read from one variable (`a/b` and `a-b` are both SLUIS_SECRET_A_B): one would silently be the other's secret.
func CheckRoot ¶
CheckRoot refuses an SSM root that would nest under another installation's private or export tree, or under layout v2's (/sluis/private, /sluis/export): no segment of it may be `private` or `export`.
func ClientSecret ¶
ClientSecret is a confidential client's secret.
func ProviderClientID ¶
ProviderClientID and ProviderClientSecret are a Google OAuth client's halves.
func ProviderClientSecret ¶
ProviderClientSecret is a Google OAuth client's secret.
Types ¶
type File ¶
type File struct{ Root string }
File reads <Root>/<name> on every use, so a rotated mount is seen at once.
type Legacy ¶
Legacy is what a v1 document named, secret by secret, in front of another source: a v1 document keeps reading the variables and the files it named until it is moved to v2. Dir, when set, holds one file per client (v1's `clientSecretsDir`).
type Location ¶
Location is where a converted v1 document said one secret was: a variable or a file.
type ParametersAPI ¶
type ParametersAPI interface {
GetParametersByPath(ctx context.Context, in *awsssm.GetParametersByPathInput, opts ...func(*awsssm.Options)) (*awsssm.GetParametersByPathOutput, error)
}
ParametersAPI is the part of the SSM client the source calls.
type SSM ¶
type SSM struct {
API ParametersAPI
Root string
Refresh time.Duration
// MaxStale is how old a copy may grow while reads fail. Zero is
// DefaultMaxStale.
MaxStale time.Duration
// Now is the clock; nil is time.Now.
Now func() time.Time
// contains filtered or unexported fields
}
SSM reads every parameter under <Root>/private/config/ at once, decrypted, and again when its copy is older than Refresh.
A read again happens outside the lock, by one caller at a time; everybody else is answered from the copy meanwhile. A read that fails keeps the copy, and the next is not tried before retryAfter. A copy older than MaxStale with every read again failing is not served: the source fails closed.
type Source ¶
type Source interface {
// Get returns the secret, or an error wrapping ErrNotFound when the
// source has none by that name. The value is never in an error.
Get(ctx context.Context, name string) (string, error)
// Describe says where a name is read from, for a log line: never a
// value.
Describe(name string) string
}
Source delivers secrets by name.
func Open ¶
Open builds the source a serve document's `secrets` names: env when it names none. A document converted from v1 reads, in front of it, every secret where v1 said it was. more are names the document does not hold that are read too (each confidential client's): with the env source no two names may share a variable.