usage

package
v1.1.0 Latest Latest
Warning

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

Go to latest
Published: May 16, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

Documentation

Overview

Package usage builds and queries an in-memory index of "what RBAC verbs has each subject actually exercised within an observation window?" It is the data layer behind the least-privilege analyzer: granted permissions come from the snapshot (point-in-time), observed permissions come from the kube-apiserver audit log (history), and the analyzer diffs them to surface narrowing opportunities.

The index never persists. Audit logs are large, snapshots are small and shareable; the loader streams the log once at scan time, builds the index, and the analyzer queries it before it is dropped at process exit. The window metadata travels on each finding so the HTML report can show reviewers what data fed the verdict.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func SubjectFromUsername

func SubjectFromUsername(username string) (models.SubjectRef, bool)

SubjectFromUsername parses a username string into a models.SubjectRef. ServiceAccount users are emitted by the API server as `system:serviceaccount:<namespace>:<name>` - we map that into the canonical SubjectRef shape so it aligns with snapshot-derived subjects. Any other shape (humans authenticated via OIDC/cert, internal system users, `system:anonymous`, etc.) returns ok=false; v1 of the least-privilege analyzer only considers ServiceAccount workloads.

Types

type AuditEvent

type AuditEvent struct {
	Username    string
	Verb        string
	APIGroup    string
	Resource    string // including any subresource appended as "resource/subresource"
	StatusCode  int
	RequestedAt time.Time
}

AuditEvent is the normalized intermediate shape the parsers produce. Both the native kube-apiserver JSON-lines parser and the EKS CloudWatch envelope parser emit AuditEvents that the indexer consumes. Keeping a single internal shape means new audit-log sources only need a new parser, never analyzer-side changes.

type Source

type Source string

Source identifies the audit-log format used to produce paths.

const (
	SourceNative Source = "native"
	SourceEKS    Source = "eks"
)

func ParseSource

func ParseSource(value string) (Source, bool)

ParseSource maps the CLI flag to a Source value. Empty defaults to native.

type UsageIndex

type UsageIndex struct {
	WindowStart     time.Time
	WindowEnd       time.Time
	EventsProcessed int
	// EventsSkipped counts events dropped during ingestion: denied responses, parse errors,
	// out-of-window timestamps, non-ServiceAccount users. The total is surfaced in the HTML
	// report so reviewers can sanity-check ingest health.
	EventsSkipped  int
	NonSAUsernames int // events from human/group users; ignored by v1 but counted for transparency
	// contains filtered or unexported fields
}

UsageIndex is the in-memory aggregate of audit-log observations keyed by subject and (apiGroup, resource[/subresource]). Construct one via LoadAuditLog. Queries are read-only and safe to call from multiple goroutines.

func EmptyIndex

func EmptyIndex() *UsageIndex

EmptyIndex returns a usable empty index. Callers in pre-flight error paths use it so downstream code can dereference the pointer without nil-checks.

func LoadAuditLog

func LoadAuditLog(paths []string, source Source, window time.Duration, now time.Time) (*UsageIndex, []string, error)

LoadAuditLog ingests one or more audit-log paths into a UsageIndex restricted to events within the last `window` duration. Paths may be individual files or directories; when a directory is supplied, every regular file inside (one level deep) is consumed. `.gz`-suffixed files are streamed through a gzip decoder so rotated logs work out of the box.

Permission/format errors on individual files are surfaced as warnings appended to the returned warnings slice and never abort the load - this mirrors the collector's "partial snapshots are still useful" posture. A nil paths slice or zero window yields an empty index without error so callers can dispatch on the resulting state.

func (*UsageIndex) HasAnyEventsFor

func (i *UsageIndex) HasAnyEventsFor(subj models.SubjectRef) bool

HasAnyEventsFor reports whether the index contains any event attributed to subj within the observation window. Used by the analyzer to distinguish "subject is silent on resource X but active elsewhere" (narrow finding) from "subject is silent everywhere" (whole-Role candidate for removal).

func (*UsageIndex) Observed

func (i *UsageIndex) Observed(subj models.SubjectRef, apiGroup, resource string) verbSet

Observed returns the verbs observed for subject on (apiGroup, resource). Subresources are tracked separately: a query for "pods" will NOT return verbs observed against "pods/exec". This matches Kubernetes RBAC semantics - pods and pods/exec are distinct grants and need distinct usage signals.

Returns an empty (non-nil) verbSet when nothing has been observed; callers can iterate freely without nil checks.

func (*UsageIndex) ObservedGVRs

func (i *UsageIndex) ObservedGVRs(subj models.SubjectRef) []gvrKey

ObservedGVRs returns every (apiGroup, resource) coordinate that subj has touched. Used by the wildcard-expansion path: when a Role grants `verbs: ["*"]` on `apiGroups: ["*"]`, we don't enumerate every possible cluster verb - we only consider what the subject has actually exercised, which is the only signal we have for "narrower than the wildcard."

Jump to

Keyboard shortcuts

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