Documentation
¶
Overview ¶
Package enrollment provides the controller-side enrollment system for accepting or rejecting agent enrollment requests via NATS.
Index ¶
- Variables
- type AcceptedAgent
- type AgentKeyStore
- type KVStore
- type KeyRotationMessage
- type NATSSubscriber
- type PKIProvider
- type PendingAgent
- type Watcher
- func (w *Watcher) AcceptAgent(ctx context.Context, machineID string) error
- func (w *Watcher) AcceptByFingerprint(ctx context.Context, fingerprint string) error
- func (w *Watcher) AcceptByHostname(ctx context.Context, hostname string) error
- func (w *Watcher) KeyStore() client.AgentKeyStore
- func (w *Watcher) ListPending(ctx context.Context) ([]PendingAgent, error)
- func (w *Watcher) LookupAgentKey(ctx context.Context, machineID string) (*AcceptedAgent, error)
- func (w *Watcher) RejectAgent(ctx context.Context, machineID string, reason string) error
- func (w *Watcher) RejectByHostname(ctx context.Context, hostname string, reason string) error
- func (w *Watcher) RemoveAgentKey(ctx context.Context, machineID string) error
- func (w *Watcher) RotateControllerKey() error
- func (w *Watcher) SetRotationGracePeriod(d time.Duration)
- func (w *Watcher) Start(ctx context.Context) error
Constants ¶
This section is empty.
Variables ¶
var ( // ErrAgentKeyNotFound means no key is stored for the machine ID. During // a rollout this is expected: no agent accepted before the store // existed has one. ErrAgentKeyNotFound = errors.New("no stored key for agent") // ErrNoPendingAgent means no enrollment request is waiting under the name // the caller gave. The API answers 404: there is nothing there to accept or // reject. ErrNoPendingAgent = errors.New("no pending agent") // decoded. It never resembles "no stored key", and is never treated as // verified. ErrAgentKeyStoreUnavailable = errors.New("agent key store unavailable") // ErrAgentKeySignatureMismatch means a key that is not this agent's // signed the message. ErrAgentKeySignatureMismatch = errors.New("agent signature mismatch") )
The three causes of a failed lookup stay distinct so an operator can tell "this agent has not re-enrolled yet" from "something is forging messages" from "the store could not be read". Collapsing them hides the difference at exactly the moment it matters.
Functions ¶
This section is empty.
Types ¶
type AcceptedAgent ¶
type AcceptedAgent struct {
MachineID string `json:"machine_id"`
Hostname string `json:"hostname"`
PublicKey ed25519.PublicKey `json:"public_key"`
Fingerprint string `json:"fingerprint"`
AcceptedAt time.Time `json:"accepted_at"`
// SupersededKey is the key replaced by the most recent rotation. It is
// absent unless a rotation is inside its grace period.
SupersededKey ed25519.PublicKey `json:"superseded_key,omitempty"`
// SupersededUntil is the instant the superseded key stops being
// accepted. Storing the instant rather than a duration means a restart
// cannot silently extend the window.
SupersededUntil time.Time `json:"superseded_until,omitempty"`
}
AcceptedAgent is the stored record of an accepted agent's public key. It outlives the pending record, which is deleted on acceptance, so every later message from that agent can be verified against the key the agent enrolled with.
Only enrollment acceptance creates or replaces a record: nothing an agent sends may introduce or change one.
type AgentKeyStore ¶
type AgentKeyStore interface {
LookupAgentKey(
ctx context.Context,
machineID string,
) (*AcceptedAgent, error)
}
AgentKeyStore is the narrow lookup the verification callers depend on, so the job client and target resolution need not import this package wholesale. Satisfied by *Watcher.
type KeyRotationMessage ¶
type KeyRotationMessage struct {
NewPublicKey []byte `json:"new_public_key"`
}
KeyRotationMessage is published to agents when the controller rotates its keypair.
type NATSSubscriber ¶
type NATSSubscriber interface {
Subscribe(
subj string,
cb nats.MsgHandler,
) (*nats.Subscription, error)
PublishCore(
subj string,
data []byte,
) error
}
NATSSubscriber defines the NATS operations needed by the enrollment watcher for subscribing to enrollment requests and publishing responses. Satisfied by the nats-client's *Client type (Subscribe + PublishCore).
type PKIProvider ¶
PKIProvider defines the PKI operations needed by the enrollment watcher to provide the controller's public key in acceptance responses.
type PendingAgent ¶
type PendingAgent struct {
MachineID string `json:"machine_id"`
Hostname string `json:"hostname"`
PublicKey []byte `json:"public_key"`
Fingerprint string `json:"fingerprint"`
RequestedAt time.Time `json:"requested_at"`
}
PendingAgent represents an agent awaiting enrollment acceptance.
type Watcher ¶
type Watcher struct {
// contains filtered or unexported fields
}
Watcher monitors NATS for agent enrollment requests and manages pending agents in a JetStream KV bucket.
func NewWatcher ¶
func NewWatcher( logger *slog.Logger, nc NATSSubscriber, enrollmentKV KVStore, pkiProvider PKIProvider, autoAccept bool, namespace string, ) *Watcher
NewWatcher creates a new enrollment Watcher.
func (*Watcher) AcceptAgent ¶
AcceptAgent accepts a pending agent by machine ID. It publishes an acceptance response containing the controller's public key to the agent's response subject and deletes the pending entry from KV.
func (*Watcher) AcceptByFingerprint ¶
AcceptByFingerprint scans the pending KV for an agent matching the given fingerprint and accepts it. Returns an error if no matching agent is found.
func (*Watcher) AcceptByHostname ¶
AcceptByHostname scans the pending KV for an agent matching the given hostname and accepts it. Returns an error if no matching agent is found.
func (*Watcher) KeyStore ¶
func (w *Watcher) KeyStore() client.AgentKeyStore
KeyStore returns the watcher as the job client's agent key store, so response verification reads the same records acceptance writes and shares their cache.
func (*Watcher) ListPending ¶
func (w *Watcher) ListPending( ctx context.Context, ) ([]PendingAgent, error)
ListPending returns all pending agents from the enrollment KV bucket.
func (*Watcher) LookupAgentKey ¶
LookupAgentKey returns the stored record for a machine ID. A missing record and an unreadable store are different answers: the first is ErrAgentKeyNotFound, the second ErrAgentKeyStoreUnavailable.
func (*Watcher) RejectAgent ¶
RejectAgent rejects a pending agent by machine ID. It publishes a rejection response to the agent's response subject and deletes the pending entry from KV.
func (*Watcher) RejectByHostname ¶
RejectByHostname scans the pending KV for an agent matching the given hostname and rejects it. Returns an error if no matching agent is found.
func (*Watcher) RemoveAgentKey ¶
RemoveAgentKey deletes an agent's stored record. After it returns, nothing signed by the removed key verifies.
func (*Watcher) RotateControllerKey ¶
RotateControllerKey publishes the controller's current public key to all agents via the pki.rotate subject. Agents receiving this message move their current controller key to previous and store the new key, enabling a grace period where both keys are accepted.
func (*Watcher) SetRotationGracePeriod ¶
SetRotationGracePeriod sets how long a replaced key keeps verifying after a rotation. Called once at startup from the controller's PKI configuration.
Without it a rotation rejects everything signed in the moments before the new key was accepted, which is the reason operators turn verification off.