Documentation
¶
Overview ¶
Package logconsole is locklog's console backend for lockkit: the agent's log methods (log.status, log.sources, log.search, log.count, ...), the checks the console makes before it opens a log (the mount roots and the .locklog directory the launcher checked, no network it was not given), the policy of a profile, and the audit record of every call. lockkit's console holds the terminal, the policy review, the approval screens and the audit log; the engine (Engine) reads and masks the logs.
Index ¶
- Constants
- Variables
- func CheckMounts(o Options) error
- func InImage(uid int, stat func(string) (os.FileInfo, error)) bool
- func OperatorLogFrom(ctx context.Context) func(string)
- func Sanitizer() *sanitize.Sanitizer
- func ShowResultsFrom(ctx context.Context) bool
- func WithApprove(ctx context.Context, a Approve) context.Context
- func WithExposure(ctx context.Context, x *Exposure) context.Context
- func WithHubCall(ctx context.Context, h *HubCall) context.Context
- func WithOperatorLog(ctx context.Context, l func(line string)) context.Context
- func WithShowResults(ctx context.Context, on bool) context.Context
- type AgentRuler
- type Approve
- type Backend
- func (b *Backend) AuditRecord(rec audit.Base) audit.Record
- func (b *Backend) CheckMask(pattern string) (string, error)
- func (b *Backend) Checks(ctx context.Context, s console.Session) []console.Finding
- func (b *Backend) Connect(ctx context.Context, c console.Credentials) (console.Session, error)
- func (b *Backend) LoadPolicy() (console.Policy, error)
- func (b *Backend) Methods() map[string]console.Handler
- func (b *Backend) OnHub(h *console.Hub)
- func (b *Backend) PIIRules(p console.Policy) (mask, allow []string)
- func (b *Backend) SaveMask(pattern string) error
- func (b *Backend) Status(s console.Session) any
- func (b *Backend) WithMask(p console.Policy, _ string) console.Policy
- type Caller
- type ClearView
- type Declined
- type Engine
- type ExposedRecord
- type Exposer
- type Exposure
- type HubCall
- type HubRequest
- type HubStatus
- type OpenOptions
- type Opener
- type Options
- type PIIRecord
- type Policy
- type Record
- type Refusal
- type Revealer
- type Roots
Constants ¶
const ( MethodStatus = "log.status" MethodSources = "log.sources" MethodSearch = "log.search" MethodCount = "log.count" // MethodPatterns is the top message templates of a range, optionally // against a baseline window (locklog_patterns). MethodPatterns = "log.patterns" // MethodContext, MethodTrace and MethodTail read around a ref, follow // an id across sources and follow new records (locklog_context, // locklog_trace, locklog_tail). MethodContext = "log.context" MethodTrace = "log.trace" MethodTail = "log.tail" // MethodScan is a call over the free budgets, approved once (class // read) for its whole plan (locklog_scan); MethodUnmask reveals exact // pseudonyms of one record on a read-pii approval (locklog_unmask); // MethodLookup gives the pseudonyms of clear values, rate-limited // (locklog_lookup). Policy changes go through the console's own // change.request (locklog_request_change). MethodScan = "log.scan" MethodUnmask = "log.unmask" MethodLookup = "log.lookup" )
The agent's methods. Each is free (no approval) within the budgets the engine enforces, and audited.
const CodeAutoDeny refusal.Code = "E_AUTO_DENY"
CodeAutoDeny: the profile's auto rules deny the action (auto.<action> = "deny"), even with a human's approval.
const EventCall = "log.call"
Audit events of locklog's calls.
const SystemFile = "/etc/locklog/system.toml"
SystemFile is the separated setup's file in locklog's image.
Variables ¶
var ErrConnLost = errors.New("the log session is lost")
ErrConnLost is returned (wrapped) by an engine whose logs can no longer be trusted to be the ones approved (a mount swapped, the engine closed): the console drops the session (console.Env.Lost) and the agent gets ipc.CodeConnLost; the next call reconnects, checking everything again.
var JournalDirs = []string{"/var/log/journal", "/run/log/journal"}
JournalDirs are the journal directories the launcher binds for a journald source, when they exist on the host.
var Methods = []string{MethodStatus, MethodSources, MethodSearch, MethodCount, MethodPatterns, MethodContext, MethodTrace, MethodTail, MethodScan, MethodUnmask, MethodLookup}
Methods lists the methods served, in a fixed order.
Functions ¶
func CheckMounts ¶
checkMounts compares, in the launcher's container, the device and inode of .locklog and of every mount root with what the launcher checked on the host: a directory swapped (for a link, another directory) between the check and docker run is refused before anything is read.
CheckMounts is exported so that the console command runs it before it reads .locklog at all; LoadPolicy and Connect run it again.
func InImage ¶
InImage reports whether this process runs in locklog's image: as the console account, or with the image's system file present. The console runs only in the container `lox run` starts, which always passes LOCKLOG_EXPECT_DIR: a host holding the system file but started without the launcher is refused, by design.
func OperatorLogFrom ¶ added in v0.2.0
OperatorLogFrom returns the call's operator log, or nil.
func Sanitizer ¶
Sanitizer scrubs locklog's error text: URL userinfo and Authorization headers, after the secrets lockkit knows.
func ShowResultsFrom ¶
ShowResultsFrom reports the clear-results switch of the call.
func WithApprove ¶
WithApprove attaches the call's approver to ctx.
func WithExposure ¶ added in v0.2.0
WithExposure attaches the console's exposure count to ctx.
func WithHubCall ¶ added in v0.2.0
WithHubCall attaches a hub call to ctx.
func WithOperatorLog ¶ added in v0.2.0
WithOperatorLog attaches the console's operator log to ctx: the engine prints a line there (a source's failure) for the human at the console's terminal only, never for the agent nor the audit.
Types ¶
type AgentRuler ¶ added in v0.2.0
type AgentRuler interface {
// CheckAgentRule checks a pii.add pattern against what the engine
// showed and returns the rule's canonical form: its keyed digest and
// mode, never a field name (a name shown masked must never reach the
// console's audit, terminal or approved policy). A refusal's text
// quotes nothing of the pattern.
CheckAgentRule(pattern string) (canonical string, refused *Refusal)
// AddAgentRule stores a rule CheckAgentRule returned, in the
// console's PII directory only (never in .locklog/pii.toml), checking
// the caps again.
AddAgentRule(canonical string) (refused *Refusal, err error)
// PIIRules are the rules in force, as the agent may see them: the
// human's mask rules, the agent's (by mode and time, never by field)
// and the approved allow rules.
PIIRules() (mask, allow []string)
}
AgentRuler is implemented by an engine that holds the agent's masking rules: the console's pii.list and pii.add reach them through the backend (console.PIIBackend).
type Approve ¶
Approve asks the human to approve a call's operation on the console's approval screen (console.Env.Approve for the call's request).
func ApproveFrom ¶
ApproveFrom returns the call's approver, or nil.
type Backend ¶
type Backend struct {
// contains filtered or unexported fields
}
Backend implements console.Backend.
func (*Backend) AuditRecord ¶ added in v0.2.0
AuditRecord is the console's hook on its own records: the policy record of a pii.add (tightened, or unsaved or failed) gets the method and the canonical pattern. Every other record is left as it is.
func (*Backend) CheckMask ¶ added in v0.2.0
CheckMask implements console.PIIBackend. The canonical form it returns is what the console prints, saves and audits for the rule: "agent <mode> <keyed digest>", never a name. A refusal is audited here, as the console audits only the rules it adds.
func (*Backend) Checks ¶
Checks implements console.Backend: a console with no remote source and no LOCKLOG_NETWORK runs with no network; any interface but the loopback means it was started otherwise, and it refuses.
func (*Backend) Connect ¶
Connect implements console.Backend: the launcher's checks first, then the engine of the policy in force.
func (*Backend) LoadPolicy ¶
LoadPolicy implements console.Backend: the profile from the project's .locklog/config.toml with the launch environment.
func (*Backend) OnHub ¶ added in v0.2.0
OnHub is console.Options.OnHub: the console's hub, before any request, gets the recompute of a renewal, and locklog_status shows hub mode.
func (*Backend) PIIRules ¶ added in v0.2.0
PIIRules implements console.PIIBackend: the rules of the engine in force, or, with no session open, the approved allow rules of p.
func (*Backend) SaveMask ¶ added in v0.2.0
SaveMask implements console.PIIBackend: the rule goes to the console's PII directory, applied at once by the engine; .locklog/pii.toml is never written.
func (*Backend) Status ¶
Status implements console.Backend: the engine's status, and in hub mode the instance the console is known by.
func (*Backend) WithMask ¶ added in v0.2.0
WithMask implements console.PIIBackend: p itself. An agent rule is not part of the policy: it lives in the console's PII directory, is applied at once by the engine and is counted in the status, so the policy's fingerprint (and the open session) stay as they are.
type ClearView ¶
ClearView is a call's clear-view state: the console's clear-results switch (Ctrl-O) and what the engine keeps for it (its own records).
func ClearViewFrom ¶
ClearViewFrom returns the call's clear-view state, or nil.
type Declined ¶
Declined is an engine's error for a call the human did not approve: Resp is the console's answer (a denial, a timeout, a pending ticket), sent to the agent as is.
type Engine ¶
type Engine interface {
Handle(ctx context.Context, method string, params json.RawMessage, c Caller) (result any, refused *Refusal, err error)
// Status is the engine's part of log.status and :status.
Status(ctx context.Context) any
Close() error
}
Engine answers the agent's log methods. params is the request's raw JSON, validated by the engine; result is a masked, typed value that marshals to JSON. A refusal is the agent's error (over budget, a personal value in a predicate, ...); err is the engine's own failure.
type ExposedRecord ¶ added in v0.2.0
ExposedRecord is one record a call returned to the agent: a key of its own (a prefix of its keyed digest, never its text) and the pseudonyms it held.
type Exposer ¶ added in v0.2.0
type Exposer interface {
Exposed(result any) (epoch string, recs []ExposedRecord)
}
Exposer is an engine that names the records of a call's result (ExposedRecord) and the pseudonym epoch they are of.
type Exposure ¶ added in v0.2.0
type Exposure struct {
// contains filtered or unexported fields
}
Exposure counts, for the epoch in force, how many distinct records holding each pseudonym were returned to agents: fed by the calls the console answered and audited, by pseudonym and record key only (never a clear value nor a record's text). The unmask screen shows it. It is the console's own count, kept in memory: a console started during the epoch counts from its start (Since).
func ExposureFrom ¶ added in v0.2.0
ExposureFrom returns the console's exposure count, nil when none.
func NewExposure ¶ added in v0.2.0
NewExposure returns an empty count, started at since.
func (*Exposure) Add ¶ added in v0.2.0
func (x *Exposure) Add(epoch string, recs []ExposedRecord)
Add counts the records a call returned in epoch. A new epoch starts the count again (pseudonyms do not link across epochs).
type HubCall ¶ added in v0.2.0
type HubCall struct {
// contains filtered or unexported fields
}
HubCall is one call of an approved method in hub mode: a new request (no ticket) or the run of a ticket.
func HubCallFrom ¶ added in v0.2.0
HubCallFrom returns the call's hub call, nil for a free method.
func (*HubCall) Approve ¶ added in v0.2.0
func (h *HubCall) Approve(ctx context.Context, r HubRequest) error
Approve decides r on the hub: a new request is submitted (pending), the run of a ticket begins when its decision verifies and binds this plan. It returns nil once the run may proceed, or a *Declined with the agent's answer.
type HubRequest ¶ added in v0.2.0
type HubRequest struct {
Risk ticket.Risk
Header string
// Note is the agent's reason, masked.
Note string
// Body is the console's screen (masked by the engine), HubBody the
// hub's when it differs (nil: Body).
Body, HubBody []string
Warnings []string
// Clear is the clear view of the screen's record (secrets redacted):
// sealed to the pinned devices only, in the clear screen.
Clear []string
// PlanHash is the engine's keyed plan hash, Inputs its keyed input
// digests (base64url HMAC-SHA256), Moving the inputs a renewal cannot
// check without the record.
PlanHash string
Inputs map[string]string
Moving []string
// Production flags the ticket. Whether it is never-auto is the
// profile's auto rule's (HubCall), not the request's.
Production bool
}
HubRequest is an approval of the engine as the hub gets it.
type HubStatus ¶ added in v0.2.0
HubStatus is the hub mode of the console in log.status: the instance only (never the hub's key, URL or a ticket's binding).
type OpenOptions ¶
type OpenOptions struct {
// Policy is the approved policy in force: the engine reads only the
// sources it names.
Policy config.Policy
// Credentials are the secret of the profile's remote source (zero for
// local sources).
Credentials egress.Credentials
// StateDir is the console's state directory (home volume).
StateDir string
// Roots are the mount roots of the policy's local sources, checked
// against the launcher's (PlanRoots): the only directories the
// sources may be given.
Roots Roots
// Project is the project root ("" for none): its .locklog holds the
// human's PII rules.
Project string
}
OpenOptions is what an engine is opened with.
type Opener ¶
type Opener func(ctx context.Context, o OpenOptions) (Engine, error)
Opener opens the engine of a policy.
type Options ¶
type Options struct {
Profile string
// Root is the project root ("" for none).
Root string
// StateDir is the console's state base.
StateDir string
// Network and ExtraRoots are LOCKLOG_NETWORK and LOCKLOG_EXTRA_ROOTS
// as the launcher passed them: they are part of the policy.
Network string
ExtraRoots []string
// ExpectDir and ExpectRoots are LOCKLOG_EXPECT_DIR and
// LOCKLOG_EXPECT_ROOTS: the device and inode of .locklog and of each
// mount root, as the launcher checked them on the host.
ExpectDir string
ExpectRoots string
// DockerRoot is LOCKLOG_DOCKER_ROOT: the containers root the launcher
// bound for a docker source.
DockerRoot string
// InContainer is true in locklog's image (InImage): LOCKLOG_EXPECT_DIR
// is then required, and Root must be the directory the launcher
// started the console in.
InContainer bool
// SysClassNet is /sys/class/net (tests replace it).
SysClassNet string
Open Opener
// Now is the clock of hub tickets: the console's (console.Options.Now;
// nil is time.Now).
Now func() time.Time
}
Options locate the profile a console serves and the launcher's checks.
type PIIRecord ¶ added in v0.2.0
PIIRecord is the console's record of a pii.add it applied, with the rule's canonical form (a keyed digest, never a name).
type Policy ¶
Policy is a locklog policy as the console handles it.
type Record ¶
type Record struct {
audit.Base
Method string `json:"method"`
Sources []string `json:"sources,omitempty"`
From string `json:"from,omitempty"`
To string `json:"to,omitempty"`
// Last is a relative range (the last duration up to the call), and
// Baseline a patterns call's offset: it also reads the same window
// that much earlier. Both in Go's canonical form ("2h0m0s"), parsed
// from the call, never the agent's text.
Last string `json:"last,omitempty"`
Baseline string `json:"baseline,omitempty"`
// Tool is the tool an approved scan runs (log.scan).
Tool string `json:"tool,omitempty"`
// Pseudonyms name what an unmask revealed or a lookup resolved, by
// pseudonym only (never the clear values).
Pseudonyms []string `json:"pseudonyms,omitempty"`
// ShownClear counts the records shown in clear on the console's
// terminal (results on, Ctrl-O), never to the agent.
ShownClear int `json:"shown_clear,omitempty"`
// AutoRule is the profile's auto rule for the call's action
// ("auto.read = ask", #126): what made its ticket asked or left to
// lox run's auto mode, or what refused a denied call. DecidedBy is
// what decided its ticket, from the hub's version-2 decision:
// "human" or "auto:<rule id>".
AutoRule string `json:"auto_rule,omitempty"`
DecidedBy string `json:"decided_by,omitempty"`
}
Record is the audit record of one agent call: the method, the sources and the range it named, and the outcome. It never holds a predicate, a value or a log line: those are the agent's and the logs' text, which the detectors may miss.
type Refusal ¶
Refusal is an answer the agent gets as a structured refusal: Message is prose that quotes no value of the agent's call nor of the logs; Info carries the code (E_OVER_BUDGET, E_PII_IN_QUERY, ...), retry and hint.
type Revealer ¶
Revealer is implemented by an engine that can show the records of a result in clear on the console's terminal (the clear view, Ctrl-O): one line per record, secrets and redactions kept. The lines are never part of an answer to the agent.
type Roots ¶
type Roots struct {
// Dirs are the directories of the file sources' globs
// (mountplan.Dir), sorted.
Dirs []string
// Journal are the journal directories bound (JournalDirs that
// exist on the host).
Journal []string
// Docker is the containers root (LOCKLOG_DOCKER_ROOT), "" without a
// docker source.
Docker string
// DockerChecked is set when the launcher could stat the containers
// root from the host and passed its device:inode; usually it cannot
// (/var/lib/docker is 0710 root), and the root is then the launcher's
// "not checked from the host" case: the console pins its device:inode
// at its first connection and checks it on every call (Backend).
DockerChecked bool
}
Roots are the mount roots of a policy's local sources, each one the launcher bound and checked (LOCKLOG_EXPECT_ROOTS): the only directories the sources are given.
func PlanRoots ¶
PlanRoots derives the mount plan of pr (mountplan.Lines) and returns its roots. In the launcher's container (o.InContainer) every planned directory must be one the launcher checked, after CheckMounts passed: a file glob's directory and each journal directory an entry of LOCKLOG_EXPECT_ROOTS, the containers root LOCKLOG_DOCKER_ROOT either such an entry or the launcher's "not checked from the host" case (a clean absolute path ending in /containers, a directory and not a link). A planned root the launcher did not check refuses the policy: the console was not started by the launcher with this plan. Outside the image (a developer's build) nothing was checked, and the planned directories are the roots.