Skip to content

Kubernetes DaemonSet Deployment Reference

Reference documentation for deploying plexd as a Kubernetes DaemonSet. Covers environment detection, configuration, CRD controller, audit log reader, authentication, and manifest structure.

KubernetesEnvironment

KubernetesEnvironment holds metadata about the Kubernetes environment in which plexd is running. A nil value from Detect() indicates the process is not running inside a pod.

go
type KubernetesEnvironment struct {
    InCluster           bool
    Namespace           string
    PodName             string
    NodeName            string
    ServiceAccountToken string
}
FieldSource
InClustertrue if KUBERNETES_SERVICE_HOST env var is set
NamespaceRead from /var/run/secrets/kubernetes.io/serviceaccount/namespace
PodNameHOSTNAME env var
NodeNameMY_NODE_NAME env var (set via downward API)
ServiceAccountToken/var/run/secrets/kubernetes.io/serviceaccount/token

EnvironmentDetector interface

go
type EnvironmentDetector interface {
    Detect() *KubernetesEnvironment
}

DefaultDetector implements this interface using real environment variables and filesystem paths.

Config

Configuration for Kubernetes integration. Passed as a constructor argument; no file I/O in this package.

FieldTypeDefaultDescription
EnabledboolfalseMust be explicitly enabled
CRDEnabledbooltrue (when Enabled)Whether CRD management is active
Namespacestring(empty — auto-detected)Override namespace
AuditLogPathstring/var/log/kubernetes/audit/audit.logPath to Kubernetes audit log
CRDSyncIntervaltime.Duration10sInterval for CRD state reconciliation
TokenPathstring/var/run/secrets/kubernetes.io/serviceaccount/tokenPath to service account token

Methods

  • ApplyDefaults(env *KubernetesEnvironment) — Sets default values for zero-valued fields. When env is non-nil and InCluster is true, sets Enabled=true, CRDEnabled=true, and auto-detects Namespace.
  • Validate() error — Skips validation when Enabled is false. Validates Namespace non-empty, AuditLogPath non-empty, CRDSyncInterval >= 1s, TokenPath non-empty.

PlexdNodeState CRD

The PlexdNodeState custom resource definition exposes node state to the Kubernetes API.

go
type PlexdNodeState struct {
    Name            string               `json:"name"`
    Namespace       string               `json:"namespace"`
    UID             string               `json:"uid,omitempty"`
    ResourceVersion string               `json:"resourceVersion,omitempty"`
    Labels          map[string]string    `json:"labels,omitempty"`
    Spec            PlexdNodeStateSpec   `json:"spec"`
    Status          PlexdNodeStateStatus `json:"status,omitempty"`
    LastUpdate      time.Time            `json:"lastUpdate"`
}

type PlexdNodeStateSpec struct {
    NodeID     string            `json:"nodeId"`
    MeshIP     string            `json:"meshIp,omitempty"`
    Metadata   map[string]string `json:"metadata,omitempty"`
    Data       []DataEntry       `json:"data,omitempty"`
    SecretRefs []SecretRef       `json:"secretRefs,omitempty"`
}

type PlexdNodeStateStatus struct {
    Report []DataEntry `json:"report,omitempty"`
}

CRD manifest: deploy/kubernetes/crds/plexdnodestate-crd.yaml

PropertyValue
API groupplexd.plexsphere.com
KindPlexdNodeState
Pluralplexdnodestates
Short namepns
ScopeNamespaced
Versionv1alpha1

Printer columns: Node ID, Mesh IP, Age.

KubeClient interface

Abstracts Kubernetes API interactions for testability. All state-modifying methods are idempotent.

go
type KubeClient interface {
    GetNodeState(ctx context.Context, namespace, name string) (*PlexdNodeState, error)
    CreateNodeState(ctx context.Context, state *PlexdNodeState) error
    UpdateNodeState(ctx context.Context, state *PlexdNodeState) error
    DeleteNodeState(ctx context.Context, namespace, name string) error
    WatchNodeState(ctx context.Context, namespace, name string) (<-chan PlexdNodeStateEvent, error)
    CreateSecret(ctx context.Context, secret *KubeSecret) error
    UpdateSecret(ctx context.Context, secret *KubeSecret) error
    DeleteSecret(ctx context.Context, namespace, name string) error
    WatchPlexdHooks(ctx context.Context, namespace string) (<-chan PlexdHookEvent, error)
    UpdatePlexdHookStatus(ctx context.Context, hook *PlexdHook) error
    CreateJob(ctx context.Context, job *PlexdJob) error
}

Sentinel errors

ErrorValueDescription
ErrNotFoundkubernetes: resource not foundResource does not exist
ErrAlreadyExistskubernetes: resource already existsResource already exists
ErrUnauthorizedkubernetes: unauthorizedClient lacks valid credentials

CRDController

Periodically syncs local node state to a PlexdNodeState CRD resource.

go
type CRDController struct {
    client         KubeClient
    reportNotifier ReportNotifier
    cfg            Config
    namespace      string
    nodeID         string
    meshIP         string
    resourceName   string // "node-{node_id}"
    logger         *slog.Logger
}

Constructor

go
func NewCRDController(client KubeClient, cfg Config, nodeID, meshIP, namespace string, reportNotifier ReportNotifier, logger *slog.Logger) *CRDController

Config defaults are applied automatically.

ReportNotifier interface

go
type ReportNotifier interface {
    NotifyChange()
}

Called when the CRDController detects new or changed report entries in the status subresource.

Start

go
func (c *CRDController) Start(ctx context.Context) error

Creates or updates the PlexdNodeState resource and starts the status watcher goroutine. Blocks until ctx is cancelled.

Start behavior

  1. Ensures the CRD resource exists (create or update)
  2. Starts a watch on the resource for status changes
  3. When status report entries change, calls ReportNotifier.NotifyChange()
  4. Blocks until context is cancelled

K8sAuditLogReader

Implements auditfwd.K8sAuditReader by reading Kubernetes audit log files in JSON-lines format.

go
type K8sAuditLogReader struct {
    path   string
    offset int64
    logger *slog.Logger
}

Constructor

go
func NewK8sAuditLogReader(path string, logger *slog.Logger) *K8sAuditLogReader

ReadEvents

go
func (r *K8sAuditLogReader) ReadEvents(ctx context.Context) ([]auditfwd.K8sAuditEntry, error)

Behavior:

  • Reads from r.offset to end of file; only new entries since last call are returned
  • If the file does not exist or cannot be opened, returns an error
  • If the file was truncated (offset > file size), resets offset to 0 and reads from the beginning
  • Malformed JSON lines are skipped with a warning log
  • Timestamp is parsed from stageTimestamp, falling back to requestReceivedTimestamp
  • objectRef and responseStatus may be nil in the audit event; handled gracefully

Audit log format (JSON-lines):

Each line is a Kubernetes audit event:

json
{"kind":"Event","verb":"create","user":{"username":"admin"},"objectRef":{"resource":"pods","namespace":"default","name":"web-1"},"responseStatus":{"code":201},"stageTimestamp":"2025-01-15T10:30:01.000000Z"}

TokenReviewAuthenticator

Validates Kubernetes service account tokens via the TokenReview API.

go
type TokenReviewAuthenticator struct {
    client    TokenReviewClient
    logger    *slog.Logger
    audiences []string
}

Constructor

go
func NewTokenReviewAuthenticator(client TokenReviewClient, logger *slog.Logger, audiences []string) *TokenReviewAuthenticator

Authenticate

go
func (a *TokenReviewAuthenticator) Authenticate(ctx context.Context, token string) (*TokenReviewResult, error)
ConditionResult
Empty tokenError: kubernetes: authenticate: empty token
Client errorError: kubernetes: authenticate: review failed
Not authenticatedError: kubernetes: authenticate: token not authenticated
Audience mismatchError: kubernetes: authenticate: audience mismatch
SuccessReturns *TokenReviewResult

HTTPTokenReviewClient

Production implementation that calls the Kubernetes API server:

go
func NewHTTPTokenReviewClient(apiServer, saTokenPath string) *HTTPTokenReviewClient
  • Posts to {apiServer}/apis/authentication.k8s.io/v1/tokenreviews
  • Authenticates with the service account token read from saTokenPath
  • Loads cluster CA from /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
  • TLS minimum version: 1.2

Kubernetes manifests

All manifests are in deploy/kubernetes/.

FileResources
namespace.yamlNamespace (plexd-system)
crds/plexdnodestate-crd.yamlCustomResourceDefinition (PlexdNodeState)
crds/plexdhook-crd.yamlCustomResourceDefinition (PlexdHook)
serviceaccount.yamlServiceAccount
rbac.yamlClusterRole + ClusterRoleBinding + consumer roles
daemonset.yamlDaemonSet with host networking and capabilities
secret.yamlExample Secret template for bootstrap token

RBAC permissions

The plexd ClusterRole grants:

API GroupResourceVerbs
plexd.plexsphere.complexdnodestatesget, list, watch, create, update, patch, delete
plexd.plexsphere.complexdnodestates/statusget, patch, update
(core)secretscreate, get, update, delete
plexd.plexsphere.complexdhooksget, list, watch
plexd.plexsphere.complexdhooks/statusget, update, patch
batchjobscreate, get, list, watch
authentication.k8s.iotokenreviewscreate

Consumer RBAC roles:

RoleResourcesVerbs
plexd-state-readerplexdnodestatesget, list, watch
plexd-state-reporterplexdnodestates/statusget, patch
plexd-secrets-readersecretsget
plexd-hook-readerplexdhooksget, list, watch

DaemonSet configuration

SettingValueReason
hostNetworktrueWireGuard mesh requires host networking
dnsPolicyClusterFirstWithHostNetRequired with hostNetwork
priorityClassNamesystem-node-criticalEnsures scheduling on resource pressure
tolerationsoperator: ExistsRun on all nodes including control plane
readOnlyRootFilesystemtrueSecurity hardening
CapabilitiesNET_ADMIN, NET_RAWWireGuard interface management

Environment variables

VariableSourceDescription
MY_NODE_NAMEfieldRef: spec.nodeNameKubernetes node name (downward API)
PLEXD_BOOTSTRAP_TOKENsecretKeyRef: plexd-bootstrapBootstrap token from Secret

Volume mounts

Mount pathSourceAccess
/etc/plexdConfigMap plexd-configread-only
/var/lib/plexdhostPathread-write
/var/run/plexdhostPathread-write
/var/log/kubernetes/audithostPathread-only

Constants

ConstantValue
ServiceAccountBasePath/var/run/secrets/kubernetes.io/serviceaccount
DefaultTokenPath{ServiceAccountBasePath}/token
DefaultNamespacePath{ServiceAccountBasePath}/namespace
DefaultCACertPath{ServiceAccountBasePath}/ca.crt
DefaultAuditLogPath/var/log/kubernetes/audit/audit.log
DefaultCRDSyncInterval10s

See also