Documentation
¶
Overview ¶
Package ingressdefense owns provider- and protocol-neutral ingress self-defense policy plus the bounded, process-local, exact-address hostile source state it drives. It is default kernel security for the standard HTTP data plane and stays required when optional feature plugins are absent, so it is not a feature plugin and not infrastructure policy.
The package knows nothing about HTTP paths, headers, auth providers, frontends, routing, providers, storage, or clocks. Request classification and responses belong to internal/stdhttp; configuration decoding and defaults belong to internal/core/config; process and generation composition belongs to internal/infra/runtimebundle.
Index ¶
- type Policy
- type Reason
- type State
- func (s *State) Clear(addr netip.Addr) int
- func (s *State) IsQuarantined(addr netip.Addr, now time.Time) bool
- func (s *State) Len() int
- func (s *State) RecordAuthFailure(addr netip.Addr, now time.Time, p Policy) Transition
- func (s *State) RecordProbe(addr netip.Addr, now time.Time, p Policy) Transition
- type StateLimits
- type Transition
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Policy ¶
type Policy struct {
// Enabled reports whether self-defense request enforcement runs.
Enabled bool
// AuthFailures is the counted unauthenticated-failure threshold that starts
// or escalates a quarantine when reached inside FailureWindow.
AuthFailures int
// FailureWindow bounds the counted authentication failures of one source.
FailureWindow time.Duration
// InitialQuarantine is the first-offense duration that later offenses grow
// from exponentially.
InitialQuarantine time.Duration
// MaxQuarantine is the ceiling that the exponential growth saturates at.
MaxQuarantine time.Duration
// AdaptiveExemptCIDRs is the read-only allowlist of sources exempt from
// adaptive state mutation and quarantine. It is never a ban list, it never
// overrides fixed network policy, and it never creates prefix-keyed state.
AdaptiveExemptCIDRs []netip.Prefix
}
Policy is the immutable, provider- and protocol-neutral request policy of one generation. It carries only bounded scalars plus the read-only adaptive exemption allowlist, and holds no source state, no credential, no request content, and no aggregate network policy. Callers must treat a compiled value as read-only; the state machine never mutates it.
func (Policy) AdaptiveExempt ¶
AdaptiveExempt reports whether addr falls inside the adaptive exemption allowlist. Exemption suppresses adaptive state mutation and quarantine only: it never overrides fixed network policy and never makes an impossible-path request valid.
func (Policy) Validate ¶
Validate reports whether the policy satisfies the bounded domain invariants the state machine depends on: a finite positive threshold and window, a positive first-offense duration, a ceiling that is not below it, and a normalized exemption allowlist. It applies to a filled, enabled policy: the zero value a nil CompiledSelfDefense projects is not a valid request policy and the state machine records nothing for a disabled one.
type Reason ¶
type Reason string
Reason is a finite denial or transition classification suitable for bounded metric labels. The set is closed: a reason never carries a request path, source address, prefix text, credential, principal, header, User-Agent, or any other attacker-controlled value.
const ( // ReasonImpossiblePath classifies a deterministic impossible-path rejection. ReasonImpossiblePath Reason = "impossible_path" // ReasonAuthFailureThreshold classifies quarantine started or escalated by // the authentication-failure threshold being reached inside the window. ReasonAuthFailureThreshold Reason = "auth_failure_threshold" // ReasonActiveQuarantine classifies refusal while a quarantine is active. ReasonActiveQuarantine Reason = "active_quarantine" // ReasonClientIPError classifies a request whose source address could not be // resolved into a usable identity. ReasonClientIPError Reason = "client_ip_error" )
func AllReasons ¶
func AllReasons() []Reason
AllReasons returns the closed reason set in stable metric-label order.
type State ¶
type State struct {
// contains filtered or unexported fields
}
State is the bounded, process-local, exact-address adaptive hostile-source state. It is process-owned and shared by every immutable generation: request policy arrives per call, capacity and inactivity TTL are fixed at construction, and nothing is persisted. The state owns no clock, no goroutine, no timer and no I/O: every operation takes the current instant from its caller so expiry is lazy on lookup and mutation and shutdown needs no cleanup.
A lock is held only for one map/ring mutation. No lock is ever held while calling authentication, HTTP handlers, logging, metrics or any other code.
Caller obligations, because the process-owned store outlives every generation and only the two recorders can see a per-generation policy: a caller must check Policy.Enabled before consulting this state at all, since the process store survives a reload that disables self-defense, and it must apply Policy.AdaptiveExempt before IsQuarantined, before RecordProbe, and before RecordAuthFailure.
func NewState ¶
func NewState(limits StateLimits) (*State, error)
NewState constructs the bounded process state for the given limits. Shard count is min(stateShardCount, MaxEntries) and each shard's capacity is MaxEntries divided across the shards, so the sum of shard capacities equals MaxEntries exactly and no address can be tracked beyond it.
func (*State) Clear ¶
Clear deletes the exact-address entry after a successful full authentication chain, discarding its accumulated offense level, and reports the number of tracked source entries afterwards. It is a reset, never a trust cache: a later hostile event starts a fresh offense level and a prior success never exempts a later impossible-path request.
func (*State) IsQuarantined ¶
IsQuarantined reports whether addr is quarantined at now. A read is not hostile activity: it never refreshes lastHostileAt, so a source cannot keep alive state it never attacks again. It does expire an entry that has been inactive for StateTTL, so a read path is also a lazy expiry path.
func (*State) Len ¶
Len reports the number of tracked source entries. It is an eventually exact gauge: every entry change is applied under a shard lock, so a quiescent state reports exactly and a concurrent caller may observe a neighbouring instant.
func (*State) RecordAuthFailure ¶
RecordAuthFailure records one counted unauthenticated-failure offense for addr and starts or escalates a quarantine when the configured threshold is reached inside the failure window. The window begins with the first counted failure.
This is the only auth-evidence entry point, so a 403, a 5xx, a provider error or a request that already established a principal cannot be recorded here: the classification of an authentication outcome is the auth adapter's decision, not this package's. Adaptive exemption is likewise the gate's decision. A counted failure below the threshold reports the zero Transition so the closed reason vocabulary stays reserved for quarantine transitions.
func (*State) RecordProbe ¶
RecordProbe records one strong hostile offense for addr: a matched impossible-path target is a deterministic rejection, so it counts immediately as a single offense on the same offense level the thresholded auth-failure window escalates. There is no weighted scoring engine behind it. The authentication window is left untouched because a path offense is not authentication evidence.
type StateLimits ¶
type StateLimits struct {
// MaxEntries is the hard cap on tracked source entries. The state must stay
// bounded at it through deterministic eviction or replacement.
MaxEntries int
// StateTTL is the hostile-inactivity lifetime after which one source entry
// and its accumulated offense level expire.
StateTTL time.Duration
}
StateLimits size the process-owned adaptive state. They are restart-required in v1 because one store is shared by every generation, so they are kept out of Policy and must never be projected into a request generation.
func (StateLimits) Validate ¶
func (l StateLimits) Validate() error
Validate reports whether the limits bound the process state: a positive capacity and a positive inactivity TTL.
type Transition ¶
type Transition struct {
// QuarantineStarted reports whether the event started or extended an active
// quarantine for the source.
QuarantineStarted bool
// QuarantineUntil is the absolute quarantine deadline, zero when no
// quarantine is active for the source.
QuarantineUntil time.Time
// Reason is the closed classification of the recorded event.
Reason Reason
// EntryCount is the number of tracked source entries after the event.
EntryCount int
}
Transition is the result of one recorded hostile event for one exact source address. It is the only value that crosses into HTTP/auth adapters and metrics, so it deliberately omits the offense level and any score: a quarantine denial must not disclose them.