Documentation
¶
Index ¶
- Constants
- Variables
- func BearerAuthMiddleware(token string) func(http.Handler) http.Handler
- func DecryptSecret(nsk []byte, envelope []byte) (string, error)
- func SecretAuthMiddleware(checker GroupChecker, getter PeerCredGetter, logger *slog.Logger) func(http.Handler) http.Handler
- func SetSocketPermissions(socketPath string, logger *slog.Logger) error
- type ActionProvider
- type Config
- type ForwarderStatus
- type ForwarderStatusProvider
- type GroupChecker
- type Handler
- func (h *Handler) Mux() *http.ServeMux
- func (h *Handler) SetActionProvider(provider ActionProvider)
- func (h *Handler) SetAuditStatus(p ForwarderStatusProvider)
- func (h *Handler) SetHookReloader(reloader HookReloader)
- func (h *Handler) SetLogStatus(p ForwarderStatusProvider)
- func (h *Handler) SetPeerProvider(p PeerProvider)
- func (h *Handler) SetPolicyProvider(p PolicyProvider)
- type HookReloader
- type LocalActionRunner
- type NodeAPIClient
- type OSGroupChecker
- type PeerCredGetter
- type PeerCredentials
- type PeerProvider
- type PeerStatus
- type PolicyProvider
- type ReportEntry
- type ReportSyncClient
- type ReportSyncer
- type SecretFetcher
- type Server
- func (s *Server) Cache() *StateCache
- func (s *Server) PublishReport(key, contentType string, payload json.RawMessage) error
- func (s *Server) ReconcileHandler() reconcile.ReconcileHandler
- func (s *Server) ReportPayload(key string) (json.RawMessage, bool)
- func (s *Server) SetActionProvider(provider ActionProvider)
- func (s *Server) SetAuditStatus(p ForwarderStatusProvider)
- func (s *Server) SetHookReloader(reloader HookReloader)
- func (s *Server) SetLogStatus(p ForwarderStatusProvider)
- func (s *Server) SetPeerProvider(p PeerProvider)
- func (s *Server) SetPolicyProvider(p PolicyProvider)
- func (s *Server) Start(ctx context.Context, nodeID string) error
- type StateCache
- func (sc *StateCache) DeleteReport(key string) error
- func (sc *StateCache) GetData() map[string]api.DataEntry
- func (sc *StateCache) GetDataEntry(key string) (api.DataEntry, bool)
- func (sc *StateCache) GetMetadata() map[string]string
- func (sc *StateCache) GetMetadataKey(key string) (string, bool)
- func (sc *StateCache) GetReport(key string) (ReportEntry, bool)
- func (sc *StateCache) GetReports() map[string]ReportEntry
- func (sc *StateCache) GetSecretIndex() []api.SecretRef
- func (sc *StateCache) Load() error
- func (sc *StateCache) OrphanedReportKeys() []string
- func (sc *StateCache) PutReport(key, contentType string, payload json.RawMessage, ifMatch *int) (ReportEntry, error)
- func (sc *StateCache) SetDeliveryMode(mode string)
- func (sc *StateCache) UpdateData(entries []api.DataEntry)
- func (sc *StateCache) UpdateMetadata(m map[string]string)
- func (sc *StateCache) UpdateSecretIndex(refs []api.SecretRef)
- type StateSummary
Constants ¶
const DefaultDebouncePeriod = 5 * time.Second
DefaultDebouncePeriod is the default debounce period.
const DefaultHTTPListen = "127.0.0.1:9100"
DefaultHTTPListen is the default HTTP listen address.
const DefaultShutdownTimeout = 5 * time.Second
DefaultShutdownTimeout is the default graceful shutdown timeout.
const DefaultSocketPath = "/var/run/plexd/api.sock"
DefaultSocketPath is the default Unix domain socket path.
Variables ¶
var ( // ErrVersionConflict is returned when an optimistic locking check fails. ErrVersionConflict = errors.New("nodeapi: version conflict") // ErrNotFound is returned when the requested entry does not exist. ErrNotFound = errors.New("nodeapi: not found") )
Functions ¶
func BearerAuthMiddleware ¶
BearerAuthMiddleware returns middleware that validates Bearer token authentication. Requests without a valid token receive 401 Unauthorized. Unix socket requests bypass this middleware (it is only applied to the TCP listener).
func DecryptSecret ¶
DecryptSecret opens the raw AES-256-GCM envelope <12-byte nonce> || <ciphertext + 16-byte GCM tag> under the NSK. The NSK (node secret key) must be exactly 32 bytes. Returns a generic error on every failure to avoid leaking cryptographic details.
func SecretAuthMiddleware ¶
func SecretAuthMiddleware(checker GroupChecker, getter PeerCredGetter, logger *slog.Logger) func(http.Handler) http.Handler
SecretAuthMiddleware returns HTTP middleware that restricts access to secret endpoints. Access is granted to root (UID 0) or processes whose user is a member of the plexd-secrets group.
The middleware extracts peer credentials from the request's underlying connection using a PeerCredGetter. In production, this is backed by SO_PEERCRED; in tests, a mock can be injected.
func SetSocketPermissions ¶
SetSocketPermissions sets ownership and permissions on the Unix socket file. If the plexd group exists, the socket is chowned to root:plexd with mode 0660. If the group does not exist, the socket gets mode 0666 and a warning is logged.
Types ¶
type ActionProvider ¶
type ActionProvider interface {
Capabilities() ([]api.ActionInfo, []api.HookInfo)
}
ActionProvider supplies action and hook information to the local API.
type Config ¶
type Config struct {
// SocketPath is the path to the Unix domain socket.
// Default: /var/run/plexd/api.sock
SocketPath string `yaml:"socket_path"`
// HTTPEnabled enables the optional HTTP listener.
// Default: false
HTTPEnabled bool `yaml:"http_enabled"`
// HTTPListen is the HTTP listen address.
// Default: 127.0.0.1:9100
HTTPListen string `yaml:"http_listen"`
// HTTPTokenFile is the path to the HTTP bearer token file.
HTTPTokenFile string `yaml:"http_token_file"`
// DebouncePeriod is the debounce period for coalescing events.
// Default: 5s
DebouncePeriod time.Duration `yaml:"debounce_period"`
// ShutdownTimeout is the maximum time to wait for a graceful shutdown.
// Default: 5s
ShutdownTimeout time.Duration `yaml:"shutdown_timeout"`
// DataDir is the path to the data directory (required).
DataDir string `yaml:"data_dir"`
// SecretAuthEnabled enables SO_PEERCRED-based authentication for
// /v1/state/secrets/* routes on the Unix socket. When enabled, only
// root (UID 0) or plexd-secrets group members may access secrets.
// Default: false (enabled by cmd/plexd/cmd/up.go in production).
SecretAuthEnabled bool `yaml:"secret_auth_enabled"`
}
Config holds the configuration for the local node API server. 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 ForwarderStatus ¶
type ForwarderStatus struct {
Enabled bool `json:"enabled"`
BufferSize int `json:"buffer_size"`
SourceCount int `json:"source_count"`
ErrorCount int `json:"error_count"`
LastReportAt string `json:"last_report_at,omitempty"`
}
ForwarderStatus describes the operational status of a log or audit forwarder.
type ForwarderStatusProvider ¶
type ForwarderStatusProvider interface {
ForwarderStatus() ForwarderStatus
}
ForwarderStatusProvider supplies forwarder status information.
type GroupChecker ¶
type GroupChecker interface {
// IsInGroup reports whether the user identified by uid belongs to the
// named group, or if the user's primary group (gid) matches the group.
IsInGroup(uid, gid uint32, groupName string) bool
}
GroupChecker checks group membership for a given user.
type Handler ¶
type Handler struct {
// contains filtered or unexported fields
}
Handler provides HTTP handlers for the local node API.
func NewHandler ¶
func NewHandler(cache *StateCache, secretFetcher SecretFetcher, nodeID string, nsk []byte, logger *slog.Logger) *Handler
NewHandler creates a new Handler.
func (*Handler) SetActionProvider ¶
func (h *Handler) SetActionProvider(provider ActionProvider)
SetActionProvider sets the action provider for action/hook endpoints.
func (*Handler) SetAuditStatus ¶
func (h *Handler) SetAuditStatus(p ForwarderStatusProvider)
SetAuditStatus sets the audit forwarder status provider.
func (*Handler) SetHookReloader ¶
func (h *Handler) SetHookReloader(reloader HookReloader)
SetHookReloader sets the hook reloader for the reload endpoint.
func (*Handler) SetLogStatus ¶
func (h *Handler) SetLogStatus(p ForwarderStatusProvider)
SetLogStatus sets the log forwarder status provider.
func (*Handler) SetPeerProvider ¶
func (h *Handler) SetPeerProvider(p PeerProvider)
SetPeerProvider sets the peer status provider.
func (*Handler) SetPolicyProvider ¶
func (h *Handler) SetPolicyProvider(p PolicyProvider)
SetPolicyProvider sets the policy provider.
type HookReloader ¶
HookReloader triggers a re-scan of hooks from the filesystem.
type LocalActionRunner ¶
type LocalActionRunner interface {
RunLocal(ctx context.Context, action string, params map[string]string) (stdout, stderr string, exitCode int, err error)
}
LocalActionRunner runs a built-in action synchronously and returns output.
type NodeAPIClient ¶
type NodeAPIClient interface {
SecretFetcher
ReportSyncClient
}
NodeAPIClient combines the control plane methods needed by the node API server.
type OSGroupChecker ¶
type OSGroupChecker struct{}
OSGroupChecker checks group membership using the OS user/group database.
type PeerCredGetter ¶
type PeerCredGetter interface {
GetPeerCredentials(r *http.Request) (*PeerCredentials, error)
}
PeerCredGetter extracts peer credentials from an HTTP request's underlying connection.
type PeerCredentials ¶
PeerCredentials holds the peer credentials extracted from a Unix socket connection.
func GetPeerCredentials ¶
func GetPeerCredentials(conn net.Conn) (*PeerCredentials, error)
GetPeerCredentials extracts peer credentials from a Unix socket connection using the SO_PEERCRED socket option. Returns an error if the connection is not a Unix socket or the credentials cannot be retrieved.
type PeerProvider ¶
type PeerProvider interface {
PeerStatuses() []PeerStatus
}
PeerProvider supplies mesh peer information.
type PeerStatus ¶
type PeerStatus struct {
ID string `json:"id"`
PublicKey string `json:"public_key"`
MeshIP string `json:"mesh_ip"`
Endpoint string `json:"endpoint"`
}
PeerStatus describes a single mesh peer's status.
type PolicyProvider ¶
type PolicyProvider interface {
ActivePolicy() *api.PolicySnapshot
}
PolicyProvider supplies the active merged network policy.
type ReportEntry ¶
type ReportEntry struct {
Key string `json:"key"`
ContentType string `json:"content_type"`
Payload json.RawMessage `json:"payload"`
Version int `json:"version"`
UpdatedAt time.Time `json:"updated_at"`
}
ReportEntry represents a locally-managed report entry.
type ReportSyncClient ¶
type ReportSyncClient interface {
PutStateReport(ctx context.Context, nodeID, key string, req api.NodeStateReportRequest) (*api.NodeStateReportResponse, error)
DeleteStateReport(ctx context.Context, nodeID, key string) error
}
ReportSyncClient publishes per-key node state reports to the control plane.
type ReportSyncer ¶
type ReportSyncer struct {
// contains filtered or unexported fields
}
ReportSyncer reconciles per-key report changes to the control plane. Changes are held in a dirty map keyed by report key; a nil value marks a pending delete. NotifyChange debounces to coalesce bursts, flush walks the dirty keys in ascending order publishing one at a time, and keys left dirty by a retryable failure are re-flushed on a timer so state converges without a new local mutation.
func NewReportSyncer ¶
func NewReportSyncer(client ReportSyncClient, debouncePeriod time.Duration, logger *slog.Logger) *ReportSyncer
NewReportSyncer creates a ReportSyncer. The node ID is supplied later to Run because the syncer is constructed in NewServer, before the node has registered and its ID is known.
func (*ReportSyncer) NotifyChange ¶
func (s *ReportSyncer) NotifyChange(entries []ReportEntry, deleted []string)
NotifyChange merges report changes into the dirty map and wakes the run loop. entries are pending PUTs and deleted keys are pending deletes; the last change to a given key wins.
func (*ReportSyncer) Run ¶
func (s *ReportSyncer) Run(ctx context.Context, nodeID string) error
Run reconciles report changes to the control plane for nodeID until ctx is cancelled, at which point it returns ctx.Err(). Dirty state is preserved in memory across the return so a subsequent Run resumes where this one left off.
type SecretFetcher ¶
type SecretFetcher interface {
FetchSecret(ctx context.Context, nodeID, name string, version int) (*api.SecretEnvelope, error)
}
SecretFetcher abstracts the control plane client for secret retrieval.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server is the local node API server. It serves HTTP over a Unix socket and optionally over TCP with bearer token authentication.
func NewServer ¶
NewServer creates a new Server. Config defaults are applied automatically. The cache is initialized eagerly so that ReconcileHandler can be called before Start.
func (*Server) Cache ¶
func (s *Server) Cache() *StateCache
Cache returns the server's state cache for use by SSE event handlers.
func (*Server) PublishReport ¶ added in v0.2.0
func (s *Server) PublishReport(key, contentType string, payload json.RawMessage) error
PublishReport writes a report entry through the cache and notifies the syncer so it converges to the control plane. It is the seam through which internal producers publish status blocks, and it holds key and payload to the same grammar and 4096-byte value cap as the local HTTP API. content_type and the resulting version stay local-only; the syncer ships only the payload value.
func (*Server) ReconcileHandler ¶
func (s *Server) ReconcileHandler() reconcile.ReconcileHandler
ReconcileHandler returns a reconcile.ReconcileHandler that refreshes the cache from the desired state block whenever it changes. The block's metadata bucket becomes the cache metadata map, and its opaque data bucket feeds the versioned data store (GET /v1/state/data): each api.StateEntry value is JSON-encoded into an api.DataEntry payload under dataStateContentType. A nil state block authoritatively clears both buckets.
The secret index is not fed here: secret references no longer ride the snapshot and secret values are always fetched live, so the pull leaves it untouched.
func (*Server) ReportPayload ¶ added in v0.2.0
func (s *Server) ReportPayload(key string) (json.RawMessage, bool)
ReportPayload returns the payload currently stored under key, and whether the key exists. Internal producers use it to compare what is actually published against what they would publish, so a report another local caller overwrote or deleted is re-asserted rather than assumed to still hold their last value.
func (*Server) SetActionProvider ¶
func (s *Server) SetActionProvider(provider ActionProvider)
SetActionProvider sets the action provider for action/hook endpoints. Must be called before Start.
func (*Server) SetAuditStatus ¶
func (s *Server) SetAuditStatus(p ForwarderStatusProvider)
SetAuditStatus sets the audit forwarder status provider. Must be called before Start.
func (*Server) SetHookReloader ¶
func (s *Server) SetHookReloader(reloader HookReloader)
SetHookReloader sets the hook reloader for the reload endpoint. Must be called before Start.
func (*Server) SetLogStatus ¶
func (s *Server) SetLogStatus(p ForwarderStatusProvider)
SetLogStatus sets the log forwarder status provider. Must be called before Start.
func (*Server) SetPeerProvider ¶
func (s *Server) SetPeerProvider(p PeerProvider)
SetPeerProvider sets the peer status provider. Must be called before Start.
func (*Server) SetPolicyProvider ¶
func (s *Server) SetPolicyProvider(p PolicyProvider)
SetPolicyProvider sets the policy provider. Must be called before Start.
type StateCache ¶
type StateCache struct {
// contains filtered or unexported fields
}
StateCache holds node state in memory with file persistence.
func NewStateCache ¶
func NewStateCache(dataDir string, logger *slog.Logger) *StateCache
NewStateCache creates a new StateCache with empty maps. dataDir is the base path; the state subdirectory tree will be created under dataDir/state/.
func (*StateCache) DeleteReport ¶
func (sc *StateCache) DeleteReport(key string) error
DeleteReport removes a report entry and its file. Returns ErrNotFound if the key does not exist.
func (*StateCache) GetData ¶
func (sc *StateCache) GetData() map[string]api.DataEntry
GetData returns a copy of the data map.
func (*StateCache) GetDataEntry ¶
func (sc *StateCache) GetDataEntry(key string) (api.DataEntry, bool)
GetDataEntry returns a data entry by key and whether it exists.
func (*StateCache) GetMetadata ¶
func (sc *StateCache) GetMetadata() map[string]string
GetMetadata returns a copy of the metadata map, with the delivery-mode diagnostic overlaid as the delivery_mode key when a mode is set.
func (*StateCache) GetMetadataKey ¶
func (sc *StateCache) GetMetadataKey(key string) (string, bool)
GetMetadataKey returns the value for a metadata key and whether it exists. The delivery_mode key resolves to the delivery-mode diagnostic.
func (*StateCache) GetReport ¶
func (sc *StateCache) GetReport(key string) (ReportEntry, bool)
GetReport returns a report entry by key and whether it exists.
func (*StateCache) GetReports ¶
func (sc *StateCache) GetReports() map[string]ReportEntry
GetReports returns a copy of the reports map.
func (*StateCache) GetSecretIndex ¶
func (sc *StateCache) GetSecretIndex() []api.SecretRef
GetSecretIndex returns a copy of the secret index.
func (*StateCache) Load ¶
func (sc *StateCache) Load() error
Load reads persisted state from disk. Missing files or directories are treated as fresh (empty) state. The directory tree is created if absent.
func (*StateCache) OrphanedReportKeys ¶ added in v0.2.0
func (sc *StateCache) OrphanedReportKeys() []string
OrphanedReportKeys returns the keys of the persisted reports the last Load rejected as outside the current key grammar. A previous release may have synced them to the control plane, where nothing local can reach them any more, so the caller queues a delete for each.
func (*StateCache) PutReport ¶
func (sc *StateCache) PutReport(key, contentType string, payload json.RawMessage, ifMatch *int) (ReportEntry, error)
PutReport creates or updates a report entry. If the entry exists and ifMatch is non-nil, it must equal the current version or ErrVersionConflict is returned. Version starts at 1 for new entries and increments on update.
func (*StateCache) SetDeliveryMode ¶ added in v0.2.0
func (sc *StateCache) SetDeliveryMode(mode string)
SetDeliveryMode records the SSE delivery channel diagnostic. It is stored separately from the snapshot-owned metadata map and is intentionally not persisted: the value is re-seeded from the live SSE manager on every startup.
func (*StateCache) UpdateData ¶
func (sc *StateCache) UpdateData(entries []api.DataEntry)
UpdateData replaces data entries in memory and persists each to data/{key}.json. Files for entries no longer present are removed.
func (*StateCache) UpdateMetadata ¶
func (sc *StateCache) UpdateMetadata(m map[string]string)
UpdateMetadata replaces the metadata in memory and persists to metadata.json.
func (*StateCache) UpdateSecretIndex ¶
func (sc *StateCache) UpdateSecretIndex(refs []api.SecretRef)
UpdateSecretIndex replaces the secret index in memory and persists to secrets.json.
type StateSummary ¶
type StateSummary struct {
Metadata map[string]string `json:"metadata"`
DataKeys []dataKeySummary `json:"data_keys"`
SecretKeys []secretKeySummary `json:"secret_keys"`
ReportKeys []reportKeySummary `json:"report_keys"`
}
StateSummary is the response for GET /v1/state.