Documentation
¶
Overview ¶
Package embedded wires keyforge for one host: a keyring file, a SQLite registry, the lifecycle jobs and, optionally, the HTTP control plane, all from a directory and a root key source.
It is the setup most deployments start with, and it is a convenience over the same packages a larger deployment assembles itself: nothing here is reachable only through it. Open returns the pieces, so a host that outgrows the single-host layout keeps using the keyforge.Client, the rotate.Rotator and the rest, and swaps the store or the provider.
Layout ¶
Everything lives in one directory, except the root key, which must not:
keyring.kf the keyring, sealed under a key derived from the root key registry.db the registry (SQLite, with its -wal and -shm files) runs/ the state of lifecycle jobs, so they resume after a restart
Backup ¶
A backup is the keyring and the registry together. Either alone is useless or dangerous: a registry without its keyring names keys that cannot be unwrapped, and a keyring without its registry cannot say which data keys it protects. Take them at the same time, acknowledge the keyring backup so rotation can rely on it, and keep the root key somewhere else again: it is the one secret that cannot be recovered from either. System.BackupRegistry writes a consistent registry copy while the system is running.
Example ¶
A single host needs a directory for its state and a root key held somewhere else. Open creates the keyring, the registry and a key for each purpose on the first start, and finds them again on every start after.
package main
import (
"context"
"crypto/rand"
"fmt"
"log"
"os"
"path/filepath"
"github.com/plexusone/keyforge"
"github.com/plexusone/keyforge/embedded"
"github.com/plexusone/keyforge/provider/local"
)
func main() {
ctx := context.Background()
tmp, err := os.MkdirTemp("", "keyforge-example-")
if err != nil {
log.Fatal(err)
}
defer func() {
if err := os.RemoveAll(tmp); err != nil {
log.Print(err)
}
}()
// In a deployment the root key comes from a secret manager or a file
// outside the state directory: local.EnvSource("KEYFORGE_ROOT_KEY") or
// local.FileSource("/etc/keyforge/root.key"). It is 32 random bytes, which
// `keyforge rootkey generate` prints.
root := make([]byte, local.RootKeySize)
if _, err := rand.Read(root); err != nil {
log.Fatal(err)
}
sys, err := embedded.Open(ctx, embedded.Options{
Dir: filepath.Join(tmp, "keyforge"),
Root: local.StaticSource(root),
Purposes: map[keyforge.Purpose]embedded.PurposeOptions{
"session-secrets": {},
},
})
if err != nil {
log.Fatal(err)
}
defer func() {
if err := sys.Close(); err != nil {
log.Print(err)
}
}()
// The context binds the ciphertext to a record: it cannot be moved.
sealed, err := sys.Client.Encrypt(ctx, "session-secrets", []byte("token-value"), []byte("user:42"))
if err != nil {
log.Fatal(err)
}
plaintext, _, err := sys.Client.Decrypt(ctx, "session-secrets", sealed, []byte("user:42"))
if err != nil {
log.Fatal(err)
}
fmt.Println(string(plaintext))
}
Output: token-value
Index ¶
Examples ¶
Constants ¶
const ( KeyringFile = "keyring.kf" RegistryFile = "registry.db" RunsDir = "runs" )
File and directory names inside Options.Dir.
Variables ¶
var ErrKeyringMissing = errors.New("embedded: the registry holds keys but the keyring file is missing; restore the keyring from backup")
ErrKeyringMissing is returned when the registry holds keys but the keyring file is gone. Creating a new keyring would not recover them: it would start an empty one beside a registry that names keys it does not hold, and every operation would fail, or worse, look like a fresh start. Restore the keyring from backup.
var ErrRegistryMissing = errors.New("embedded: the keyring exists but the registry file is missing; restore the registry from backup")
ErrRegistryMissing is returned when the keyring exists and the registry file does not. Starting would register brand-new keys beside a keyring whose old ones are still in it, and nothing written under those old keys could be read: the registry is where their wrapped data keys are recorded. Restore the registry from backup. If the registry is genuinely not wanted, remove the keyring as well and start again.
Functions ¶
This section is empty.
Types ¶
type Options ¶
type Options struct {
// Dir holds the keyring, the registry and the job state. It is created if
// it does not exist. Required.
Dir string
// Root supplies the root key that protects the keyring. It must not be a
// file in Dir. Required.
Root local.RootKeySource
// AcceptRollback opens a keyring that is older than one this host has
// seen, which is what restoring from backup produces. Without it, Open
// fails with [local.ErrKeyringRolledBack] naming both generations, so a
// stale copy is never used by mistake. Set it only after confirming that
// the restore was intended.
AcceptRollback bool
// Purposes lists the purposes to manage. Each gets a KEK on first start.
// Required.
Purposes map[keyforge.Purpose]PurposeOptions
// Policy is the rotation and retirement policy. The zero value is the
// default policy.
Policy policy.Set
// Selector chooses the algorithm profile. The zero value uses the default
// profile.
Selector *algorithm.Selector
// Guard is consulted before a version is retired or destroyed. With none,
// nothing is retired or destroyed.
Guard guard.Guard
// Audit receives lifecycle events. The default discards them.
Audit audit.Sink
// Metrics receives operation and rotation counts. The default discards them.
Metrics metrics.Metrics
// Clock defaults to the system clock.
Clock keyforge.Clock
// Authorizer is asked before every control-plane action. Without one the
// system has no control plane and [System.Handler] returns an error: the
// control plane is never open by default.
Authorizer service.Authorizer
// Jobs tunes the scheduler; its provider, rotator, store and purposes are
// supplied here and ignored if set.
Jobs jobs.SchedulerOptions
}
Options configures Open.
type PurposeOptions ¶
type PurposeOptions struct {
// Algorithms narrows the algorithms for the purpose. The zero value uses
// the active profile's.
Algorithms algorithm.Selection
// PerSubject gives each subject its own data keys, so a subject can be
// shredded. See [keyforge.PurposeConfig].
PerSubject bool
}
PurposeOptions configures one purpose.
type System ¶
type System struct {
// Client encrypts and decrypts.
Client *keyforge.Client
// Rotator performs rotation, re-wrapping, retirement and the emergency
// procedures.
Rotator *rotate.Rotator
// Reporter builds the status report.
Reporter *status.Reporter
// Registry is the SQLite registry.
Registry *sqlstore.Store
// Keyring is the keyring-file provider.
Keyring *local.Provider
// Jobs runs the lifecycle jobs. [System.Run] starts it.
Jobs *jobs.Embedded
// contains filtered or unexported fields
}
System is a running single-host keyforge: the pieces, wired.
func Open ¶
Open opens the system in o.Dir, creating the keyring and the registry on first use and a KEK for each purpose that has none. It is idempotent: opening an existing directory changes nothing.
func (*System) BackupRegistry ¶
BackupRegistry writes a consistent copy of the registry to path, which must not exist. The keyring is backed up by copying its file; acknowledge it with the keyring's AckBackup afterwards.
func (*System) Handler ¶
Handler returns the HTTP control plane. It is an error to ask for it when Options.Authorizer was not set.