Documentation
¶
Overview ¶
Package enforcement implements the endpoint side of AntiFlock's signed plan transaction. OS mutation is delegated to a least-privilege Driver; this package owns validation, ordering, verification, replay defense, and rollback.
Index ¶
- Variables
- func SignExecutionResult(result *antiflockv1.PlanExecutionResult, keyID string, ...) error
- func VerifyExecutionResult(result *antiflockv1.PlanExecutionResult, publicKey ed25519.PublicKey) error
- type CheckObservation
- type Config
- type Driver
- type Enforcer
- type FileStateStore
- func (store *FileStateStore) Complete(ctx context.Context, result *antiflockv1.PlanExecutionResult) error
- func (store *FileStateStore) Reserve(ctx context.Context, reservation Reservation) (*antiflockv1.PlanExecutionResult, error)
- func (store *FileStateStore) Revisions(ctx context.Context) (uint64, uint64, error)
- type MemoryStateStore
- type NftablesAdapter
- func (adapter *NftablesAdapter) Apply(ctx context.Context, operation *antiflockv1.PlanOperation) (OperationObservation, error)
- func (adapter *NftablesAdapter) DryRunApply(operation *antiflockv1.PlanOperation) (NftablesCommand, error)
- func (adapter *NftablesAdapter) DryRunRollback(operation *antiflockv1.PlanOperation) (NftablesCommand, error)
- func (adapter *NftablesAdapter) Rollback(ctx context.Context, operation *antiflockv1.PlanOperation) (OperationObservation, error)
- type NftablesCommand
- type NftablesConfig
- type NftablesExecRunner
- type NftablesRunner
- type OperationObservation
- type Reservation
- type StateStore
Constants ¶
This section is empty.
Variables ¶
var ( ErrPlanReplay = errors.New("plan replay or revision conflict") ErrPlanInProgress = errors.New("plan execution is already in progress") ErrPlanRejected = errors.New("plan was rejected") )
var ErrNftablesApplyDisabled = errors.New("nftables apply is disabled")
Functions ¶
func SignExecutionResult ¶
func SignExecutionResult(result *antiflockv1.PlanExecutionResult, keyID string, privateKey ed25519.PrivateKey, signedAt time.Time) error
SignExecutionResult applies the deterministic protobuf signing contract for antiflock.plan-result.v1. It mutates only NodeSignature.
func VerifyExecutionResult ¶
func VerifyExecutionResult(result *antiflockv1.PlanExecutionResult, publicKey ed25519.PublicKey) error
Types ¶
type CheckObservation ¶
type CheckObservation struct {
Outcome antiflockv1.CheckOutcome
ReasonCode string
SafeMessage string
Evidence []*antiflockv1.EvidenceReference
}
CheckObservation is supplied by a local deterministic check implementation. The enforcer validates its evidence before a required pass can commit.
type Config ¶
type Config struct {
DeploymentID string
NodeID string
PlanKeyID string
PlanPublicKey ed25519.PublicKey
NodeKeyID string
NodePrivateKey ed25519.PrivateKey
Capabilities *antiflockv1.CapabilityManifest
Driver Driver
StateStore StateStore
Clock func() time.Time
MaximumEvidenceAge time.Duration
RollbackTimeout time.Duration
}
type Driver ¶
type Driver interface {
Check(context.Context, *antiflockv1.PlanCheck) (CheckObservation, error)
Apply(context.Context, *antiflockv1.PlanOperation) (OperationObservation, error)
Rollback(context.Context, *antiflockv1.PlanOperation) (OperationObservation, error)
}
Driver is the only mutation boundary. Tests and simulations should provide an in-memory implementation; production implementations belong in isolated, least-privilege platform adapters.
type Enforcer ¶
type Enforcer struct {
// contains filtered or unexported fields
}
func (*Enforcer) Apply ¶
func (enforcer *Enforcer) Apply(ctx context.Context, input *antiflockv1.Plan) (*antiflockv1.PlanExecutionResult, error)
Apply executes one plan serially. Invalid plans return a signed REJECTED result together with ErrPlanRejected; transaction failures are represented by a terminal signed result and do not expose raw driver errors.
type FileStateStore ¶
type FileStateStore struct {
// contains filtered or unexported fields
}
FileStateStore is the Linux durable replay boundary for plan execution. It uses an OS lock across processes and atomically persists every reservation before any driver mutation. An interrupted in-progress reservation remains fail-closed and requires operator recovery; it is never silently retried.
func NewFileStateStore ¶
func NewFileStateStore(directory string) (*FileStateStore, error)
NewFileStateStore creates or validates a private Linux state directory. The resolved path and its final component must not traverse a symlink.
func (*FileStateStore) Complete ¶
func (store *FileStateStore) Complete(ctx context.Context, result *antiflockv1.PlanExecutionResult) error
func (*FileStateStore) Reserve ¶
func (store *FileStateStore) Reserve(ctx context.Context, reservation Reservation) (*antiflockv1.PlanExecutionResult, error)
type MemoryStateStore ¶
type MemoryStateStore struct {
// contains filtered or unexported fields
}
func NewMemoryStateStore ¶
func NewMemoryStateStore(lastPolicyRevision, lastPlanRevision uint64) *MemoryStateStore
func (*MemoryStateStore) Complete ¶
func (store *MemoryStateStore) Complete(ctx context.Context, result *antiflockv1.PlanExecutionResult) error
func (*MemoryStateStore) Reserve ¶
func (store *MemoryStateStore) Reserve(ctx context.Context, reservation Reservation) (*antiflockv1.PlanExecutionResult, error)
func (*MemoryStateStore) Revisions ¶
func (store *MemoryStateStore) Revisions() (uint64, uint64)
type NftablesAdapter ¶
type NftablesAdapter struct {
// contains filtered or unexported fields
}
NftablesAdapter renders an isolated fail-closed output table. It never flushes or replaces an existing table. Rollback deletes only a table that this adapter instance successfully created.
func NewNftablesAdapter ¶
func NewNftablesAdapter(runner NftablesRunner, config NftablesConfig) (*NftablesAdapter, error)
func (*NftablesAdapter) Apply ¶
func (adapter *NftablesAdapter) Apply(ctx context.Context, operation *antiflockv1.PlanOperation) (OperationObservation, error)
Apply crosses the nft process boundary only when EnableApply was explicitly set at construction. Dry-run rendering is always available.
func (*NftablesAdapter) DryRunApply ¶
func (adapter *NftablesAdapter) DryRunApply(operation *antiflockv1.PlanOperation) (NftablesCommand, error)
DryRunApply renders the only supported firewall action. Recovery entries must be literal IP addresses or CIDRs; hostnames require a separately verified resolver-to-address preparation step and therefore fail closed.
func (*NftablesAdapter) DryRunRollback ¶
func (adapter *NftablesAdapter) DryRunRollback(operation *antiflockv1.PlanOperation) (NftablesCommand, error)
func (*NftablesAdapter) Rollback ¶
func (adapter *NftablesAdapter) Rollback(ctx context.Context, operation *antiflockv1.PlanOperation) (OperationObservation, error)
Rollback refuses to delete a table unless this process observed its own successful creation. Crash recovery requires a durable ownership record and is intentionally left to the agent state integration.
type NftablesCommand ¶
type NftablesCommand struct {
Executable string `json:"executable"`
Arguments []string `json:"arguments"`
Input string `json:"input"`
}
NftablesCommand is a reviewable, shell-free dry run. Arguments and input are passed separately to the executor.
type NftablesConfig ¶
type NftablesExecRunner ¶
type NftablesExecRunner struct{}
NftablesExecRunner invokes exactly a trusted system nft binary with -f -. It discards command output so provider or host details cannot be copied into safe API messages.
type NftablesRunner ¶
NftablesRunner is the explicit privileged-process boundary. Implementations receive a prevalidated nft batch on stdin and must never invoke a shell.
type OperationObservation ¶
OperationObservation contains no raw command output. A Driver must sanitize provider and operating-system errors before setting SafeMessage.
type Reservation ¶
type Reservation struct {
PlanID string
Fingerprint string
PolicyRevision uint64
PlanRevision uint64
}
Reservation is the replay-safe identity of executable plan content.
type StateStore ¶
type StateStore interface {
Reserve(context.Context, Reservation) (*antiflockv1.PlanExecutionResult, error)
Complete(context.Context, *antiflockv1.PlanExecutionResult) error
}
StateStore atomically reserves a monotonic plan before mutation and records its terminal signed result. MemoryStateStore is only for tests and simulation; Linux enforcement should use FileStateStore so replay defense survives restart.