Documentation
¶
Overview ¶
Package audit writes the console's JSONL audit trail: one record per event, never secrets and never row data. The file is opened O_APPEND, mode 0600 in a 0700 directory of the console account's state: the agent account cannot read or alter it, while the console account and root can. Each record carries prev_hash, the hash of the line before it, so that Verify detects a record edited, removed, inserted or reordered; records written before the chain stay readable (see Verify). On Linux, chattr +a makes the file append-only for the console account too.
Index ¶
Constants ¶
const ( EventLogin = "login" EventPolicy = "policy" // policy change, applied or refused (see Decision) EventRefused = "refused" // request refused by the policy EventApproved = "approved" // the human approved EventDenied = "denied" // the human denied EventTimeout = "timeout" // approval timed out EventAbandoned = "abandoned" // the client left while its approval was pending EventAuto = "auto" // auto-approved under auto mode (older logs: the console has no auto mode now) // EventPending is a request handed to the hub (Ticket set): decided // there, later. It is written before the requester is told pending: // a request whose pending record fails is refused, and its ticket // withdrawn. EventPending = "pending" // EventRun is the outcome of an approved or auto-approved request // (Decision ok or failed). It follows the approval record, which is // written before anything runs. EventRun = "run" EventCatalog = "catalog" // catalog read // EventMode, EventResults and EventUnmask are the console's switches, // changed from its keyboard: Decision is the new state (shown or // hidden; allowed or off). EventMode (manual or auto) is in older // logs only: the console has no auto mode now. EventMode = "mode" EventResults = "results" EventUnmask = "unmask" EventLogout = "logout" // EventReveal is a hub request to show a ticket's clear screen on a // local facade (Ctrl-O): Decision sealed or refused, with the ticket, // the approver and the facade. EventRenew is a request to ask again // for an expired ticket (Decision renewed or refused). EventReveal = "reveal" EventRenew = "renew" // EventHubRequest is a hub request refused before its kind is known // (it did not verify). EventHubRequest = "hub-request" )
Events of the console.
const Genesis = "genesis"
Genesis is the prev_hash of the first record of an empty log.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type Anchor ¶ added in v0.3.0
Anchor is a chained line of a log, kept apart from it (by the hub): the 1-based Line and its LineHash. Summary.Anchor makes one.
type Base ¶
type Base struct {
Event string `json:"event"`
Profile string `json:"profile,omitempty"`
Decision string `json:"decision,omitempty"`
Error string `json:"error,omitempty"`
// Code is the refusal code of a refused record (package refusal), and
// Span where the refusal points in the request, when known.
Code string `json:"code,omitempty"`
Span *refusal.Span `json:"span,omitempty"`
Warnings []string `json:"warnings,omitempty"`
// Intent is the agent's one-line reason for a request (untrusted
// text), kept on every record of that request.
Intent string `json:"intent,omitempty"`
// Reason is the human's reason for a denial, escaped.
Reason string `json:"reason,omitempty"`
// Approval is how the human approved: "session" (typed in a login
// session of the console account), "desktop+typed" (typed in a desktop
// window, level 1) or "desktop+fingerprint" (level 2).
Approval string `json:"approval,omitempty"`
// The client behind a request, from the kernel's peer credentials:
// set on the records of client requests only. PeerUID is nil when
// unknown (uid 0 is root); PeerUser is the account name when it
// resolves. The process name is the agent's choice and is not kept.
PeerUID *int `json:"peer_uid,omitempty"`
PeerPID int `json:"peer_pid,omitempty"`
PeerUser string `json:"peer_user,omitempty"`
// The hub's decision behind a record, in hub mode only: the ticket
// ID, who answered it and on which facade (from the signed decision),
// and the hub's signature of that decision, so that the record can be
// checked against the hub's key later.
Ticket string `json:"ticket,omitempty"`
Approver *ticket.Approver `json:"approver,omitempty"`
Facade string `json:"facade,omitempty"`
HubSig string `json:"hub_sig,omitempty"`
}
Base is the product-neutral record: a product with no fields of its own writes it as is; another declares its own struct (with the same JSON names for these fields) and implements Record.
type ChainError ¶ added in v0.3.0
ChainError is a break of the chain, at a 1-based line of the log.
func (*ChainError) Error ¶ added in v0.3.0
func (e *ChainError) Error() string
type Log ¶
type Log struct {
// contains filtered or unexported fields
}
Log appends records to <stateDir>/<product>/audit.log. The file is opened in append mode for each record, so it is never truncated and never held open. Logs of one file in one process share a lock, and every append holds a lock on the file itself, so that each record links to the line written just before it, whatever process writes it.
func Open ¶
Open prepares the audit log of p under stateDir (kit.StateBase() in production): the directory <stateDir>/<p.Name> is 0700 and the file 0600 (Unix), created if missing; existing content is kept.
func (*Log) Append ¶
Append writes rec as one JSON line: {"ts": the current time in RFC 3339 with milliseconds, then rec's fields in their encoding order, then "prev_hash": the LineHash of the file's last line (Genesis in an empty file)}. The chain comes last, so that a product's line keeps its format before the chain as a prefix; a record may not name prev_hash itself. A last line cut short is left as it is and the record starts a line of its own. An error means nothing was written, or the write failed: the caller must not treat the event as audited.
type Record ¶
type Record interface {
AuditEvent() string
}
Record is one audit event of a product. Its JSON encoding must be an object whose fields hold no password and no row or message data: Error and every free text must already be sanitised by the caller. AuditEvent is the record's event name, never empty.
type Summary ¶ added in v0.3.0
type Summary struct {
// Records is the number of lines, Legacy those written before the
// chain (without prev_hash), at the start of the log.
Records, Legacy int
// Last is the LineHash of the last chained line, "" when none: an
// anchor kept elsewhere (the hub's own log) detects a cut tail.
Last string
}
Summary is what Verify read.
func Verify ¶ added in v0.3.0
Verify reads an audit log and checks its chain: every line is a JSON object; lines without prev_hash may only precede the first chained one (the format before the chain); from there, each prev_hash is the LineHash of the line before it, and Genesis only on the first line. A record edited, removed, inserted or reordered breaks the chain. Records cut from the end, or a chain stripped of every prev_hash, leave a log that verifies: VerifyAnchored checks it against an anchor kept elsewhere. It returns a *ChainError for a break.
func VerifyAnchored ¶ added in v0.3.0
VerifyAnchored is Verify for a log anchored at a: the log reaches line a.Line, that line is chained and hashes to a.Hash. A log cut before the anchor, or whose chain was stripped (lines without prev_hash up to the anchor: a log that Verify alone reads as legacy) is a *ChainError.