Documentation
¶
Overview ¶
Package local is the keyring-file provider: KEKs held in an encrypted file on the local disk, for development and single-host deployments.
The provider is reported as "local" in status and in the startup log so it is not mistaken for a managed key service. It is as safe as the host and the source of its root key. It is not an HSM, and where managed keys are required a managed provider is the right one.
Protection ¶
KEK versions are held in a file that is sealed with AES-256-GCM under a key derived from a root key. The root key comes from a RootKeySource and never touches disk beside the keyring. The file is created with mode 0600 and a keyring that other users can read is refused.
Writes ¶
A change is made by taking an advisory lock, re-reading the file, applying the change, writing a temporary file, flushing it, renaming it over the keyring and flushing the directory. A crash leaves the previous keyring or the new one, never a mixture, and concurrent processes do not interleave.
Index ¶
- Constants
- Variables
- func Peek(path string) (generation uint64, rootKeyID string, err error)
- type BackupStatus
- type Options
- type Provider
- func (p *Provider) AckBackup(ctx context.Context, gen uint64, at time.Time, note string) error
- func (p *Provider) BackupStatus(ctx context.Context) (BackupStatus, error)
- func (p *Provider) Close() error
- func (p *Provider) CreateKey(ctx context.Context, spec provider.KeySpec) (provider.KeyRef, error)
- func (p *Provider) Destroy(ctx context.Context, ref provider.KeyRef, ver uint32) error
- func (p *Provider) Generation() uint64
- func (p *Provider) Health(ctx context.Context) error
- func (p *Provider) Name() string
- func (p *Provider) Path() string
- func (p *Provider) RootKeyID() string
- func (p *Provider) Rotate(ctx context.Context, ref provider.KeyRef) (uint32, error)
- func (p *Provider) RotateRootKey(ctx context.Context, next RootKeySource) error
- func (p *Provider) Unwrap(ctx context.Context, ref provider.KeyRef, ver uint32, wrapped, aad []byte) ([]byte, error)
- func (p *Provider) UpToDate(ctx context.Context) (bool, error)
- func (p *Provider) Wrap(ctx context.Context, ref provider.KeyRef, ver uint32, dek, aad []byte) ([]byte, error)
- type RollbackError
- type RootKeySource
Examples ¶
Constants ¶
const DefaultName = "local"
DefaultName is the provider name reported in key references and status.
const RootKeySize = 32
RootKeySize is the size in bytes of a root key.
Variables ¶
var ( // ErrKeyringRolledBack is returned when the keyring on disk has a lower // generation than this host has seen: an older copy was restored over it. // Data wrapped under versions the older copy lacks cannot be unwrapped. ErrKeyringRolledBack = errors.New("local: the keyring is older than one this host has seen") // ErrAckAhead is returned when acknowledging a generation the keyring has // not reached. ErrAckAhead = errors.New("local: cannot acknowledge a generation the keyring has not reached") // ErrAckRegressed is returned when acknowledging a generation lower than // the one already acknowledged. ErrAckRegressed = errors.New("local: cannot acknowledge an older generation than the last acknowledgement") // ErrCorruptBackupState is returned when the backup state file cannot be // read. Remove it to start fresh, which also forgets the high-water mark. ErrCorruptBackupState = errors.New("local: the backup state file is unreadable") )
Typed errors for backup state.
var ( // ErrKeyringNotFound is returned when the keyring file does not exist. ErrKeyringNotFound = errors.New("local: keyring not found") // ErrKeyringExists is returned when creating a keyring over an existing one. ErrKeyringExists = errors.New("local: keyring already exists") // ErrWrongRootKey is returned when the root key is not the one the keyring // was written under. It is reported before decryption is attempted, so a // wrong key is not mistaken for a corrupted file. ErrWrongRootKey = errors.New("local: wrong root key for this keyring") // ErrCorruptKeyring is returned for a file that is not a keyring this // build understands, or that fails authentication: truncated, modified, or // not written by keyforge. ErrCorruptKeyring = errors.New("local: corrupt keyring") // ErrInsecurePermissions is returned for a keyring that other users can // read. ErrInsecurePermissions = errors.New("local: keyring is readable by others") )
Typed errors for the keyring file.
var ( // ErrBadRootKey is returned when a source yields something that is not a // root key. The error never contains what was read. ErrBadRootKey = errors.New("local: root key is not 32 bytes of base64 or hex") // unset variable or a missing file. ErrRootKeyUnavailable = errors.New("local: root key unavailable") // ErrInsecureRootKeyFile is returned for a root key file that other users // can read. ErrInsecureRootKeyFile = errors.New("local: root key file is readable by others") )
Typed errors for root key sources.
var ErrClosed = errors.New("local: provider is closed")
ErrClosed is returned by a provider after Close.
var ErrRootKeyBesideKeyring = errors.New("local: the root key file is in the keyring's directory")
ErrRootKeyBesideKeyring is returned when the root key file is in the same directory as the keyring, which defeats the point of the root key.
Functions ¶
Types ¶
type BackupStatus ¶
type BackupStatus struct {
// Generation is the keyring's current generation.
Generation uint64
// Acknowledged is the generation an operator last confirmed as safely
// backed up, or zero if none has been.
Acknowledged uint64
// AcknowledgedAt is when it was acknowledged, from the caller's clock.
AcknowledgedAt time.Time
// Note is the free text given with the acknowledgement, such as where the
// copy is kept.
Note string
}
BackupStatus reports how the keyring stands against its last acknowledged backup.
func (BackupStatus) Behind ¶
func (s BackupStatus) Behind() uint64
Behind returns how many generations the keyring is ahead of its backup.
func (BackupStatus) UpToDate ¶
func (s BackupStatus) UpToDate() bool
UpToDate reports whether the acknowledged backup is the current keyring. A policy that requires a verified backup waits until this is true.
type Options ¶
type Options struct {
// Path is the keyring file. Required.
Path string
// Root supplies the root key. Required.
Root RootKeySource
// Name overrides [DefaultName].
Name string
// Entropy supplies key material and identifiers. The default is
// crypto/rand. Tests inject a deterministic reader.
Entropy io.Reader
// AllowRootKeyBesideKeyring permits a root key file in the keyring's
// directory. Leave it false outside tests.
AllowRootKeyBesideKeyring bool
// AcceptRollback opens a keyring that is older than one this host has
// seen, which is what restoring from backup produces. Set it only after
// confirming that the restore was intended; new writes continue from the
// highest generation seen, so generations are never reused.
AcceptRollback bool
}
Options configures a provider.
type Provider ¶
type Provider struct {
// contains filtered or unexported fields
}
Provider is the keyring-file provider. It is safe for concurrent use, within a process and across processes sharing the file.
func Create ¶
Create makes a new, empty keyring. It fails with ErrKeyringExists if the file is already there.
Example ¶
dir, _ := os.MkdirTemp("", "keyforge-example-*")
defer os.RemoveAll(dir)
root := make([]byte, RootKeySize) // in practice: 32 random bytes from a secret manager
for i := range root {
root[i] = byte(i) //nolint:gosec // G115: i < 32
}
p, err := Create(context.Background(), Options{Path: filepath.Join(dir, "keyring.kf"), Root: StaticSource(root)})
if err != nil {
fmt.Println(err)
return
}
defer p.Close()
fmt.Println(p.Name(), p.Generation())
Output: local 1
func Open ¶
Open opens an existing keyring. It fails with ErrKeyringNotFound if there is none, with ErrWrongRootKey if the root key is not the one it was written under, and with ErrCorruptKeyring if the file does not authenticate.
func (*Provider) AckBackup ¶
AckBackup records that the operator has safely stored a copy of the keyring at generation gen. It cannot acknowledge a generation the keyring has not reached, or one older than the last acknowledgement. Repeating the same generation is allowed and updates the time and note.
at is the time of the acknowledgement from the caller's clock.
func (*Provider) BackupStatus ¶
func (p *Provider) BackupStatus(ctx context.Context) (BackupStatus, error)
BackupStatus returns the keyring's current generation and the last acknowledged backup.
func (*Provider) Close ¶
Close zeroes the key material held in memory. The provider cannot be used afterwards.
func (*Provider) CreateKey ¶
CreateKey implements provider.Provider.
func (*Provider) Destroy ¶
Destroy implements provider.Provider. The material is removed from the keyring file; it survives only in backups taken before.
func (*Provider) Generation ¶
Generation returns the generation of the keyring this provider last wrote or read. It increases with every change.
func (*Provider) Health ¶
Health implements provider.Provider: it reports whether the keyring can be read and authenticated with the root key in use.
func (*Provider) RootKeyID ¶
RootKeyID returns the non-secret identifier of the root key in use, derived from it, as lowercase hex.
func (*Provider) Rotate ¶
Rotate implements provider.Provider. Versions are numbered 1, 2, 3, and so on.
func (*Provider) RotateRootKey ¶
func (p *Provider) RotateRootKey(ctx context.Context, next RootKeySource) error
RotateRootKey re-encrypts the keyring under a new root key in one atomic write. Until the write completes the old root key still opens the keyring; afterwards only the new one does, so the caller keeps the old key until it has confirmed the new one is stored.
func (*Provider) Unwrap ¶
func (p *Provider) Unwrap(ctx context.Context, ref provider.KeyRef, ver uint32, wrapped, aad []byte) ([]byte, error)
Unwrap implements provider.Provider.
type RollbackError ¶
type RollbackError struct {
// Found is the generation of the keyring on disk.
Found uint64
// Highest is the highest generation this host has seen.
Highest uint64
}
RollbackError describes a keyring found older than one already seen. It matches ErrKeyringRolledBack.
func (*RollbackError) Unwrap ¶
func (e *RollbackError) Unwrap() error
Unwrap lets errors.Is match ErrKeyringRolledBack.
type RootKeySource ¶
type RootKeySource interface {
// RootKey returns the root key. The caller zeroes the returned slice when
// done with it.
RootKey(ctx context.Context) ([]byte, error)
}
RootKeySource supplies the root key that protects the keyring file. The root key never touches disk beside the keyring: it comes from an environment variable, from a file kept elsewhere, or from a secret manager.
The root key is 32 random bytes, for example the output of `openssl rand -base64 32`. keyforge does not stretch a passphrase: a passphrase is not a key, and a source that holds one belongs in the secret manager that derives the key from it.
func EnvSource ¶
func EnvSource(name string) RootKeySource
EnvSource reads the root key from an environment variable.
func FileSource ¶
func FileSource(path string) RootKeySource
FileSource reads the root key from a file. On Unix the file must not be readable by group or others.
func StaticSource ¶
func StaticSource(key []byte) RootKeySource
StaticSource returns a source that holds a key in memory, for tests and for callers that obtained the key some other way. It keeps its own copy.