policy

package
v0.8.0 Latest Latest
Warning

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

Go to latest
Published: Nov 6, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

Documentation

Overview

Package policy is lockhttp's policy: the profile the human writes in the console account's config file, in canonical form. The console approves it (lockkit's "the AI tightens, the human loosens" review) and serves it to the gate; the gate never reads a policy file.

Index

Constants

View Source
const (
	// ModeEnforce applies every verdict.
	ModeEnforce = "enforce"
	// ModeShadow logs what would have asked or been denied and lets it
	// pass; the floor and secret matches stay enforced.
	ModeShadow = "shadow"
	// ModeOff (heuristics only) computes no score.
	ModeOff = "off"
)

Modes of the policy and of the heuristics.

View Source
const (
	VerdictDeny = "deny"
	VerdictAsk  = "ask"
	// VerdictAuto asks without NeverAuto: auto mode may answer it.
	VerdictAuto = "auto"
	VerdictFree = "free"
)

Verdicts of a rule.

View Source
const (
	DefaultBufferLimit  = 10 << 20
	DefaultHold         = 60
	DefaultConnectGrant = 600
	DefaultIdle         = 24 * 3600
	DefaultMaxSession   = 24 * 3600
)

Defaults (decisions D5 and the connect grant).

View Source
const ConfigHeader = `` /* 207-byte string literal not displayed */

ConfigHeader opens the config file `lockhttp profile add` creates.

Variables

View Source
var AnthropicDomains = []string{"anthropic.com", "claude.ai", "claude.com"}

AnthropicDomains are the domains whose other hosts always ask (NeverAuto) unless a rule says otherwise.

View Source
var ClaudeHosts = []string{"api.anthropic.com", "claude.ai", "console.anthropic.com", "statsig.anthropic.com"}

ClaudeHosts are free at the domain in phase 1 (CONNECT): the hosts Claude Code reaches. Any other host under the Anthropic domains asks.

View Source
var DefaultRedirectCDN = []string{
	"ghcr.io>pkg-containers.githubusercontent.com",
	"github.com>objects.githubusercontent.com",
	"github.com>release-assets.githubusercontent.com",
	"github.com>codeload.github.com",
	"api.github.com>codeload.github.com",
	"registry-1.docker.io>production.cloudflare.docker.com",
	"proxy.golang.org>storage.googleapis.com",
	"pypi.org>files.pythonhosted.org",
}

DefaultRedirectCDN are the CDNs an origin's 3xx may send a GET to (the exact target, once, within 60 s), whatever the policy adds.

View Source
var DocsTrusted = []string{
	"go.dev", "pkg.go.dev", "proxy.golang.org", "sum.golang.org", "storage.googleapis.com",
	"docs.python.org", "pypi.org", "files.pythonhosted.org",
	"registry.npmjs.org", "nodejs.org",
	"developer.mozilla.org", "docs.docker.com", "kubernetes.io", "man7.org", "docs.anthropic.com",
	"github.com", "api.github.com", "codeload.github.com", "objects.githubusercontent.com",
	"raw.githubusercontent.com", "release-assets.githubusercontent.com",
	"crates.io", "static.crates.io", "index.crates.io",
	"deb.debian.org", "security.debian.org", "archive.ubuntu.com", "security.ubuntu.com",
	"dl-cdn.alpinelinux.org",
}

DocsTrusted is the initial trusted read set (decision D8).

View Source
var MessagingHosts = []string{
	"hooks.slack.com", "slack.com", "*.slack.com",
	"gmail.googleapis.com", "graph.microsoft.com", "outlook.office.com", "outlook.office365.com",
	"discord.com", "discordapp.com", "api.telegram.org", "api.twilio.com",
	"api.sendgrid.com", "api.mailgun.net", "api.postmarkapp.com", "smtp.gmail.com",
	"api.pushover.net", "ntfy.sh", "webhook.site", "*.webhook.office.com",
}

MessagingHosts send messages on someone's behalf: every write asks, and no rule may make them auto.

View Source
var PublishHosts = []string{
	"registry.npmjs.org", "upload.pypi.org", "test.pypi.org", "crates.io", "rubygems.org",
	"registry-1.docker.io", "ghcr.io", "quay.io", "*.pkg.dev", "*.azurecr.io",
	"api.nuget.org", "repo.maven.apache.org", "uploads.github.com",
}

PublishHosts are package and image registries: a write there publishes.

Functions

func AmbiguousPath added in v0.8.0

func AmbiguousPath(path string) bool

AmbiguousPath reports whether an escaped path may name, at a target, another path than its string, so that a path-scoped credential must not be added to it. Only a canonical path is unambiguous (an allowlist, not a list of known tricks):

  • its bytes are RFC 3986 unreserved characters (ALPHA, DIGIT, '-', '.', '_', '~') and '/', or percent-escapes;
  • an escape of an ASCII byte names a reserved character other than '/' (":?#[]@!$&'()*+,;="): never '.', '/', '\', '%', a space, a control byte or an unreserved character ("%2e", "%2f", "%5c", "%25", "%20", "%00", "%41");
  • escapes of bytes >= 0x80 decode to valid, shortest-form UTF-8 of letters, marks or decimal digits ("%c3%a9"): never an overlong form ("%c0%ae"), a stray byte, a space, a punctuation sign or a symbol (a fullwidth dot or slash a target may normalise to '.' or '/'), never a default-ignorable letter or mark ("%cd%8f" U+034F, "%ef%b8%8f" U+FE0F, "%e3%85%a4" U+3164), and a mark only right after a letter, a digit or such a mark, never after a dot, a reserved character or at the start of a segment ("..%cc%b8", ".%cc%81.", "%cc%81..") (a target that drops ignorables or marks would read "..");
  • no segment, decoded and cut at its first reserved character (";", "?", ":", ...), is made only of dots ("." "..", "...", "..;x", "..%3f").

Anything else (literal sub-delims or spaces, a backslash, an invalid escape, a twice escaped byte) is ambiguous: a target that resolves dot segments, trims dots, spaces or NULs, decodes the path or reads overlong UTF-8 would route it outside the prefix it matched as a string.

func CheckHostPattern

func CheckHostPattern(key, h string, single bool) (string, error)

CheckHostPattern validates and normalises a host or "*.domain" pattern. A single-label host is accepted only when single is set (internal hosts); IP literals never are.

func CredentialName added in v0.8.0

func CredentialName(name string) bool

CredentialName reports whether name is a valid name of a secret, in the policy and in the gate's secrets file: lower-case letters, digits, '_', '-' and '.', at most 32 characters, no run of more than 16 letters and digits (a name, not a pasted token).

func DefaultConfig

func DefaultConfig(name string, production bool, extra []string) string

DefaultConfig is a new config file: ConfigHeader and DefaultProfile.

func DefaultProfile added in v0.7.0

func DefaultProfile(name string, production bool, extra []string) string

DefaultProfile is the profile `lockhttp profile add` writes: the docs set, plus extra (checked entries, from ImportHosts) as trusted reads. name is a valid profile name (config.CheckProfileName).

func FormatSize

func FormatSize(n int64) string

FormatSize prints n bytes for a human.

func HostMatch

func HostMatch(pat, host string) bool

HostMatch matches a host against a pattern: an exact host, "*.domain" (any subdomain, not the domain itself) or "*" (any host).

func ImportHosts added in v0.7.0

func ImportHosts(imports []string) ([]string, error)

ImportHosts reads hosts to trust for reads in the format of lea's LEA_EGRESS_ALLOW: "example.org" trusts that host, ".corp.example" the domain and its subdomains. It returns the trusted entries they make, checked and normalised. Its error names the entry's position, never the entry (it may be anything pasted in the wrong place).

func IsMessaging

func IsMessaging(host string) bool

IsMessaging reports whether host sends messages.

func IsPublish

func IsPublish(host string) bool

IsPublish reports whether a write to host publishes a package or image.

func IsReceivePack

func IsReceivePack(path, rawQuery string) bool

IsReceivePack reports whether path pushes to a git repository.

func ParseSize

func ParseSize(s string) (int64, error)

ParseSize reads "10MiB", "256KiB", "1GiB", "512B" or a plain number of bytes.

func Profiles

func Profiles(data []byte, name string) ([]string, error)

Profiles lists the profile names of a config file.

func PublishMethods

func PublishMethods(host string, methods []string) bool

PublishMethods reports whether methods (empty: all) include one that publishes on registry host: PUT, PATCH, DELETE anywhere, and any write on an upload host.

func UnderAnthropic

func UnderAnthropic(host string) bool

UnderAnthropic reports whether host is one of the Anthropic domains or a subdomain of one.

Types

type Inject added in v0.8.0

type Inject struct {
	ID string `json:"id"`
	// Container limits the injection to one container ("" for every one).
	Container string `json:"container,omitempty"`
	// Host is an exact host name (an internal host may be single-label).
	Host string `json:"host"`
	Port int    `json:"port"`
	// Header is the header's canonical name ("Authorization").
	Header string `json:"header"`
	// Scheme prefixes the value ("Bearer"), "" for the bare secret.
	Scheme string `json:"scheme,omitempty"`
	// Credential names the value in the gate's secrets file ("secret" is
	// a word config keys may not use).
	Credential string `json:"credential"`
	// Methods limits the injection to these methods (upper case, sorted;
	// empty: every method but TRACE and TRACK, in any case, which the gate
	// refuses).
	Methods []string `json:"methods,omitempty"`
	// Paths limits it to these paths, each exact or a prefix when it ends
	// with "*" (sorted; empty: every path). A path a target may resolve to
	// another one never matches (AmbiguousPath).
	Paths []string `json:"paths,omitempty"`
	Note  string   `json:"note,omitempty"`
}

Inject is one credential injection: the gate adds Header to the requests a container sends to Host:Port, after the verdict, with the value of the secret named Credential in the gate's secrets file (prefixed with Scheme and a space when Scheme is set). The policy names the secret, never its value: the value stays in the gate's container and never reaches the agent, the console or the hub.

func (Inject) Applies added in v0.8.0

func (i Inject) Applies(container, host string, port int) bool

Applies reports whether i covers a request of container to host:port.

func (Inject) MatchesRequest added in v0.8.0

func (i Inject) MatchesRequest(method, path string) bool

MatchesRequest reports whether i covers method and path (the escaped path, as rules match it). TRACE and TRACK, in any case, never match: they echo the request, the injected header included. With paths, a path a target may resolve to another one (see AmbiguousPath) never matches: "/api/../admin" is not under "/api/*" at the target.

func (Inject) String added in v0.8.0

func (i Inject) String() string

String is the one-line form of an injection (for the review): names only, never a value.

type InternalHost

type InternalHost struct {
	Host  string `json:"host"`
	Ports []int  `json:"ports"`
	// Read makes reads free; otherwise every request asks.
	Read bool `json:"read"`
}

InternalHost is a host of the user's internal-hosts list: it may resolve to a private address, on the ports it declares.

type Policy

type Policy struct {
	Name       string `json:"name"`
	Production bool   `json:"production"`
	Mode       string `json:"mode"`
	Heuristics string `json:"heuristics"`
	// BufferLimit is the largest body the gate holds for a human (bytes).
	BufferLimit int64 `json:"buffer_limit"`
	// Hold is how long the gate holds a request for the human (seconds).
	Hold int64 `json:"hold"`
	// ConnectGrant is how long an approved CONNECT opens its host for the
	// container (seconds).
	ConnectGrant int64 `json:"connect_grant"`
	IdleTimeout  int64 `json:"idle_timeout"`
	MaxSession   int64 `json:"max_session"`
	// Trusted hosts are free for reads (GET, HEAD, OPTIONS without body).
	Trusted []string `json:"trusted"`
	// Splice hosts are never decrypted: decided at the domain.
	Splice []string `json:"splice"`
	// Deny hosts are always refused.
	Deny []string `json:"deny"`
	// RedirectCDN lists "origin>cdn": a 3xx from origin to cdn lets the
	// same container GET the exact target once, within 60 s.
	RedirectCDN []string       `json:"redirect_cdn"`
	Internal    []InternalHost `json:"internal_hosts"`
	Rules       []Rule         `json:"rules"`
	// Inject lists the credential injections: headers the gate adds, from
	// its own secrets file, to the requests of one host and port. Omitted
	// when empty, so that a policy without injection keeps its
	// fingerprint.
	Inject []Inject `json:"inject,omitempty"`
}

Policy is one profile in canonical form: sorted lists, defaults applied, durations in seconds. Its JSON is what the console fingerprints and stores as the approved policy, and what the gate receives.

func Canonical

func Canonical(p Policy) Policy

Canonical sorts p's lists and removes duplicates.

func Parse

func Parse(data []byte, name, profile string) (Policy, error)

Parse decodes the config file data and returns profile in canonical form. Any unknown key, any key that looks like a secret, and any rule that would let auto mode answer a forbidden class is refused.

func (Policy) AutoForbidden

func (p Policy) AutoForbidden(r Rule) string

AutoForbidden returns why rule may not have verdict auto in p, or "". The classes are those that always need a human: production, all hosts, a spliced host, messaging, registry publishing, git-receive-pack. The runtime classes (an exfiltration score, a secret, an upload) are checked per request.

func (Policy) CDNFor

func (p Policy) CDNFor(origin, target string) bool

CDNFor reports whether a redirect from origin to target is listed.

func (Policy) Clone

func (p Policy) Clone() Policy

Clone returns a deep copy of p.

func (Policy) Denied

func (p Policy) Denied(host string) bool

Denied reports whether host is in the deny list.

func (Policy) Describe

func (p Policy) Describe() []string

Describe implements config.Policy.

func (Policy) Diff

func (p Policy) Diff(approved kconfig.Policy) []kconfig.Change

Diff implements config.Policy: what changed since approved, each change marked Loosens when it widens what passes without a human.

func (Policy) Fingerprint

func (p Policy) Fingerprint() string

Fingerprint is the canonical hash of p, as the console stores it.

func (Policy) InjectAt added in v0.8.0

func (p Policy) InjectAt(host string, port int) []Inject

InjectAt returns every injection to host:port, whatever its container, methods and paths: each response from that host and port may carry one of their credentials (stored by an injected request, served to another path or another container).

func (Policy) InjectFor added in v0.8.0

func (p Policy) InjectFor(container, host string, port int) []Inject

InjectFor returns the injections that apply to a request of container to host:port (any method and path), a container-scoped one before an all-containers one of the same header (only the first of each header applies).

func (Policy) InjectForRequest added in v0.8.0

func (p Policy) InjectForRequest(container, host string, port int, method, path string) []Inject

InjectForRequest is InjectFor for one method and path: an injection whose methods or paths do not name them does not apply (and does not hide an all-containers one).

func (Policy) InternalHost

func (p Policy) InternalHost(host string) (InternalHost, bool)

InternalHost returns the internal host entry of host.

func (Policy) Profile

func (p Policy) Profile() console.Profile

Profile implements console.Policy: the gate needs no secret.

func (Policy) Spliced

func (p Policy) Spliced(host string) bool

Spliced reports whether host is in the splice list.

func (Policy) Trusts

func (p Policy) Trusts(host string) bool

Trusts reports whether host is in the trusted list.

type Rule

type Rule struct {
	ID        string   `json:"id"`
	Container string   `json:"container,omitempty"`
	Host      string   `json:"host"`
	Methods   []string `json:"methods"`
	// Path is an exact path, or a prefix when it ends with "*"; "" is any.
	Path    string `json:"path,omitempty"`
	Verdict string `json:"verdict"`
	// MaxBody bounds the body of a matching request (bytes, 0 = the
	// buffer limit).
	MaxBody int64 `json:"max_body,omitempty"`
	// Expires is the last day (UTC) the rule applies, "" for never.
	Expires string `json:"expires,omitempty"`
	Note    string `json:"note,omitempty"`
}

Rule is one policy rule. It applies to every container or to one, never to an agent: an agent name is declarative.

func (Rule) Active

func (r Rule) Active(now time.Time) bool

Active reports whether r applies at now (its expiry day included, UTC).

func (Rule) MatchesMethod

func (r Rule) MatchesMethod(method string) bool

MatchesMethod reports whether method is covered by r.Methods (empty: every method).

func (Rule) MatchesPath

func (r Rule) MatchesPath(path string) bool

MatchesPath reports whether path is covered by r.Path.

func (Rule) String

func (r Rule) String() string

String is the one-line form of a rule (for the review and the status).

Jump to

Keyboard shortcuts

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