logconsole

package
v0.2.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: 33 Imported by: 0

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

View Source
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.

View Source
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.

View Source
const EventCall = "log.call"

Audit events of locklog's calls.

View Source
const SystemFile = "/etc/locklog/system.toml"

SystemFile is the separated setup's file in locklog's image.

Variables

View Source
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.

View Source
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.

Methods lists the methods served, in a fixed order.

Functions

func CheckMounts

func CheckMounts(o Options) error

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

func InImage(uid int, stat func(string) (os.FileInfo, error)) bool

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

func OperatorLogFrom(ctx context.Context) func(string)

OperatorLogFrom returns the call's operator log, or nil.

func Sanitizer

func Sanitizer() *sanitize.Sanitizer

Sanitizer scrubs locklog's error text: URL userinfo and Authorization headers, after the secrets lockkit knows.

func ShowResultsFrom

func ShowResultsFrom(ctx context.Context) bool

ShowResultsFrom reports the clear-results switch of the call.

func WithApprove

func WithApprove(ctx context.Context, a Approve) context.Context

WithApprove attaches the call's approver to ctx.

func WithExposure added in v0.2.0

func WithExposure(ctx context.Context, x *Exposure) context.Context

WithExposure attaches the console's exposure count to ctx.

func WithHubCall added in v0.2.0

func WithHubCall(ctx context.Context, h *HubCall) context.Context

WithHubCall attaches a hub call to ctx.

func WithOperatorLog added in v0.2.0

func WithOperatorLog(ctx context.Context, l func(line string)) context.Context

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.

func WithShowResults

func WithShowResults(ctx context.Context, on bool) context.Context

WithShowResults attaches the console's clear-results switch (Ctrl-O) to ctx: an approval screen then shows its record in clear, on the console, and the engine keeps the call's records for Reveal.

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

type Approve func(ctx context.Context, s console.Screen) console.Approval

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

func ApproveFrom(ctx context.Context) Approve

ApproveFrom returns the call's approver, or nil.

type Backend

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

Backend implements console.Backend.

func New

func New(o Options) *Backend

New returns the backend of o.Profile.

func (*Backend) AuditRecord added in v0.2.0

func (b *Backend) AuditRecord(rec audit.Base) audit.Record

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

func (b *Backend) CheckMask(pattern string) (string, error)

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

func (b *Backend) Checks(ctx context.Context, s console.Session) []console.Finding

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

func (b *Backend) LoadPolicy() (console.Policy, error)

LoadPolicy implements console.Backend: the profile from the project's .locklog/config.toml with the launch environment.

func (*Backend) Methods

func (b *Backend) Methods() map[string]console.Handler

Methods implements console.Backend.

func (*Backend) OnHub added in v0.2.0

func (b *Backend) OnHub(h *console.Hub)

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

func (b *Backend) PIIRules(p console.Policy) (mask, allow []string)

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

func (b *Backend) SaveMask(pattern string) error

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

func (b *Backend) Status(s console.Session) any

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

func (b *Backend) WithMask(p console.Policy, _ string) console.Policy

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 Caller

type Caller struct{ UID, PID int }

Caller is the kernel-verified peer of a call.

type ClearView

type ClearView struct {
	On   bool
	Kept any
}

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

func ClearViewFrom(ctx context.Context) *ClearView

ClearViewFrom returns the call's clear-view state, or nil.

type Declined

type Declined struct {
	Resp ipc.Response
}

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.

func (*Declined) Error

func (d *Declined) Error() string

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

type ExposedRecord struct {
	Key        string
	Pseudonyms []string
}

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

func ExposureFrom(ctx context.Context) *Exposure

ExposureFrom returns the console's exposure count, nil when none.

func NewExposure added in v0.2.0

func NewExposure(since time.Time) *Exposure

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).

func (*Exposure) Count added in v0.2.0

func (x *Exposure) Count(epoch string, pseudonyms []string) ([]int, bool, time.Time)

Count returns, for each pseudonym, the distinct records holding it returned to agents in epoch, whether the counts are exact (false: lower bounds) and since when the console counts.

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

func HubCallFrom(ctx context.Context) *HubCall

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.

func (*HubCall) TokenHash added in v0.2.0

func (h *HubCall) TokenHash() string

TokenHash is the hash of the agent's token the call's plans are bound to (plans.Binding.TokenHash): what binds an approved scan to the agent too (engine.Peer.Token).

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

type HubStatus struct {
	Mode     string `json:"mode"`
	Instance string `json:"instance"`
}

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.

func FromEnv

func FromEnv(o Options, getenv func(string) string) Options

FromEnv reads the launcher's environment into o. It does not decide InContainer: that is InImage, independent of the variables it checks.

type PIIRecord added in v0.2.0

type PIIRecord struct {
	audit.Base
	Method  string `json:"method"`
	Pattern string `json:"pattern"`
}

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

type Policy struct {
	config.Policy
}

Policy is a locklog policy as the console handles it.

func (Policy) Diff

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

Diff implements config.Policy.

func (Policy) Profile

func (p Policy) Profile() console.Profile

Profile implements console.Policy: the secret the console asks is the one of the profile's remote source (one at most), none for local sources.

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.

func (Record) AuditEvent

func (r Record) AuditEvent() string

AuditEvent implements audit.Record.

type Refusal

type Refusal struct {
	Message string
	Info    refusal.Info
}

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

type Revealer interface {
	Reveal(ctx context.Context, result any) []string
}

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

func PlanRoots(o Options, pr config.Profile) (Roots, error)

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.

func (Roots) All

func (r Roots) All() []string

All returns every root, sorted.

Jump to

Keyboard shortcuts

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