registration

package
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Jul 29, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

Documentation

Overview

Package registration implements node self-registration.

Index

Constants

View Source
const DefaultMaxRetryDuration = 5 * time.Minute

DefaultMaxRetryDuration is the default maximum retry duration.

View Source
const DefaultMetadataProjectIDPath = "/plexd/project-id"

DefaultMetadataProjectIDPath is the default metadata key path for the project ID.

View Source
const DefaultMetadataRequestedResourceIDPath = "/plexd/requested-resource-id"

DefaultMetadataRequestedResourceIDPath is the default metadata key path for the requested resource ID.

View Source
const DefaultMetadataResourceHandlePath = "/plexd/resource-handle"

DefaultMetadataResourceHandlePath is the default metadata key path for the resource handle.

View Source
const DefaultMetadataTimeout = 2 * time.Second

DefaultMetadataTimeout is the default timeout for metadata service requests.

View Source
const DefaultMetadataTokenPath = "/plexd/bootstrap-token"

DefaultMetadataTokenPath is the default metadata key path for the bootstrap token.

View Source
const DefaultTokenEnv = "PLEXD_BOOTSTRAP_TOKEN"

DefaultTokenEnv is the default environment variable name for the bootstrap token.

View Source
const DefaultTokenFile = "/etc/plexd/bootstrap-token"

DefaultTokenFile is the default path to the bootstrap token file.

Variables

View Source
var ErrMetadataNotFound = errors.New("registration: imds: no value at path")

ErrMetadataNotFound reports that the metadata service serves no value at the requested path. Callers treat it as "not provisioned" rather than a failure, which keeps optional registration inputs optional.

View Source
var ErrNotRegistered = errors.New("registration: node is not registered")

ErrNotRegistered indicates that no valid identity files exist in data_dir.

Functions

func ClearPendingKey added in v0.2.0

func ClearPendingKey(dataDir string) error

ClearPendingKey removes the staged rotation key. A missing file is not an error: clearing is the terminal step of a rotation and must be idempotent.

func CommitRotatedKey added in v0.2.0

func CommitRotatedKey(dataDir string, id *NodeIdentity, kp *Keypair, receipt *RotationReceipt) error

CommitRotatedKey performs the post-confirmation swap after the control plane accepts a rotation. It durably installs kp as the node's private_key, records the receipt in identity.json, and only then drops the staging file. A nil receipt keeps the previous LastRotation, which happens when the server answers 422 unchanged on a crash-retry of an already-committed rotation. Rotation only ever owns private_key and last_rotation; every other identity field is taken from disk, so a re-registration that happened after id was loaded is not overwritten with the caller's stale copy.

func SaveIdentity

func SaveIdentity(dataDir string, id *NodeIdentity) error

SaveIdentity persists the node identity atomically to dataDir.

func SavePendingKey added in v0.2.0

func SavePendingKey(dataDir string, kp *Keypair) error

SavePendingKey stages the fresh rotation private key on disk before its public half is submitted to the control plane. Persisting it first makes rotation crash-safe: if the process dies after the server learns the new public key, a startup resubmit can recover the matching private key from this file.

Types

type Config

type Config struct {
	// DataDir is the path to the data directory (required). It is propagated
	// from the top-level data_dir by AgentConfig.ApplyDefaults, not from YAML.
	DataDir string `yaml:"-"`

	// ProjectID is the platform project UUID the node registers into
	// (required for fresh registration).
	ProjectID string `yaml:"project_id"`

	// ResourceHandle is the platform Resource handle the node binds to
	// (required for fresh registration).
	ResourceHandle string `yaml:"resource_handle"`

	// RequestedResourceID is an optional override used when substrate naming
	// differs from the platform handle.
	RequestedResourceID string `yaml:"requested_resource_id"`

	// TokenFile is the path to the bootstrap token file.
	// Default: /etc/plexd/bootstrap-token
	TokenFile string `yaml:"token_file"`

	// TokenEnv is the environment variable name for the bootstrap token.
	// Default: PLEXD_BOOTSTRAP_TOKEN
	TokenEnv string `yaml:"token_env"`

	// TokenValue is a direct token value override.
	TokenValue string `yaml:"token_value"`

	// UseMetadata enables cloud metadata service for registration.
	// Default: false
	UseMetadata bool `yaml:"use_metadata"`

	// MetadataTokenPath is the metadata key path used to retrieve the
	// bootstrap token from an instance metadata service (e.g. IMDS).
	// Default: /plexd/bootstrap-token
	MetadataTokenPath string `yaml:"metadata_token_path"`

	// MetadataTimeout is the maximum time to wait for a metadata service
	// response.
	// Default: 2s
	MetadataTimeout time.Duration `yaml:"metadata_timeout"`

	// MaxRetryDuration is the maximum duration to retry registration.
	// Default: 5m
	MaxRetryDuration time.Duration `yaml:"max_retry_duration"`
}

Config holds the configuration for the agent registration process. Config is passed as a constructor argument — no file I/O in this package.

func (*Config) ApplyDefaults

func (c *Config) ApplyDefaults()

ApplyDefaults sets default values for zero-valued fields.

func (*Config) Validate

func (c *Config) Validate() error

Validate checks that required fields are set.

type IMDSProvider

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

IMDSProvider reads values (bootstrap token, project id, resource handle) from a cloud instance metadata service. It supports both IMDSv2 (session-based) and IMDSv1 (open GET) with automatic fallback: a PUT is attempted first to acquire a session token; if that fails the subsequent GET proceeds without the session header.

func NewIMDSProvider

func NewIMDSProvider(timeout time.Duration, baseURL string) *IMDSProvider

NewIMDSProvider creates an IMDSProvider that reads values from baseURL, using timeout as the HTTP client timeout.

func (*IMDSProvider) ReadValue added in v0.2.0

func (p *IMDSProvider) ReadValue(ctx context.Context, path string) (string, error)

ReadValue fetches the value at path from the metadata service. It first attempts IMDSv2 session token acquisition; if that fails it falls back to an unauthenticated IMDSv1 GET.

type Keypair

type Keypair struct {
	PrivateKey []byte // 32 bytes, never logged
	PublicKey  []byte // 32 bytes
}

Keypair holds a Curve25519 keypair for WireGuard.

func GenerateKeypair

func GenerateKeypair() (*Keypair, error)

GenerateKeypair generates a new Curve25519 keypair for WireGuard mesh encryption.

func LoadPendingKey added in v0.2.0

func LoadPendingKey(dataDir string) (*Keypair, error)

LoadPendingKey reads a staged rotation keypair from dataDir. It returns (nil, nil) when no key is staged so callers can distinguish "nothing pending" from a read error. The public key is re-derived from the stored private key.

func (*Keypair) EncodePublicKey

func (k *Keypair) EncodePublicKey() string

EncodePublicKey returns the standard base64 encoding of the public key.

type MetadataProvider

type MetadataProvider interface {
	ReadValue(ctx context.Context, path string) (string, error)
}

MetadataProvider reads a value from a cloud metadata service at a given path.

type NodeIdentity

type NodeIdentity struct {
	NodeID           string           `json:"node_id"`
	MeshIP           string           `json:"mesh_ip"`
	SigningPublicKey string           `json:"signing_public_key"`
	SigningKeyID     string           `json:"signing_key_id"`
	DomainMeshCIDR   string           `json:"domain_mesh_cidr"`
	LastRotation     *RotationReceipt `json:"last_rotation,omitempty"`
	PrivateKey       []byte           `json:"-"` // never serialized to JSON
	NodeSecretKey    string           `json:"-"` // never serialized to JSON
}

NodeIdentity holds the registration identity of a node.

func LoadIdentity

func LoadIdentity(dataDir string) (*NodeIdentity, error)

LoadIdentity reads a previously saved node identity from dataDir.

func (*NodeIdentity) BearerToken added in v0.3.0

func (id *NodeIdentity) BearerToken() (string, error)

BearerToken assembles the Authorization bearer credential the control plane admits on every post-registration call:

nsk_<env>_<base64url(node_id_bytes(16) || nsk_plaintext_bytes(32))>

The payload is exactly 48 bytes — the node's UUID followed by the decoded NSK — encoded with unpadded base64url. Presenting the raw base64 nsk string instead is refused by the control plane with 401. The embedded node id is a lookup hint for the control plane's resolver, not an authorizer: the server independently matches it against the URL path and verifies the key bytes against its own records.

func (*NodeIdentity) SecretKey added in v0.2.0

func (id *NodeIdentity) SecretKey() ([]byte, error)

SecretKey decodes NodeSecretKey into the raw AES-256-GCM key used to open secret envelopes. Per the register contract nsk travels — and is stored — as standard-padded base64; the bearer credential sent in the Authorization header stays the encoded string.

type Registrar

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

Registrar orchestrates node registration with the control plane.

func NewRegistrar

func NewRegistrar(client *api.ControlPlane, cfg Config, logger *slog.Logger) *Registrar

NewRegistrar creates a new Registrar with the given client, config, and logger.

func (*Registrar) IsRegistered

func (r *Registrar) IsRegistered() bool

IsRegistered returns true if a valid identity exists on disk.

func (*Registrar) Register

func (r *Registrar) Register(ctx context.Context) (*NodeIdentity, error)

Register orchestrates the full registration flow. If a valid identity already exists on disk, it is returned without contacting the control plane.

func (*Registrar) SetClock

func (r *Registrar) SetClock(c api.Clock)

SetClock sets a custom clock for testing.

func (*Registrar) SetMetadataProvider

func (r *Registrar) SetMetadataProvider(mp MetadataProvider)

SetMetadataProvider sets an optional metadata provider for token resolution.

type RotationReceipt added in v0.2.0

type RotationReceipt struct {
	RotationID     string `json:"rotation_id"`
	KID            string `json:"kid"`
	WrapKeyVersion int    `json:"wrap_key_version"`
}

RotationReceipt is the control plane's receipt for a completed mesh-key rotation. It is persisted into identity.json so the node can prove which rotation the currently committed private_key belongs to.

type TokenResolver

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

TokenResolver resolves the bootstrap token from multiple sources.

func NewTokenResolver

func NewTokenResolver(cfg *Config, metadata MetadataProvider) *TokenResolver

NewTokenResolver creates a new TokenResolver.

func (*TokenResolver) Resolve

func (r *TokenResolver) Resolve(ctx context.Context) (*TokenResult, error)

Resolve locates a bootstrap token by checking sources in priority order: direct value, file, environment variable, metadata service.

func (*TokenResolver) ResolveValue added in v0.2.0

func (r *TokenResolver) ResolveValue(ctx context.Context, name, direct, metadataPath string) (string, error)

ResolveValue resolves the registration input named name from a direct setting or the cloud metadata service. A non-empty trimmed direct value always wins; otherwise, when metadata is enabled and a provider is set, the value at metadataPath is read. A path the metadata service does not serve (ErrMetadataNotFound) means "not provisioned" and yields an empty string, so optional inputs stay optional. Every other read error is returned: a transient IMDS failure must not be reported as a missing config setting.

type TokenResult

type TokenResult struct {
	Value    string // the token value
	FilePath string // non-empty if the token was read from a file (for cleanup)
}

TokenResult holds the resolved token and its source metadata.

Jump to

Keyboard shortcuts

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