Documentation
¶
Overview ¶
Package hostcustody is the node side of sealed host delivery (pkg/hostdelivery). It owns this server's recipient key and its generations, the delivering authority key pinned for each generation, the replay ledger, the registry of delivered secrets, detachment, and the local custody of delivered Wallet items and SSH access state.
A StackKit acts only on the server it is installed on: every record lives in the node's owner-signed private custody, the recipient private key and every delivered value are encrypted under the node's local custody key, and no value is ever written to a log, receipt or stored command. Callers hold the lifecycle mutation while they call a mutating function.
Index ¶
- Variables
- func EstablishRecipient(store Store) error
- func LoadWalletItem(store Store, tenantRef, itemRef string) (uint64, []byte, error)
- func PinAuthority(store Store, authority AuthorityKey, now time.Time) error
- func PruneRetired(store Store, now time.Time) error
- func RecipientStatement(store Store, challenge string, now time.Time) (hostdelivery.Statement, error)
- func RotateRecipient(store Store, authority *AuthorityKey, now time.Time) (uint64, error)
- func SaveSSHAccess(store Store, record SSHAccess) error
- type AuthorityKey
- type DeliveredItem
- type Delivery
- type DetachReport
- type Expectation
- type RetiredGeneration
- type SSHAccess
- type Status
- type Store
- type WalletItemReceipt
- type WalletItemStatus
Constants ¶
This section is empty.
Variables ¶
var ErrDetached = errors.New("hostcustody: this node is detached from host delivery; re-enroll with a recipient rotation")
ErrDetached reports that this node was detached from host delivery and must be re-enrolled (a recipient rotation) before it accepts a delivery again.
var ErrMissing = errors.New("hostcustody: no host custody record")
ErrMissing reports that a host custody record does not exist.
Functions ¶
func EstablishRecipient ¶
EstablishRecipient creates the node's encryption recipient key (generation 1) once. The private half is encrypted under the local custody key and never leaves this node; no signing key is ever used to decrypt.
func LoadWalletItem ¶
LoadWalletItem returns the newest version of an item in custody and a caller-owned copy of its value. Callers clear the value after use.
func PinAuthority ¶
func PinAuthority(store Store, authority AuthorityKey, now time.Time) error
PinAuthority pins the delivering authority key for the current generation. Pinning the same key again changes nothing; a different key is refused, and only a recipient rotation (re-enrollment) can replace it.
func PruneRetired ¶
PruneRetired destroys every retired recipient key whose window has closed.
func RecipientStatement ¶
func RecipientStatement(store Store, challenge string, now time.Time) (hostdelivery.Statement, error)
RecipientStatement proves the current recipient key belongs to this node's owner, names its generation and pinned authority, and answers the delivering authority's challenge. A detached node issues no statement.
func RotateRecipient ¶
RotateRecipient re-enrolls the node under recipient generation n+1. The previous key stays openable for deliveries issued before this moment, for at most the maximum delivery lifetime, and is then destroyed; a detached node destroys it at once. A rotation ends detachment. A non-nil authority replaces the pinned delivering authority key for the new generation; otherwise the current pin carries over. It returns the new generation.
func SaveSSHAccess ¶
SaveSSHAccess records the SSH access this node must hold.
Types ¶
type AuthorityKey ¶
type AuthorityKey struct {
KeyID string `json:"keyId"`
PublicKey string `json:"publicKey"`
PinnedAt string `json:"pinnedAt,omitempty"`
}
AuthorityKey is a delivering authority's Ed25519 public key pinned by the node.
func ParseAuthorityKey ¶
func ParseAuthorityKey(keyID, encoded string) (AuthorityKey, error)
ParseAuthorityKey validates a base64 (raw or padded) Ed25519 public key.
func (AuthorityKey) Fingerprint ¶
func (a AuthorityKey) Fingerprint() string
Fingerprint names the authority key in statements and status.
type DeliveredItem ¶
type DeliveredItem struct {
TenantRef string `json:"tenantRef"`
DeliveryRef string `json:"deliveryRef"`
ItemVersion uint64 `json:"itemVersion"`
Operation string `json:"operation"`
DeliveredAt string `json:"deliveredAt"`
NeedsRotation bool `json:"needsRotation,omitempty"`
}
DeliveredItem names one secret that reached this node. It never carries the value.
type Delivery ¶
type Delivery struct {
Context hostdelivery.Context
Plaintext []byte
}
Delivery is one opened delivery. The caller owns Plaintext and clears it.
func OpenDelivery ¶
OpenDelivery opens one sealed delivery for this node and consumes it. It refuses a delivery for another tenant, Site, node or operation, a detached node, a retired or unknown recipient generation, an operation whose authority signature is missing or not by the pinned key, an expired or oversized one, and a replay. The delivery is recorded as consumed (and a secret one in the delivered registry) before the plaintext is returned, so a replay, including one after a crash, is denied and the delivering authority must seal a fresh one.
type DetachReport ¶
type DetachReport struct {
Generation uint64 `json:"generation"`
DetachedAt string `json:"detachedAt"`
Rotate []DeliveredItem `json:"rotate"`
}
DetachReport names what the owner must rotate upstream after a detach. It carries references and versions only, never a value.
func Detach ¶
func Detach(store Store, now time.Time) (DetachReport, error)
Detach ends host delivery for this node's current binding: every delivered secret is marked as needing rotation, every retired recipient key is destroyed, and no delivery opens until the node is re-enrolled with a recipient rotation. Values already in local custody stay usable here; the report lists the references to rotate where they came from. Detaching again keeps the first detach time and reports the same references.
type Expectation ¶
Expectation is what the opening node requires of a delivery beyond its own identity: the tenant the owner operates for, and the operations this caller can carry out.
type RetiredGeneration ¶
type RetiredGeneration struct {
Generation uint64 `json:"generation"`
RetiredAt string `json:"retiredAt"`
OpenUntil string `json:"openUntil"`
}
RetiredGeneration is one previous recipient generation still openable.
type SSHAccess ¶
type SSHAccess struct {
APIVersion string `json:"apiVersion"`
CAKeys []string `json:"caKeys"`
Principals map[string][]string `json:"principals"`
// TrustRef and TrustVersion name the delivery the trust came from.
TrustRef string `json:"trustRef,omitempty"`
TrustVersion uint64 `json:"trustVersion,omitempty"`
// HostCertificate is one OpenSSH host certificate line.
HostCertificate string `json:"hostCertificate,omitempty"`
HostCertificateRef string `json:"hostCertificateRef,omitempty"`
HostCertificateVersion uint64 `json:"hostCertificateVersion,omitempty"`
// CleanupPending names target-side effects of a revocation that could not
// be completed yet (for example an sshd reload).
CleanupPending []string `json:"cleanupPending,omitempty"`
UpdatedAt string `json:"updatedAt"`
Signature localevidence.OwnerPolicyStateSignature `json:"signature"`
}
SSHAccess is the SSH access this node was told to hold: the owner's user CA trust with the principals each local user accepts, and the host certificate. None of it is secret, but it is integrity-critical, so it lives in owner-signed custody and host security repair reinstalls it from here.
func LoadSSHAccess ¶
LoadSSHAccess reads the recorded SSH access. A node that never received one is ErrMissing.
type Status ¶
type Status struct {
Generation uint64 `json:"generation"`
Fingerprint string `json:"fingerprint"`
AuthorityKeyID string `json:"authorityKeyId,omitempty"`
AuthorityFingerprint string `json:"authorityFingerprint,omitempty"`
Detached bool `json:"detached"`
DetachedAt string `json:"detachedAt,omitempty"`
Retired []RetiredGeneration `json:"retired"`
Delivered []DeliveredItem `json:"delivered"`
}
Status is the secret-free state of this node's host delivery custody.
type Store ¶
type Store interface {
// Workspace is the StackKit workspace root whose Owner custody signs records.
Workspace() string
// WrappingKey returns a fresh caller-owned copy of the local custody key,
// establishing it once. Callers clear it after use.
WrappingKey() ([]byte, error)
// Read decodes one owner-signed private record by file name, strictly and
// within maxBytes, and verifies its signature. A missing record is
// ErrMissing.
Read(name string, maxBytes int64, record any, signature *localevidence.OwnerPolicyStateSignature) error
// Write signs and durably writes one private record. With create the
// record must not exist yet.
Write(name string, record any, signature *localevidence.OwnerPolicyStateSignature, create bool) error
// Remove deletes one private record; a missing record is not an error.
Remove(name string) error
}
Store is the node's owner-bound private storage. Backup custody provides it so the recipient key and the delivery ledger stay in the files and under the local custody key the node already holds.
type WalletItemReceipt ¶
type WalletItemReceipt struct {
TenantRef string `json:"tenantRef"`
ItemRef string `json:"itemRef"`
Version uint64 `json:"version"`
Removed []uint64 `json:"removedVersions,omitempty"`
// CleanupPending names older version records that could not be removed;
// the index no longer serves them.
CleanupPending []string `json:"cleanupPending,omitempty"`
}
WalletItemReceipt reports a stored item by reference; it carries no value.
func StoreWalletItem ¶
StoreWalletItem places an opened wallet-item-import or wallet-item-rotate delivery in encrypted local custody, keyed by tenant, item reference and version. Versions are immutable: an import must be newer than every version in custody, and a rotation must replace an item already held, after which every older version is removed.
type WalletItemStatus ¶
type WalletItemStatus struct {
TenantRef string `json:"tenantRef"`
ItemRef string `json:"itemRef"`
Versions []uint64 `json:"versions"`
NeedsRotation bool `json:"needsRotation"`
}
WalletItemStatus lists one item by reference and the versions in custody.
func ListWalletItems ¶
func ListWalletItems(store Store) ([]WalletItemStatus, error)
ListWalletItems lists every item by tenant, reference and versions, with its rotation mark. It never reads a value.