Help us shape the future of Go! Take the 2026 Go Developer Survey

gate

package
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Oct 30, 2026 License: Apache-2.0 Imports: 42 Imported by: 0

Documentation

Overview

Package gate is lockhttp's data plane: the HTTP(S) proxy every request of the agents goes through. It decides each request with the policy the console approved (it never reads a policy file), holds what needs a human, asks the console, and audits what it lets through or refuses.

Index

Constants

View Source
const AuditRetention = 30 * 24 * time.Hour

AuditRetention is how long the gate keeps its daily audit files.

View Source
const ContractVersion = 1

ContractVersion is the version of the files lea writes for the gate.

View Source
const RedirectTTL = 60 * time.Second

RedirectTTL is how long a listed CDN redirect target stays open.

View Source
const SingleUseTTL = 10 * time.Minute

SingleUseTTL is how long a single-use authorisation waits for its request.

Variables

View Source
var ErrNoConsole = errors.New("no lockhttp console is running")

ErrNoConsole means no console answers.

Functions

func BodyFingerprint

func BodyFingerprint(contentType string, body []byte) string

BodyFingerprint is the normalised fingerprint of a body: multipart parts without their boundary, canonical JSON without volatile fields, else the raw bytes.

func Preview

func Preview(contentType string, body []byte) string

Preview is the masked start of a body for the approval screen (decision D-body): textual bodies only, personal data and secrets masked, at most wire.MaxPreview bytes.

func PushRefs

func PushRefs(body []byte) []string

PushRefs reads the ref updates of a git-receive-pack request body (pkt-lines "old new ref\0caps" up to the flush packet).

func RequestFingerprint

func RequestFingerprint(container, method, url string, h http.Header, bodyFP string) string

RequestFingerprint binds an ask (and a single-use authorisation) to a request: container, method, URL, media type, every end-to-end header and the normalised body.

Types

type AuditLog

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

AuditLog is the gate's JSONL audit of every decision it applies: FREE, DENY, and the outcome of each ask (the console audits the human's side): one file per UTC day, gate-YYYY-MM-DD.jsonl, kept 30 days. With no directory it writes to w (stderr) or nowhere.

func NewAuditLog

func NewAuditLog(dir string, w io.Writer, now func() time.Time) (*AuditLog, error)

NewAuditLog opens dir (created 0700) or, when dir is "", writes to w.

func (*AuditLog) Append

func (l *AuditLog) Append(rec wire.Record)

Append writes one record.

func (*AuditLog) Close

func (l *AuditLog) Close()

Close closes the current file.

type CertSource

type CertSource interface {
	Certificate(ctx context.Context, host string) (*tls.Certificate, error)
}

CertSource issues the leaf certificate presented to a client for host (MITM). The CA key stays with the signer.

type Config

type Config struct {
	Profile string
	// Upstream is the proxy the gate sends through (squid on loopback);
	// nil dials the target itself, refusing private addresses.
	Upstream *url.URL
	Sources  *Sources
	Creds    *Credentials
	Console  Console
	Audit    *AuditLog
	// StatusFile is rewritten when the console state or the pending
	// counts change ("" for none).
	StatusFile string
	// Certs enables MITM; nil decides CONNECTs at the domain.
	Certs CertSource
	// UpstreamTLS verifies the targets (nil: the system roots). The gate
	// never skips verification.
	UpstreamTLS *tls.Config
	// Dial replaces the network dialer (tests).
	Dial func(ctx context.Context, network, addr string) (net.Conn, error)
	// Resolve replaces the resolver of direct mode (tests).
	Resolve func(ctx context.Context, host string) ([]netip.Addr, error)
	// Hub, when set, sends the asks to lockhub (hub mode); the console
	// still serves the policy.
	Hub *Hub
	// PolicyPoll is how often the policy is fetched (2 s by default).
	PolicyPoll time.Duration
	Now        func() time.Time
	// Log receives operational lines (not the audit).
	Log io.Writer
}

Config configures a gate.

type Console

type Console interface {
	Policy(ctx context.Context) (wire.PolicyResult, error)
	// Approve returns nil when approved; a *Refusal when the human (or the
	// timeout) refused; ErrNoConsole when no console answers.
	Approve(ctx context.Context, p wire.ApproveParams) (wire.ApproveResult, error)
}

Console is the gate's view of the console: the approved policy and the human's answers.

type Credentials

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

Credentials implements decide.Credentials from credentials.json; with no file the check is off. An unreadable file fails closed: every credential is unknown.

func NewCredentials

func NewCredentials(path string) *Credentials

NewCredentials loads path ("" for none).

func (*Credentials) Known

func (c *Credentials) Known(container, via, sum string) (bool, bool)

Known implements decide.Credentials.

func (*Credentials) Reload

func (c *Credentials) Reload()

Reload reads the file again when it changed.

type Gate

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

Gate is a running data plane.

func New

func New(cfg Config) *Gate

New returns a gate.

func (*Gate) Policy

func (g *Gate) Policy() (*policy.Policy, string)

Policy returns the policy in force (nil: none received yet) and its fingerprint.

func (*Gate) Run

func (g *Gate) Run(ctx context.Context)

Run polls the policy, runs the ask queue and reloads the source files until ctx ends.

func (*Gate) Serve

func (g *Gate) Serve(ctx context.Context, ln net.Listener) error

Serve accepts connections on ln until it is closed.

func (*Gate) SetPolicy

func (g *Gate) SetPolicy(p *policy.Policy, fp string)

SetPolicy installs a policy (tests and the poller).

func (*Gate) Stopped added in v0.3.0

func (g *Gate) Stopped() <-chan struct{}

Stopped is closed once Run's queue stopped and every ask it was answering ended (in hub mode, their tickets withdrawn).

type Grants

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

Grants holds what approvals open: connect grants (container × host × port, for the policy's connect_grant), single-use authorisations (request fingerprint, 10 min) and redirect exemptions (container × exact URL, 60 s, GET only).

func NewGrants

func NewGrants(now func() time.Time) *Grants

NewGrants returns empty grants.

func (*Grants) Connect

func (g *Grants) Connect(container, hostport string) bool

Connect reports a live connect grant.

func (*Grants) GrantConnect

func (g *Grants) GrantConnect(container, hostport string, d time.Duration)

GrantConnect opens container × host:port for d.

func (*Grants) GrantOnce

func (g *Grants) GrantOnce(fp string)

GrantOnce adds a single-use authorisation for fingerprint fp.

func (*Grants) GrantRedirect

func (g *Grants) GrantRedirect(container, url string)

GrantRedirect opens the exact URL for container's next GET.

func (*Grants) Sweep

func (g *Grants) Sweep()

Sweep drops expired entries.

func (*Grants) UseOnce

func (g *Grants) UseOnce(fp string) bool

UseOnce consumes the single-use authorisation of fp.

func (*Grants) UseRedirect

func (g *Grants) UseRedirect(container, url string) bool

UseRedirect consumes a redirect exemption.

type Hub added in v0.3.0

type Hub struct {
	Client *hubclient.Client
	Plans  *plans.Store
	// Hosts are the names of the hub on its network and Nets its
	// addresses: the gate refuses them as targets, whatever internal_hosts
	// allows, so that an agent never reaches the hub through the gate.
	Hosts []string
	Nets  []netip.Prefix
	// Poll is how often a held ask looks for its decision (1 s by
	// default); the decisions arrive by the client's long poll.
	Poll time.Duration
}

Hub sends the gate's asks to lockhub instead of the console: each held request becomes a plan and a signed ticket, and a decision that verifies under the pinned hub key grants the same single-use pass (or connect grant) a console approval does. The console still serves the policy.

func (*Hub) Close added in v0.3.0

func (h *Hub) Close() error

Close closes the plan store (it holds its directory for this process): after the gate stopped (Gate.Stopped), never while asks wait.

type LockkitConsole

type LockkitConsole struct {
	Profile string
	// Cwd is where the client looks for a project (none: "/").
	Cwd string
	// contains filtered or unexported fields
}

LockkitConsole dials the console with lockkit's client: one connection for the policy poll, one for approvals (the console is serial: a poll queued behind an approval simply waits).

func (*LockkitConsole) Approve

Approve implements Console.

func (*LockkitConsole) Policy

Policy implements Console.

type Outcome

type Outcome struct {
	Approved bool
	Auto     bool
	Code     string
	Reason   string
}

Outcome is the answer to an ask.

type Queue

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

Queue holds the asks and sends them to the console one at a time (lockkit's console is serial, decision D7). A human decision is shared only by requests of identical fingerprint; requests under the same key with another fingerprint are separate items.

func NewQueue

func NewQueue(c Console) *Queue

NewQueue returns a queue sending to c.

func (*Queue) Pending

func (q *Queue) Pending() int

Pending is the number of queued items.

func (*Queue) Run

func (q *Queue) Run(ctx context.Context)

Run sends the queued items until ctx ends.

func (*Queue) Submit

func (q *Queue) Submit(fp, key string, p wire.ApproveParams, onApprove func(clientGone bool)) (<-chan Outcome, func())

Submit adds a waiter for the request of fingerprint fp. It returns the channel the outcome arrives on and a cancel func the waiter calls when it stops waiting (the item stays queued).

type Refusal

type Refusal struct {
	Code   string
	Reason string
}

Refusal is a console's refusal of an approval.

func (*Refusal) Error

func (r *Refusal) Error() string

type Sources

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

Sources resolves who sent a connection. With no file it runs in standalone mode: every connection is container "local".

func NewSources

func NewSources(path string) *Sources

NewSources loads path ("" for standalone mode).

func (*Sources) Err

func (s *Sources) Err() error

Err is the load error, if any.

func (*Sources) Reload

func (s *Sources) Reload()

Reload reads the file again when it changed. A file that cannot be read or parsed fails closed: every lookup is refused until it is fixed.

func (*Sources) Resolve

func (s *Sources) Resolve(local netip.Addr, user, token, leaSource string) (wire.Source, error)

Resolve names the source of a request that arrived on local address local, with the agent and token of its Proxy-Authorization (if any) and the X-Lea-Source header (keyproxy only).

type Status

type Status struct {
	Version            int            `json:"version"`
	Console            string         `json:"console"`
	Pending            int            `json:"pending"`
	PendingByContainer map[string]int `json:"pending_by_container"`
	PolicyFP           string         `json:"policy_fp,omitempty"`
	Updated            string         `json:"updated"`
}

Status is the status file lea reads (docs/lea.md §5).

Jump to

Keyboard shortcuts

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