autoexclude

package
v1.0.97 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 13, 2026 License: MIT Imports: 5 Imported by: 0

Documentation

Overview

Package autoexclude is the adaptive decryption-exclusion cache: a bounded, TTL-bounded, in-memory learned set of hosts that could not be SSL-inspected, so that subsequent CONNECTs to them can fail open (bypass decryption) instead of breaking. It is the ADAPTIVE half of decryption exclusion; the MANUAL half (operator-authored bypass patterns) lives in internal/sslbypass.

This is PAN-OS's "local SSL decryption exclusion cache" modeled for a multi-tenant forward proxy. Four design choices make it safe to auto-disable inspection for a host based on a runtime signal:

  1. SCOPED KEY. Every entry is keyed by (scope, host), where scope is an explicit policy boundary — the matched decryption profile's identity. A host learned under one fail-open profile is consulted ONLY for sessions matched to that same profile, so one fail-open rule/profile/tenant can never create a bypass consumed by another rule targeting the same host. Host-only keying is NOT policy isolation; the scope is.

  2. CONFIRM-COUNT over distinct CLIENT-EVIDENCE tokens. A host is not excluded on the first failure. The cache holds a PENDING observation per (scope, host, reason) accumulating the distinct client-evidence tokens (authenticated identity when available, else client address — the caller decides; the engine treats the token opaquely) that hit a qualifying failure within a rolling window; only when the count reaches confirmN is the host promoted. A single endpoint therefore cannot self-poison.

  3. The CALLER gates BOTH the learn (Observe) and the read (Contains) on the matched rule's fail-open opt-in. This engine stores and answers; it never decides policy. Critical origins kept on fail-close rules are never learned or consulted, so they are un-poisonable by design.

  4. VOLATILE. In-memory only, never persisted, never synced CP->DP, off every config surface. A restart re-learns cheaply. (Per-node exclusions match PAN-OS's per-firewall local cache.)

Concurrency: an RWMutex guards both maps. Contains (the per-CONNECT hot read, fail-open rules only) takes the READ lock and bumps the per-entry hit counter ATOMICALLY, so concurrent reads on different hosts run fully parallel; writers (Observe/Remove/Clear/evict) take the write lock, which excludes all readers so an entry is never mutated or deleted while a reader holds it.

Index

Constants

View Source
const (
	DefaultTTL        = 12 * time.Hour
	DefaultPinnedTTL  = 1 * time.Hour
	DefaultConfirmN   = 2
	DefaultWindow     = 10 * time.Minute
	DefaultMaxEntries = 4096
)

Defaults. TTL matches the PAN-OS local-cache default (12h) for the server-observed reasons; PinnedTTL is shorter because the client signal is the spoofable class. ConfirmN=2 distinct client-evidence tokens blocks single-endpoint poisoning while letting a real fleet-wide incompatibility promote quickly. Window bounds how long partial observations accumulate.

Variables

This section is empty.

Functions

This section is empty.

Types

type Cache

type Cache struct {
	// contains filtered or unexported fields
}

Cache is the learned-exclusion store. The zero value is NOT ready; use New.

func New

func New(cfg Config) *Cache

New builds a Cache from cfg, applying Default* for any zero field.

func (*Cache) Clear

func (c *Cache) Clear() int

Clear evicts every active exclusion and pending observation. Returns the number of active entries removed.

func (*Cache) Contains

func (c *Cache) Contains(scopeID, host string) (Reason, bool)

Contains reports whether (scopeID, host) is actively excluded, returning the learn reason. On a hit it increments that entry's blast-radius hit counter. Expired entries read as absent (lazy — physical removal happens in evict/List).

func (*Cache) Len

func (c *Cache) Len() int

Len is the current count of active (non-expired) exclusions.

func (*Cache) List

func (c *Cache) List() []Entry

List returns a stable, expired-filtered snapshot of active exclusions, sorted by learnedAt (newest first) for a predictable UI ordering.

func (*Cache) Observe

func (c *Cache) Observe(scopeID, scopeName, host string, reason Reason, client string) (promoted bool)

Observe records a qualifying inspect failure for (scopeID, host) under reason, attributing it to the opaque distinct-evidence token `client` (the caller derives it — authenticated identity preferred, else client address). It reports whether this observation PROMOTED the (scope, host) to an active exclusion (promoted=true is the security-relevant "inspection just went dark" event — the caller fires the audit/alert/metric on it). If already actively excluded, it is a no-op returning false. An empty scopeID or host is ignored (fail-safe).

func (*Cache) PendingLen

func (c *Cache) PendingLen() int

PendingLen is the current count of in-progress (unconfirmed) observations.

func (*Cache) Remove

func (c *Cache) Remove(scopeID, host string) bool

Remove evicts one (scopeID, host). Returns true if it was present.

func (*Cache) Stats

func (c *Cache) Stats() Stats

Stats returns a snapshot of the cache configuration and occupancy.

type Config

type Config struct {
	TTL        time.Duration
	PinnedTTL  time.Duration
	ConfirmN   int
	Window     time.Duration
	MaxEntries int
	// Now is injectable for deterministic tests; nil ⇒ time.Now.
	Now func() time.Time
}

Config parameterizes a Cache. Zero fields fall back to the Default* constants.

type Entry

type Entry struct {
	ScopeID   string    `json:"scope_id"`   // decryption-profile identity that owns this exclusion
	ScopeName string    `json:"scope_name"` // human-readable scope (profile name) for the UI/audit
	Host      string    `json:"host"`
	Reason    Reason    `json:"reason"`
	LearnedAt time.Time `json:"learned_at"`
	ExpiresAt time.Time `json:"expires_at"`
	// Hits counts sessions that bypassed inspection because of this entry — the
	// blast-radius signal a security team triages against.
	Hits int64 `json:"hits"`
	// ClientCount is how many distinct client-evidence tokens were observed
	// failing before promotion (provenance for the learn decision).
	ClientCount int `json:"client_count"`
}

Entry is one active exclusion (a (scope, host) inspection is currently OFF for).

type Reason

type Reason string

Reason classifies WHY a host was learned. The set is bounded (safe as a metric label). Only these are ever learned; an untrusted/expired origin cert and every generic/ambiguous origin-controlled TLS alert are dropped by the caller's classifier, because auto-bypassing them is an exfil vector, not a compat fix.

const (
	// ReasonClientCertRequired is the origin-demanded-a-client-certificate reason:
	// the origin sent a CertificateRequest we cannot satisfy (a specific,
	// structured TLS signal — the one origin-leg reason allowed to live-rescue).
	ReasonClientCertRequired Reason = "client_cert_required"
	// ReasonUnsupportedParams is a genuine TLS-parameter incompatibility detected
	// LOCALLY by our own stack (no supported version overlap / no cipher overlap).
	// Learn-only: it enters pending learning but never live-rescues the triggering
	// session (it is lower-confidence than client-cert-required).
	ReasonUnsupportedParams Reason = "unsupported_params"
	// ReasonClientPinned is the client-rejected-our-forged-leaf reason (a pinned
	// app). Spoofable from the client side, so it is the reason most reliant on
	// the confirm-count and gets the shorter TTL. Learn-only.
	ReasonClientPinned Reason = "client_pinned"
)

type Stats

type Stats struct {
	Active     int `json:"active"`
	Pending    int `json:"pending"`
	ConfirmN   int `json:"confirm_n"`
	TTLSecs    int `json:"ttl_secs"`
	PinnedSecs int `json:"pinned_ttl_secs"`
	WindowSecs int `json:"window_secs"`
	MaxEntries int `json:"max_entries"`
}

Stats reports the cache posture for the read-only governance/API surface, so an operator can prove the feature's configuration (and that a no-fail-open deployment has an inert, empty cache).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL