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
- Variables
- func BodyFingerprint(contentType string, body []byte) string
- func Preview(contentType string, body []byte) string
- func PushRefs(body []byte) []string
- func RequestFingerprint(container, method, url string, h http.Header, bodyFP string) string
- type AuditLog
- type CertSource
- type Config
- type Console
- type Credentials
- type Gate
- type Grants
- func (g *Grants) Connect(container, hostport string) bool
- func (g *Grants) GrantConnect(container, hostport string, d time.Duration)
- func (g *Grants) GrantOnce(fp string)
- func (g *Grants) GrantRedirect(container, url string)
- func (g *Grants) Sweep()
- func (g *Grants) UseOnce(fp string) bool
- func (g *Grants) UseRedirect(container, url string) bool
- type Hub
- type LockkitConsole
- type Outcome
- type Queue
- type Refusal
- type Sources
- type Status
Constants ¶
const AuditRetention = 30 * 24 * time.Hour
AuditRetention is how long the gate keeps its daily audit files.
const ContractVersion = 1
ContractVersion is the version of the files lea writes for the gate.
const RedirectTTL = 60 * time.Second
RedirectTTL is how long a listed CDN redirect target stays open.
const SingleUseTTL = 10 * time.Minute
SingleUseTTL is how long a single-use authorisation waits for its request.
Variables ¶
var ErrNoConsole = errors.New("no lockhttp console is running")
ErrNoConsole means no console answers.
Functions ¶
func BodyFingerprint ¶
BodyFingerprint is the normalised fingerprint of a body: multipart parts without their boundary, canonical JSON without volatile fields, else the raw bytes.
func Preview ¶
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.
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 ¶
NewAuditLog opens dir (created 0700) or, when dir is "", writes to w.
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 (*Gate) Policy ¶
Policy returns the policy in force (nil: none received yet) and its fingerprint.
func (*Gate) Run ¶
Run polls the policy, runs the ask queue and reloads the source files until ctx ends.
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 (*Grants) GrantConnect ¶
GrantConnect opens container × host:port for d.
func (*Grants) GrantRedirect ¶
GrantRedirect opens the exact URL for container's next GET.
func (*Grants) UseRedirect ¶
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.
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 ¶
func (c *LockkitConsole) Approve(ctx context.Context, p wire.ApproveParams) (wire.ApproveResult, error)
Approve implements Console.
func (*LockkitConsole) Policy ¶
func (c *LockkitConsole) Policy(ctx context.Context) (wire.PolicyResult, error)
Policy implements Console.
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 (*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 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 ¶
NewSources loads path ("" for standalone mode).
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.
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).