Documentation
¶
Overview ¶
Package enforce holds the agent's installed-policy boundary. PolicyStore is what the sync loop installs each complete rendered policy into, atomically, so a host runs one version or the next and never a mixture (ADR-0003). The production implementation is the nftables backend in enforce/nft, which persists each policy durably here (PolicyFile, WriteDurable) before it replaces the owned table as a unit, and never touches state outside that table (ADR-0020). MemoryStore is the in-memory implementation the sync loop's tests run against. The local kill switch drops the owned table without control-plane involvement (ADR-0011).
Index ¶
Constants ¶
const PolicyFileName = "policy.bin"
PolicyFileName is the file in the agent state directory that holds the last applied policy.
Variables ¶
var ErrCorruptPolicyFile = errors.New("enforce: persisted policy file is corrupt")
ErrCorruptPolicyFile is returned by Load when the file exists but its content does not verify. A corrupt file is never partially trusted: the caller applies nothing from it and says so loudly (ADR-0011).
Functions ¶
func PolicyPath ¶
PolicyPath returns the policy file's path inside a state directory.
func WriteDurable ¶
WriteDurable replaces the file at path with data, mode 0600, so that a reader sees the old content or the new and never a mixture, and so that once it returns nil the new content survives a crash or a power loss: a temporary file beside path is written and synced, renamed into place, and then the directory holding it is synced, because the rename is an update of the directory and is not durable until the directory is. The directory is created, mode 0700, when missing.
An error before the rename leaves the old file exactly as it was. An error from the directory sync comes after the rename: the new content is in place but its durability is not established, and the caller treats the write as failed.
Types ¶
type MemoryStore ¶
type MemoryStore struct {
// Fail, when set, is consulted before an apply; a non-nil error is
// returned and nothing changes. Tests use it to exercise the FAILED
// path; production leaves it nil.
Fail func(policy *innerwallv1.WorkloadPolicy) error
// contains filtered or unexported fields
}
MemoryStore keeps the installed policy in memory. It is the store the daemon runs with until enforcement lands; it makes the apply loop, the acknowledgement protocol, and the failure path testable and observable without a firewall.
func (*MemoryStore) Apply ¶
func (m *MemoryStore) Apply(_ context.Context, policy *innerwallv1.WorkloadPolicy) error
Apply implements PolicyStore.
func (*MemoryStore) Current ¶
func (m *MemoryStore) Current() *innerwallv1.WorkloadPolicy
Current implements PolicyStore.
type PolicyFile ¶
type PolicyFile struct {
Path string
}
PolicyFile persists the last applied WorkloadPolicy so that it survives a daemon restart and a host reboot: the agent fails static, re-applying on start what it last acknowledged (ADR-0011, ADR-0018). The file is the deterministic serialization of the canonical policy, prefixed by a magic line and followed by a SHA-256 of the serialization, written durably with mode 0600.
func (PolicyFile) Load ¶
func (f PolicyFile) Load() (*innerwallv1.WorkloadPolicy, error)
Load reads the persisted policy. It returns nil, nil when no file exists, and ErrCorruptPolicyFile when the file exists but does not verify.
func (PolicyFile) Remove ¶
func (f PolicyFile) Remove() error
Remove deletes the persisted policy; a missing file is not an error.
func (PolicyFile) Save ¶
func (f PolicyFile) Save(policy *innerwallv1.WorkloadPolicy) error
Save writes policy durably (WriteDurable): a reader sees the old file or the new one and never a partial write, and once Save returns nil the new file survives a crash or a power loss. The store saves before it touches the kernel, so a policy is never acknowledged without its persisted copy (ADR-0020).
type PolicyStore ¶
type PolicyStore interface {
// Apply installs policy as the complete desired state. On success it is
// the installed state; on error the previously installed state is
// untouched, so the caller can acknowledge FAILED and stay on its last
// good version.
Apply(ctx context.Context, policy *innerwallv1.WorkloadPolicy) error
// Current returns the installed policy, or nil before the first Apply.
Current() *innerwallv1.WorkloadPolicy
}
PolicyStore is the installed desired state of one host. The sync loop computes each complete next policy (from a snapshot, or by applying a delta to the current policy) and calls Apply with it; it never asks the store to mutate rules incrementally. That is what lets an implementation replace the host's owned firewall table as a unit: a host runs version N or version N+1, never a mixture (ADR-0003, ADR-0015).
type Presence ¶ added in v0.2.0
type Presence struct {
// Probe reads whether the owned table is in the kernel.
Probe func(ctx context.Context) (bool, error)
// Installed reports whether the agent has installed a policy; before
// one there is no table to expect, and Check says nothing. Always
// true when nil.
Installed func() bool
// Removed is called when a check first finds the table gone, with the
// earliest instant the removal can have happened: the last instant the
// table was known present, or the check itself when none is known.
Removed func(since time.Time)
// Restored is called when an apply or a check finds the table
// installed again after Removed.
Restored func(at time.Time)
Log *slog.Logger
// Now is the clock; time.Now when nil.
Now func() time.Time
// contains filtered or unexported fields
}
Presence tracks whether the owned table the agent installed is still in the kernel. The local kill switch deletes it without the daemon or the control plane (ADR-0011), and with it goes everything the agent enforces and observes; the version the agent last acknowledged is unchanged by that, so it cannot say whether the table is there. Check reads the kernel when a heartbeat is composed and says, and the first check that finds the table gone reports the interval since it was last known present, so the blind period is recorded rather than read as quiet (issue #64).
Presence never reinstalls the table: removing it is the operator's kill switch, and the daemon reinstalls only on its next apply or restart, as the kill switch says.
func (*Presence) Check ¶ added in v0.2.0
func (p *Presence) Check(ctx context.Context) innerwallv1.OwnedTableState
Check reads the kernel and returns the owned table's state as a heartbeat carries it: unspecified before any policy is installed or when the read fails, present, or absent. A nil Presence says nothing.
func (*Presence) MarkInstalled ¶ added in v0.2.0
MarkInstalled records that an apply installed the table at at. A nil Presence records nothing.
type Watched ¶ added in v0.2.0
type Watched struct {
PolicyStore
Presence *Presence
}
Watched is a PolicyStore whose every successful apply is reported to a Presence: an apply that succeeded installed the table, whatever the kernel held before it.
func (Watched) Apply ¶ added in v0.2.0
func (w Watched) Apply(ctx context.Context, policy *innerwallv1.WorkloadPolicy) error
Apply implements PolicyStore.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package nft is the Linux enforcement backend: it installs a rendered WorkloadPolicy into one nftables table this agent owns, as a single atomic transaction, and never touches state outside that table (ADR-0003, ADR-0020).
|
Package nft is the Linux enforcement backend: it installs a rendered WorkloadPolicy into one nftables table this agent owns, as a single atomic transaction, and never touches state outside that table (ADR-0003, ADR-0020). |