decryptprofile

package
v1.0.126 Latest Latest
Warning

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

Go to latest
Published: Jul 18, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package decryptprofile is the named SSL-decryption-profile engine: a PAN-OS-style "how to decrypt" object that policy rules reference by name to control HOW an inspected (SSLAction=Inspect) tunnel is decrypted — whether to inspect natively as HTTP/2, the upstream server-certificate-verification posture, the unsupported-TLS failure posture, the TLS version floor/cap, and the per-stream inactivity bound. It is the "how" half that complements the "what to match" half already in package main's policy rules + sslbypass.

This engine owns storage + validation ONLY. The resolvers that turn a matched rule's profile reference into a runtime decision (resolveStripALPN and friends) live in package main on the proxy hot path, mirroring how catgroup exposes the pure store while categoryGroupMatchesHost stays in main.

Concurrency: an RWMutex protects the store; reads (GetByName) take RLock, writes rebuild order under Lock. All validation is enforced in Add/Update AND ReplaceAll, because profiles are written by THREE paths — the admin API, config import, and CP→DP snapshot apply — and a handler-only check would be bypassed by the latter two.

Index

Constants

View Source
const (
	MinStallSecs = 5
	MaxStallSecs = 3600
)

Clamp bounds for StallTimeoutSecs. 0 means "engine default" (the caller substitutes sslInspectBodyStallTimeout); any other value must fall in [MinStallSecs, MaxStallSecs]. A too-low value kills legitimate long-poll/SSE/ large-download streams; a too-high value lets a slow-loris stream pin a handler + scan buffer. MaxStallSecs matches the default tunnel-idle ceiling (1h).

Variables

This section is empty.

Functions

func Validate

func Validate(p *Profile) error

Validate checks a profile's fields. Enforced on EVERY write path (Add/Update/ ReplaceAll) so config-import and CP→DP snapshot-apply cannot smuggle an invalid profile onto the data plane. Does not check name uniqueness (that's store-scoped).

Types

type Profile

type Profile struct {
	ID   string `json:"id"`
	Name string `json:"name"`

	// InspectHTTP2: nil = inherit (strip → HTTP/1.1, today's default); true =
	// native HTTP/2 inspection; false = force strip/HTTP-1.1.
	InspectHTTP2 *bool `json:"inspectHttp2,omitempty"`

	// CertVerification of the upstream (origin) cert on the inspect leg:
	// "" inherit (rule's TLSSkipVerify) | "strict" (verify, block untrusted/expired)
	// | "permissive" (verify, allow+log — DEFERRED enforcement) | "skip".
	CertVerification string `json:"certVerification,omitempty"`

	// OnUnsupported posture when the origin TLS can't be inspected (version/cipher
	// below floor): "" inherit | "fail-close" (drop — today's behavior) | "fail-open"
	// (raw-relay bypass — DEFERRED, superseded by OnInspectError for fail-open).
	OnUnsupported string `json:"onUnsupported,omitempty"`

	// OnInspectError is the adaptive decryption-exclusion / fail-open posture when
	// an inspected tunnel CANNOT be established because the host is incompatible
	// with inspection (origin demands a client cert we can't present, or the TLS
	// parameters are unsupported, or a pinned client rejects our forged leaf):
	// "" inherit (fail-close, today's behavior) | "fail-close" (502/drop) |
	// "fail-open" (record the host in the auto-exclusion cache after a
	// confirm-count of distinct clients, rescue the current session where it has
	// not yet committed to the client, and bypass subsequent sessions to the host
	// until the entry expires). It deliberately does NOT fire on an untrusted/
	// expired origin cert — that stays a block. See internal/autoexclude.
	OnInspectError string `json:"onInspectError,omitempty"`

	// MinTLSVersion / MaxTLSVersion floor and cap on the inspect handshakes:
	// "" inherit | "1.2" | "1.3".
	MinTLSVersion string `json:"minTlsVersion,omitempty"`
	MaxTLSVersion string `json:"maxTlsVersion,omitempty"`

	// StallTimeoutSecs per-stream inactivity bound; 0 = engine default, else
	// clamped to [MinStallSecs, MaxStallSecs].
	StallTimeoutSecs int `json:"stallTimeoutSecs,omitempty"`

	CreatedAt string `json:"created_at,omitempty"`
	UpdatedAt string `json:"updated_at,omitempty"`
}

Profile is a named decryption profile. Zero/empty fields mean "inherit the default" so an absent or partially-filled profile never silently changes today's behavior (back-compat with the pre-profile inline rule fields).

type Store

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

Store manages persistent decryption profiles with O(1) name lookups.

func New

func New() *Store

New builds an empty store.

func (*Store) Add

func (s *Store) Add(p Profile) (*Profile, error)

Add creates a new profile. Validates fields, then name uniqueness. Assigns a short unique ID (a truncated UUID) when absent (ID-preserving otherwise).

func (*Store) Delete

func (s *Store) Delete(name string) error

Delete removes a profile by name.

func (*Store) DeleteByID added in v1.0.87

func (s *Store) DeleteByID(id string) (string, error)

DeleteByID removes the profile with the given stable ID. Returns the removed profile's name (for audit) or an error if not found.

func (*Store) FailOpenScope added in v1.0.82

func (s *Store) FailOpenScope(name string) (id string, ok bool)

FailOpenScope returns the profile's ID and true IFF a profile with the given name exists AND opts into fail-open (OnInspectError=="fail-open"). It is a HOT-PATH accessor (resolveSSLAction calls it per CONNECT for fail-open rules): it reads only two string fields under the RLock and returns NO copy, avoiding the copyOut allocation a full GetByName pays. The learn/cold paths keep the copy-returning accessors.

func (*Store) FailOpenScopeByID added in v1.0.95

func (s *Store) FailOpenScopeByID(id string) (scope string, resolved bool)

FailOpenScopeByID resolves the autoexclude scope by the profile's stable ULID (references-by-id / rename-safe). resolved=true iff a profile with that id EXISTS; scope is its ID when that profile is fail-open, else "". The ID is AUTHORITATIVE: a resolved fail-close profile returns ("", true) so the caller does NOT fall back to the name — otherwise a rule whose id points at a fail-close profile but whose stale name points at a different fail-open one could get its (fail-close) session bypassed, violating the "fail-close is un-poisonable" invariant. Mirrors resolveDecryptionProfile: name fallback only when the id resolves to no profile at all. No-copy fast path; O(profiles), a small admin set, only on the SSL-inspect CONNECT path.

func (*Store) GetByID added in v1.0.87

func (s *Store) GetByID(id string) *Profile

GetByID returns a copy of the profile with the given stable ID, or nil.

func (*Store) GetByName

func (s *Store) GetByName(name string) *Profile

GetByName returns a profile by name (case-insensitive). O(1). nil if not found.

func (*Store) List

func (s *Store) List() []Profile

List returns a copy of all profiles (safe for JSON serialization).

func (*Store) Load

func (s *Store) Load(path string) error

Load reads profiles from a JSON file. Invalid profiles on disk are skipped with a log line (fail-safe — a corrupt entry never blocks startup or the valid ones).

func (*Store) Names

func (s *Store) Names() []string

Names returns all profile names (for UI dropdowns), in insertion order.

func (*Store) Path

func (s *Store) Path() string

Path reports the persistence path ("" = disabled).

func (*Store) Rename added in v1.0.95

func (s *Store) Rename(id, newName string) (oldName string, err error)

Rename changes the display name of the profile with the given stable ID, re-keying the name index (references-by-id: rules link by ID, so the rename is safe — the caller cascades the denormalized name onto referencing rules). Validates the new name is non-empty and not already taken by a DIFFERENT profile. Returns the OLD name (for audit + the caller's cascade). A case-only change updates the display name in place without a collision check.

func (*Store) ReplaceAll

func (s *Store) ReplaceAll(profiles []Profile)

ReplaceAll atomically replaces all profiles (cluster sync / rollback / import). Invalid profiles are skipped (fail-safe) so a bad remote/imported entry never takes down the store or the valid entries. IDs are PRESERVED as provided (backfilled only when empty) so a capture→apply→re-capture round-trip is stable.

func (*Store) Save

func (s *Store) Save()

Save persists the current profiles to disk (atomic write). No-op when path unset.

func (*Store) SetPathForTest

func (s *Store) SetPathForTest(path string)

SetPathForTest points persistence at path without loading.

func (*Store) Update

func (s *Store) Update(p Profile) error

Update replaces an existing profile's fields (by name). Preserves ID + CreatedAt.

func (*Store) UpdateByID added in v1.0.87

func (s *Store) UpdateByID(id string, p Profile) error

UpdateByID replaces the content of the profile with the given stable ID (rename-safe addressing). Like the name-keyed Update, it edits content and keeps the profile's current name + CreatedAt + ID — position/identity are not changed by an edit. Returns error if no profile carries the id.

Jump to

Keyboard shortcuts

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