Documentation
¶
Overview ¶
Package control is the enrolled agent's side of the controller protocol (SPEC §6; proto/shieldlist/v1/agent.proto): enrolment, the sync round trip, the long-lived stream, and the loop that keeps the two sides agreeing — what this machine decided goes up, what it must now hold comes down.
The agent dials out and never listens. Every cursor it presents is its own, persisted in its store after the answer has been acted on, so a lost message is asked for again and nothing is applied twice.
Index ¶
- func Enrol(ctx context.Context, base, token, hostname, agentVersion, minController string) (credential, fingerprint, controllerVersion string, err error)
- func Run(ctx context.Context, opts Options) error
- type Client
- type HTTPError
- type Handler
- type HitCount
- type Lift
- type Loop
- type Options
- type Order
- type Progress
- type Settings
- type Watched
- type WatchedLine
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Enrol ¶
func Enrol(ctx context.Context, base, token, hostname, agentVersion, minController string) (credential, fingerprint, controllerVersion string, err error)
Enrol presents a join token and returns the credential, the controller's version and the fingerprint the connection was pinned to (empty when the token carried none). The token is `secret` or `secret@fingerprint`.
Types ¶
type Client ¶
type Client struct {
URL string // base, e.g. https://controller.example.net:17453
Hostname string
Credential string
Fingerprint string // pinned; empty verifies against the system roots
AgentVersion string
MinController string
// contains filtered or unexported fields
}
Client talks to one controller as one agent.
func NewClient ¶
NewClient builds a client. A pinned fingerprint replaces chain verification: the join token carried it, and it names one controller.
func (*Client) Stream ¶
func (c *Client) Stream(ctx context.Context, revision string, fleetCursor, liftCursor, orderCursor uint64, handle func(*pb.SyncResponse) error) error
Stream holds the long-lived connection and hands every pushed answer to handle, until the context ends, the connection drops, or handle returns an error. Cursors and the held revision are what the stream starts from.
func (*Client) Sync ¶
func (c *Client) Sync(ctx context.Context, req *pb.SyncRequest) (*pb.SyncResponse, error)
Sync is one round trip.
type Handler ¶
type Handler interface {
// ApplyConfig makes the bundle under dir the running configuration.
// An error keeps the previous revision on record, so a fixed bundle
// is fetched again; the same broken revision is not retried.
ApplyConfig(revision, dir string) error
ApplySettings(s Settings)
// ApplyFleet enforces sanctions other machines decided (SPEC §5.4
// Distribution). Origin says which machine and rule.
ApplyFleet(sanctions []store.Sanction) error
// ApplyLift lifts a sanction on the controller's word; withdraw asks
// that what this machine reported about the address be withdrawn too.
ApplyLift(value, by, reason string, withdraw bool) error
// ApplyLifts is ApplyLift for a sync's worth at once.
ApplyLifts(lifts []Lift) error
// ApplyOrder bans on the operator's word — through the same checks as
// any sanction — and answers what happened in a sentence.
ApplyOrder(o Order) (string, error)
// ApplyOrders is ApplyOrder for a sync's worth of orders at once —
// one pass, one transaction, one batch — answering one result each.
ApplyOrders(os []Order) ([]string, error)
// GeoHash is the content hash of the geo database this machine holds
// of one kind (country, asn), "" for none; ApplyGeo installs a new
// edition, hash and all, and turns on what waited for it.
GeoHash(kind string) string
ApplyGeo(kind, hash string, data []byte) error
}
Handler is the agent's side of what comes down. Each call blocks until the agent has acted, because the cursor is persisted only after.
type HitCount ¶
type HitCount struct {
Value string
Count int // hits under rules in prod
Test int // hits under rules in test (a detection in the making, never a ban)
At time.Time
}
HitCount is one offender's positive hits since the last sync (a flow); At names the minute when the hits are a backfill, zero otherwise.
type Loop ¶
type Loop struct {
// contains filtered or unexported fields
}
Loop is one running conversation with the controller.
type Options ¶
type Options struct {
// Store is the loop's own handle on the agent's state, for READING:
// the round transaction belongs to the main loop, and so does every
// write — a second writer beside a long-lived read-then-write round
// is how that round finds its snapshot stale and loses its writes.
Store *store.Store
// Write runs fn with the main loop's store, on the main loop, inside
// its round — where every write to the agent's state belongs.
Write func(fn func(*store.Store) error) error
URL string
Hostname string
AgentVersion string
MinController string
// ReceivedDir is where bundles land: a full agent tree the agent's
// loader reads beside its own files.
ReceivedDir string
// Watch, when set, snapshots the offenders being counted but not yet
// convicted (eval.Watch): sent on every sync, bounded, never stored.
Watch func(limit int, linesSince time.Time) []Watched
// Flow, when set, hands over the positive hits since the last sync by
// offender (eval.Flow) — the flow the controller draws over time —
// and Unflow puts them back when the sync failed.
Flow func() []HitCount
Unflow func([]HitCount)
// Backfill, when set, reads the hits still in the windows that came
// before the given moment, by minute — sent once, when the controller
// says from when it holds this machine's flow.
Backfill func(before time.Time) []HitCount
// Interval is the sync cadence — the upload of what this machine
// decided, and the poll fallback when the stream cannot be held.
Interval time.Duration
// Wake, when set, is nudged by the machine's own loop the moment a
// round has banned someone or scored a hit: the sync leaves on their
// heels (a second at most, never more often than nudgeFloor) instead
// of waiting out the interval — what the console shows is then this
// second's bans and hits, not this half-minute's. A quiet machine
// syncs on the interval alone.
Wake <-chan struct{}
Handler Handler
Logger *slog.Logger
}
Options configure the loop.
type Order ¶
type Order struct {
ID uint64
Value string
Seconds int64 // 0: permanent
By string
Reason string
ReportAs string
Ports []uint16
// Lines: the log lines the operator banned from (a detection's), the
// manual sanction's evidence here.
Lines []store.EvidenceLine
}
Order is a ban the operator decided by hand, as the sync carries it.
type Settings ¶
type Settings struct {
State string // on | detect-only
EnforceFleet bool
OfferFleet bool
Reporting string // agent | controller | off
Level string
}
Settings are the controller's per-machine decisions that are not files (SPEC §5.4, §5.5), as the agent receives them on every answer.