English · 한국어
Does this Linux server pass muster? muster collects the facts of a host as
root, then evaluates them — offline, from the snapshot alone — against the Unix
server items (U-xx) of the KISA technical vulnerability assessment guide for
critical information infrastructure, with CIS Benchmarks®, DISA STIG and NIST SP
800-53 numbers attached as cross-references where a mapping is filed.
In Korean security practice this kind of check is called a CCE assessment (configuration vulnerabilities, as opposed to CVE). muster is a CCE checker for the Linux server asset class: KISA is the primary standard, and global benchmarks (CIS Benchmarks, DISA STIG, NIST SP 800-53) are attached as references and, later, offered as selectable profiles.
Status: stage 3D-1b merged (October 2026). Stage 1 — the skeleton:
collect,check, waivers, exit codes, eight controls end to end — stage 2 — every automatable KISA item, from the foundations with DISA STIG and NIST SP 800-53 references through accounts, PAM, sshd, home directories, system files, services, the firewall, logging, NFS, SNMP, patch hygiene, FTP, mail and DNS to the closing coverage-and-reference gate — and stages 3A, 3B, 3C-1, 3C-2a, 3C-2b, 3D-1, 3D-2, 3D-2b, 3D-2c, 3D-2d and 3D-1b — the deep filesystem walk, the first checks beyond the guide, audit pipeline health and package integrity, who holds root's power without being root, exposure, profiles and tuning, and thefix --dry-runreview script, its proof on real hosts and scripts for the controls beyond the first sixteen, the audit family and what a file says, and the CIS Level 1 profiles over an index of recommendation numbers — are merged. 67 of the 67 items are enrolled (68 controls: 57 auto, 7 partial, 4 manual with the evidence attached), and 49 beyond the guide.collect --deepwalks the local filesystems once, under a time and entry budget, without following a symlink or entering a remote mount or a container layer, and joins every setuid, world-writable, unowned or hidden entry to the package that owns it — rpm's file table on the RHEL family, dpkg's file lists plus a reference list of declared modes on the Debian family — so U-15, U-23, U-25 and U-33 are judged from evidence rather than reported for review. Which item is judged by which control, with its automation class, and which fact keys the controls read is generated into the coverage table and checked by CI, which also runs the collector, walk included, as root on a VM, as a normal user, inside Ubuntu 22.04/24.04 and Rocky/Alma 9 containers (with Debian 12 as a non-blocking canary), in a read-only container without a network, and against a capability matrix of facts that must readdeniedwithout root,unsupportedwithout systemd andunsupportedfor the walk inside a container. The architecture, contracts and release scope are written up in the design specification; changes that alter a verdict are recorded in CHANGELOG.md. The remaining stage-3 and stage-4 items below are the plan, not a promise.
Not affiliated. muster is an unofficial personal project. It is not endorsed by KISA or by the Center for Internet Security® (CIS), it contains no CIS Benchmark text (only recommendation numbers), it does not claim any level of CIS compliance, and it does not replace an official vulnerability assessment.
sudo muster collect --out host.json # on the host, as root: facts snapshot
muster check --facts host.json # anywhere, no root: table + exit code
muster check --facts host.json --format json
muster check --facts host.json --profile docs/examples/profiles/exclude-beyond.yaml # the KISA guide alone
muster check --facts host.json --tuning docs/examples/profiles/tuning.yaml # a site's parameter values
muster fix --dry-run --facts host.json --format script > fix.sh # a review script; muster never runs it
muster controls lint --references docs/reference # the control set's own gate
collectruns on the host and writes one JSON snapshot: the OS release and environment, the package list, effectivesshdsettings (with theMatchpersonas), the PAM stacks and the policy derived from them, service, socket and super-server state, listening sockets, the firewall backend and its rules, accounts and password policy, home directories and the shell environment, login banners, the permissions of system files, startup scripts, cron entries andsudoers, logging and time synchronisation, NFS exports, SNMP (with communities reduced to their shape, never the string), patch state, and FTP, mail and DNS configuration. It writes nothing else, restarts nothing, and never touches the network.checkreads a snapshot plus a control set and reports, per control:PASS,FAIL,WARN,MANUAL,NOT_APPLICABLE,ERRORorWAIVED, always with the evidence that produced the verdict (the value, the file and line it came from, the reason). It does not need the host.- Controls are data. Each KISA item is a YAML control with a small judgment vocabulary; an item that cannot be expressed that way can call a named Go function, and today none needs to. Distribution differences are absorbed in the collector, so a control is written once against normalized facts.
- Waivers live in a file with a mandatory reason and an optional expiry.
A waived finding is counted and shown, never silently dropped, and a waiver
never covers an
ERROR. - Exit codes are a contract:
2(could not run or cannot be trusted) outranks1(findings) outranks0(clean).MANUALitems do not fail the run unless you ask them to. A FAIL beyond the guide counts like any other: a host that is clean against the 67 items but ships stock kernel, boot or mount settings reads1— the summary's two scopes say which side it is.
- Unseen is unseen. A fact that could not be read is
ERROR, never a quietPASS. Half a check is not aPASSeither: items that need an interview are reported asMANUALwith whatever evidence was collected, and so is a host whose layout muster declined to read (a home outside the usual roots, a configuration construct outside the parser's model) — named in the reason, never guessed. - Evidence rides in the result. If the reason for a verdict only exists in a log line, it effectively does not exist.
- Runtime and persisted are both checked. A firewall whose kernel ruleset is
live but whose configuration on disk would not bring it back, a password
policy that differs between
login.defsand the running PAM stack, or a service that isenabledbut socket-activated, is reported as what it is, not folded into one boolean. - Safe to run as root. No shell, a fixed whitelist of commands with absolute
paths and timeouts, no symlink following, a size cap on every file read, and a
snapshot that stores derived attributes (hash algorithm, machine-id hash, an
SNMP community's length and whether it is a default) rather than secrets.
muster collect --list-actionsprints exactly what the collector reads and runs.
- CVE matching. The snapshot carries the installed package list so it can be handed to a vulnerability scanner such as assay as an SBOM; muster itself never matches advisories.
- Runtime detection (eBPF, process monitoring) — tools like Tetragon exist.
- Container and Kubernetes benchmarks — kube-bench exists.
- External port scanning. Network checks are host-internal only: listening sockets, firewall rules, unnecessary services.
- Applying fixes. Every judged control carries its remediation text, risk
level and rollback;
fix --dry-runturns a host's failures into a risk-ordered plan and a review script, and muster never executes it. - Other asset classes. Windows, DBMS, web/WAS, network and security devices have their own sections of the KISA guide. They would be sibling tools sharing muster's control and report contracts, never part of this codebase.
Linux only, permanently. The first release targets Ubuntu LTS (22.04, 24.04) and Rocky / AlmaLinux 9; supporting both from the start forces the distribution-variance structure to exist rather than be retrofitted.
The control set follows the structure and numbering of the Unix server section of the 2026 edition of the KISA guide (주요정보통신기반시설 기술적 취약점 분석·평가 방법 상세가이드, published 2025-12-24): 67 items, U-01–U-67. Every control carries its KISA item number per edition, including the 2021 number where the 2021 edition had the item, so older assessments can still be related to a result.
From the guide, this repository reproduces only item codes, item names,
categories, importance levels and page numbers, as a cross-reference index
(docs/reference/kisa/). Control titles, descriptions and rationale are
muster's own wording; no inspection, criterion or remediation text from the
guide is included, and the guide itself is not included. CIS Benchmark
references are benchmark name, version and recommendation number only, and
muster judges no CIS compliance. See ATTRIBUTION.md.
Global benchmarks are cross-references, not a second rulebook. Of the 117
controls, 58 carry DISA STIG rule ids, 65 carry NIST SP 800-53 control ids and
69 carry CIS Benchmark recommendation numbers; a control gets one only where
the benchmark judges the same criterion. muster controls lint refuses a STIG,
NIST or CIS reference that is not in the committed index and cross-checks every KISA item
number against the 67-item inventory, so an item can be neither forgotten nor
mis-claimed (an item may be judged by more than one control, as U-23 is, but
every citation must name a real item). Since stage 3D-1 a profile (check --profile)
selects controls and parameters from the same collector, and since 3D-1b three built-ins,
cis-ubuntu2204-l1, cis-ubuntu2404-l1 and cis-rhel9-l1, select the controls that
carry a Level 1 – Server recommendation of their benchmark, minus a committed review
list (docs/reference/cis/review.yaml)
(muster check --facts snap.json --profile cis-ubuntu2204-l1).
The cis-<distro>-l1 profiles select muster's own controls that carry a CIS
Benchmark recommendation number at Level 1 – Server; their results are
muster's verdicts on muster's controls, not a CIS assessment, and muster is an
independent publication not authorized, sponsored or otherwise approved by the
Center for Internet Security. The
recommendation numbers are generated into docs/reference/cis/ by tools/cisindex
from ComplianceAsCode's public control files (BSD-3-Clause; see NOTICE). muster records the
numbers, never the benchmark text, and certifies no level of CIS or STIG
compliance. The STIG and NIST identifiers a control may cite are generated
into docs/reference/stig/ from DISA's public files by tools/refindex.
This repository reproduces only STIG and CCI identifiers, severities and
titles, never their discussion, check and fix text. See
ATTRIBUTION.md.
- Skeleton.
collectandcheck, the facts schema, table and JSON output, exit codes, waivers, and eight controls flowing end to end. - Both distributions, every automatable KISA item. Collectors for Ubuntu
and Rocky; 64 of the 67 items judged automatically or with automatic
evidence, the three that need a filesystem traversal — file and directory
ownership (U-15), SUID/SGID/sticky files (U-23) and hidden files (U-33) —
deferred to the walk. Every control gains DISA STIG and NIST SP 800-53
reference numbers where a mapping exists, and
controls lintcross-checks every KISA reference against the 67-item inventory. - The deep filesystem walk (3A, merged), then high-value checks beyond the
list and profiles, from the same collector. 3A:
collect --deepenrols the three deferred items and makes U-25 judge a real host — 67 of the 67 items, 68 controls (57 auto, 7 partial, 4 manual with the evidence attached); a maintainer tool,tools/suidindex, generates the per-release reference lists of declared modes from public container images. 3B (merged): the first nineteen controls beyond the guide — kernel self-protection sysctls and the core-dump policy, the boot chain, separate partitions and mount options, swap encryption, module blacklists — from six read-only collectors, with the summary split into the guide and beyond it; a FAIL beyond the guide counts for the exit code unless a profile excludes the control (3D-1). 3C-1 (merged): audit pipeline health and package integrity — the audit daemon's rules, immutability, disk actions and log permissions, log forwarding, sudo's own log, the file-integrity tool, and whole-database package verification (rpm -Va/dpkg --verifyunder--deep, the noise filter recorded) — nine controls from three collectors and two extensions. 3D-1 (merged): profiles and tuning —check --profileselects controls by id globs, overrides parameters and severities and chains throughextends;check --tuninglayers a site's values; the result records both with every value's source. 3D-2 (merged):fix --dry-run— the failures of the profile in force as a risk-ordered plan (table, JSON) and a POSIXshreview script, one block per control with declared backups, a one-line validate and rollback-then-restore; muster never runs it, and a CI job proves the seeded scripts in throwaway containers. 3D-2b (merged): the review script, proven — a host guard refuses a script on a host whose machine id differs from the snapshot's (MUSTER_ANY_HOST=1overrides); service controls declare their units, and a failed block puts each unit's recorded enablement and activity back; file scripts only clear bits; a table holds every scripted control to the round-trip rows that prove it (Rocky 9, AlmaLinux 9, Debian 12, Ubuntu 22.04) or to the reason none can, and each init row also proves the guard and the rollback of a deliberately failed run. 3D-2c (merged): scripts beyond the sixteen — 48 controls now carry a review script, adding the kernel settings, the moduleinstalllines, nine more file and directory permissions and four account policies; a script may read a profile's or tuning's parameter as aP_value held to a shell-safe charset, writes only when the host is outside what the control accepts, and records the modes, kernel values and module states it changes under its own block's directory, so a failed block puts them back; the GitHub runner VM joins the round-trip rows to prove what a container cannot judge. 3D-2d (merged): 56 controls now carry a review script — the audit daemon started and enabled, a floor of audit watches persisted where the host keeps none and loaded only into an empty kernel list, auditd's disk actions set to values that keep the host up, the inactivity bound over every account that can log in (root included, skipping any account it would disable at once), and what a file says:+lines in/etc/hosts.equiv, postfix's VRFY, vsftpd's greeting and anonymous login, andno_root_squashin exports. No script makes a change only a reboot undoes, and none starts auditd or loads rules over a pending-e 2. Three collector readings those files exposed were fixed first: vsftpd's greeting order, an auditdexecaction and an exports default option list. 3C-2b (merged): exposure — every listening socket's owners and packages, deleted executables still running, the exposure cross-check against the firewall's folded rule table, the network sysctls — thirteen controls. 3C-2a (merged): privilege — file capabilities judged against the package's own declaration, root services' writable executables,ld.so.preload, container-runtime sockets and their groups, passwordlessALLin sudoers, the inactivity policy over every interactive account, root's authorized keys and SSH key quality — eight controls from three extended collectors and three new ones. 3D-1b (merged): thecis-<distro>-l1profiles for Ubuntu 22.04, Ubuntu 24.04 and the RHEL 9 family over an index of CIS recommendation numbers (references.cison every selected control). Next: 3D-3 (--anonymize,--max-age).kisa-unix-2026stays the default profile. - Public release. Snapshot diff, SARIF, bilingual docs, signed reproducible builds with SBOM, deb/rpm packages, and a differential comparison against Lynis.
Deferred to a later release: multi-host aggregation, an agentless SSH mode, an ISMS-P mapping, file-integrity baselines, certificate expiry, OS end-of-life detection, cloud VM items.
examples/ holds two real snapshots and the reports muster check
makes from them — one collected inside a public ubuntu:24.04 container, one on
the GitHub Actions runner VM with --deep — so the shape of the output can be
read without running anything. Neither comes from anyone's host: the
examples.yml workflow collects them and rewrites the host identity before they
are kept, and a test checks them end to end on every change.
Beside each snapshot's report, examples/<snapshot>-fix-report.{json,txt,sh} show
what fix --dry-run makes of it: the plan as JSON and as a table, and the review
script.
docs/examples/profiles/ holds an example profile
(exclude-beyond.yaml: the KISA guide alone, the controls beyond it excluded by one
id glob) and an example tuning file (tuning.yaml: one site's parameter values).
check --profile <name|path> and check --tuning <path> take them; the result
records both, with every value's source and every excluded id.
- CONTRIBUTING.md — adding a control (
muster controls newscaffolds it,muster snapshot extractcuts its fixture out of a snapshot), adding a fact, the collector conventions, the test discipline, and what must never be committed. - CHANGELOG.md — the control set's version and every change that alters a verdict.
- docs/reference/coverage.md — which KISA item is judged by which control, and which fact keys the controls read; generated, checked by CI.
- docs/reference/kisa/ — the item inventory and the deferred items; docs/reference/stig/ — the STIG and NIST identifiers a control may cite.
- SECURITY.md, THREAT_MODEL.md, ATTRIBUTION.md.
assay is a vulnerability scanner; muster is a configuration checker. They share decisions (evidence in the result, the exit code contract, waiver rules, per-distribution logic kept separate), not code.