Documentation
¶
Overview ¶
Package registration implements node self-registration.
Index ¶
- Constants
- Variables
- func ClearPendingKey(dataDir string) error
- func CommitRotatedKey(dataDir string, id *NodeIdentity, kp *Keypair, receipt *RotationReceipt) error
- func SaveIdentity(dataDir string, id *NodeIdentity) error
- func SavePendingKey(dataDir string, kp *Keypair) error
- type Config
- type IMDSProvider
- type Keypair
- type MetadataProvider
- type NodeIdentity
- type Registrar
- type RotationReceipt
- type TokenResolver
- type TokenResult
Constants ¶
const DefaultMaxRetryDuration = 5 * time.Minute
DefaultMaxRetryDuration is the default maximum retry duration.
const DefaultMetadataProjectIDPath = "/plexd/project-id"
DefaultMetadataProjectIDPath is the default metadata key path for the project ID.
const DefaultMetadataRequestedResourceIDPath = "/plexd/requested-resource-id"
DefaultMetadataRequestedResourceIDPath is the default metadata key path for the requested resource ID.
const DefaultMetadataResourceHandlePath = "/plexd/resource-handle"
DefaultMetadataResourceHandlePath is the default metadata key path for the resource handle.
const DefaultMetadataTimeout = 2 * time.Second
DefaultMetadataTimeout is the default timeout for metadata service requests.
const DefaultMetadataTokenPath = "/plexd/bootstrap-token"
DefaultMetadataTokenPath is the default metadata key path for the bootstrap token.
const DefaultTokenEnv = "PLEXD_BOOTSTRAP_TOKEN"
DefaultTokenEnv is the default environment variable name for the bootstrap token.
const DefaultTokenFile = "/etc/plexd/bootstrap-token"
DefaultTokenFile is the default path to the bootstrap token file.
Variables ¶
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.
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
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
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).
DataDir string `yaml:"data_dir"`
// 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.
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.
type Keypair ¶
Keypair holds a Curve25519 keypair for WireGuard.
func GenerateKeypair ¶
GenerateKeypair generates a new Curve25519 keypair for WireGuard mesh encryption.
func LoadPendingKey ¶ added in v0.2.0
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 ¶
EncodePublicKey returns the standard base64 encoding of the public key.
type MetadataProvider ¶
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) 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 ¶
NewRegistrar creates a new Registrar with the given client, config, and logger.
func (*Registrar) IsRegistered ¶
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) 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.