secretstore

package
v1.147.0 Latest Latest
Warning

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

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

Documentation

Overview

Package secretstore holds the secrets a request references by placeholder (#2051): the record an administrator writes, the rules it is held to, its PostgreSQL store with the value encrypted, and the lookup the api gateway fills a placeholder through.

The value is write-only. No method returns it except the lookup, which the gateway calls as it sends a request, and the lookup reads it from the database on every call, so a rotated value is used from the next call on.

A secret is one of two kinds (#2065): a value, sent where {{secret:<name>}} is written, or an authenticator seed, never sent, whose current one-time code is sent where {{totp:<name>}} is written.

Index

Constants

View Source
const (
	// KindValue is sent as written where {{secret:<name>}} is.
	KindValue = "value"
	// KindTOTP is an authenticator seed (RFC 6238): never sent, and its
	// current code is sent where {{totp:<name>}} is (#2065).
	KindTOTP = "totp"
)

The kinds a secret may be.

View Source
const (
	MaxValueBytes       = 64 * 1024
	MaxDescriptionBytes = 1000
)

Limits on what a write may carry.

Variables

View Source
var ErrInvalid = errors.New("invalid secret")

ErrInvalid is wrapped by every refusal of a write.

View Source
var ErrNotFound = errors.New("secret not found")

ErrNotFound is returned for a name no secret is stored under.

View Source
var ErrNotTOTP = errors.New("not an authenticator seed")

ErrNotTOTP is returned for a current code of a value secret.

Functions

func Allowed

func Allowed(sec Secret, connection, persona string) error

Allowed refuses a use of sec outside its scope.

func ConnectionAllowed added in v1.142.0

func ConnectionAllowed(sec Secret, connection string) error

ConnectionAllowed refuses sec to the configuration of a connection it may not be used by: one its allow_connections does not list, or any connection at all while it names personas, since a connection's credential serves every persona that uses the connection. The admin API asks it when a connection configuration naming the secret is saved (#2066), and the read at send time asks it again, so a secret rescoped afterwards is refused from the next request on.

func Validate

func Validate(w Write, creating bool) error

Validate refuses a write the store would hold wrongly. creating says whether no secret is stored under the name yet, in which case a value is required.

Types

type CurrentCode added in v1.142.0

type CurrentCode struct {
	Code        string      `json:"code" example:"287082"`
	SecondsLeft int         `json:"seconds_left" example:"17"`
	Params      totp.Params `json:"totp"`
}

CurrentCode is a totp secret's code for this moment and how many seconds of its period are left, for an administrator to compare with the phone app before an automation depends on the secret. Showing it issues nothing: the next fill may send the same code.

type Encryptor

type Encryptor interface {
	Encrypt(plaintext string) (string, error)
	Decrypt(ciphertext string) (string, error)
}

Encryptor encrypts and decrypts one value. fieldcrypt.RestFieldEncryptor satisfies it; a nil one stores values as written.

type Secret

type Secret struct {
	Name        string `json:"name"`
	Description string `json:"description"`
	// Kind is "value" or "totp".
	Kind string `json:"kind" enums:"value,totp"`
	// TOTP is a totp secret's code parameters, read from the otpauth URI it
	// was saved as; absent for a value secret. The seed is never returned.
	TOTP             *totp.Params `json:"totp,omitempty"`
	AllowConnections []string     `json:"allow_connections"`
	AllowPersonas    []string     `json:"allow_personas"`
	CreatedBy        string       `json:"created_by"`
	UpdatedBy        string       `json:"updated_by"`
	CreatedAt        time.Time    `json:"created_at"`
	UpdatedAt        time.Time    `json:"updated_at"`
}

Secret is a stored secret as every reader but the gateway sees it: what it is for and where it may go, never its value.

type Source

type Source interface {
	Lookup(ctx context.Context, connection, persona string) secretref.Lookup
}

Source is what the gateway fills placeholders from: the store, or a fake in a test.

type Store

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

Store persists secrets in gateway_secrets.

func NewStore

func NewStore(db *sql.DB, enc Encryptor) *Store

NewStore creates a store. enc may be nil.

func (*Store) Code added in v1.142.0

func (s *Store) Code(ctx context.Context, name string) (CurrentCode, error)

Code returns the current code of the totp secret name.

func (*Store) ConnectionValue added in v1.142.0

func (s *Store) ConnectionValue(ctx context.Context, name, connection string) (string, error)

ConnectionValue reads a secret a connection's own configuration names, rather than a call's placeholder: the key file of a Google service account (#2061), or a {{secret:<name>}} in a credential field (#2066). The value is the connection's credential for every caller, so allow_connections must list the connection, and allow_personas must be empty: the token one key mints is cached and served to every persona the connection serves, so a key cannot be limited to some of them.

func (*Store) Delete

func (s *Store) Delete(ctx context.Context, name string) error

Delete removes a secret, or returns ErrNotFound.

func (*Store) Get

func (s *Store) Get(ctx context.Context, name string) (Secret, error)

Get returns one secret without its value, or ErrNotFound.

func (*Store) List

func (s *Store) List(ctx context.Context) ([]Secret, error)

List returns every secret in name order, without values.

func (*Store) Lookup

func (s *Store) Lookup(ctx context.Context, connection, persona string) secretref.Lookup

Lookup returns the lookup one call fills its placeholders through: a secret is answered only when connection is in its allow_connections and, when it names personas, persona is one of them or the administrator persona. allow_connections holds for every caller: it is where the value may go, not who may ask. Every refusal names the secret and says what would allow it. A name is read once per call however many placeholders name it.

A {{totp:<name>}} placeholder is asked for as secretref.TOTPName(name) and answered with a one-time code (#2065): the seed is recorded on ctx's Redactor so a response that echoes it is redacted, and the code is issued once per period (issueCode). Every placeholder naming the same secret in one call gets the same code.

func (*Store) Put

func (s *Store) Put(ctx context.Context, w Write) (Secret, bool, error)

Put creates or updates a secret and returns it as stored, and whether it was created.

func (*Store) WithAdmin

func (s *Store) WithAdmin(persona string) *Store

WithAdmin names the administrator persona the lookup lets through allow_personas, and returns the store.

func (*Store) WithClock added in v1.142.0

func (s *Store) WithClock(now func() time.Time, sleep func(ctx context.Context, d time.Duration) error) *Store

WithClock replaces the clock and the wait a one-time code is issued by, and returns the store. A test passes a clock it moves itself, so no test waits out a period.

type Write

type Write struct {
	Name             string
	Description      string
	Kind             string
	Value            *string
	AllowConnections []string
	AllowPersonas    []string
	Actor            string
}

Write is one create or update. Value nil on an update keeps the stored value, so an administrator can rescope a secret without retyping it. Kind empty is a value secret on a create and the stored kind on an update; a totp secret's Value is an otpauth://totp URI or a bare base32 seed.

Jump to

Keyboard shortcuts

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