=CANON(the.fleet)
1
lft.kosir.info · agentless · MIT

Linux Fleet Toolkit

One testament, several books. No agent in any of them.

Ten command-line tools for running a RHEL 9 fleet over SSH. Each does one job, writes a report thou canst hand to anyone, and re-checks the live host before it changes a thing.

“And the disk was at ninety-seven percent, and no one had looked.” Findings 3:16 — the verse that started all of this

They were written in the order the pager demanded them. First the one that looks, then the one that mends, then the one that empties the disk at 3am, and so on until there were ten. They do not share a command surface — they share something more useful, which is a posture. It is set out below.

local.user@localhost $ ssh web01 'rpm -qa | grep fleet-agent'
(no output)
local.user@localhost $ ssh web01 'systemctl list-units | grep fleet'
(no output)
Agents installed by this toolkit: 0
Agents it will ever install: 0
2

The Books

Ten of them. Read whichever one thy week requires.

Every row is a working tool with its own site and its own repository. The verbs column is there because they genuinely differ — this is not a suite with one command spelled ten ways.

BookVerbsWhat it does
linux-audit
lfa.kosir.info
read only
(no subcommand)
One read-only collection pass per host: inventory, severity-ranked Findings, and around thirty CIS-style PASS/FAIL/WARN checks. Optionally emits a remediation plan with --plan-out. The one that found the ninety-seven percent.
linux-harden
lfh.kosir.info
asks first
plan.json, --apply, --rollback
Consumes the audit's plan, re-verifies every finding live, and applies only what thou approvest — one check at a time, fleet-wide. Local backups, real rollback, and an sshd watchdog that restores itself if thou losest the connection.
linux-patch
lfp.kosir.info
asks first
discover apply rollback
Staged dnf patching. Tags security advisories with severity, re-runs check-update before installing, and reboots only with --reboot — serialized, one host at a time.
linux-certs
lfc.kosir.info
read only
scan report
Every TLS certificate on every host, sorted by the day it dies. Undersized keys and SHA-1/MD5 signatures named. It never renews anything — that is thy CA's job.
linux-users
lfu.kosir.info
asks first
discover apply
Accounts, sudoers and authorized_keys across the fleet. Locks the stale ones, expires them with --expire, and never runs userdel. Keys and sudoers are reported, never modified.
linux-drift
lfd.kosir.info
read only
capture check
Capture a baseline, then know the hour anything moves away from it: effective sshd -T config, sysctl, file hashes, packages, ports, users, groups. Or hold the whole fleet to one golden host.
linux-firewall
lff.kosir.info
asks first
discover apply
Declare what each host may expose in a policy file, then hold firewalld's default zone to it. An SSH lockout guard blocks anything that would drop thy own session, unless thou passest --force.
linux-diskspace
lds.kosir.info
asks first
discover apply
Finds what filled the disk — bytes and inodes, flagged independently — and empties per host only what is safe. Truncates rather than deletes, and knows exactly why that matters. A hardcoded deny list no flag can override.
linux-motd
lfme.kosir.info
asks first
preview plan push
Writes the login banner on every host — uptime, load, disk, pending updates, failed units. Backs up what was there, verifies the write landed, and puts it all back with remove. Eight verbs, because presentation has more states than it looks.
linux-apply
lfr.kosir.info
asks first
adapt run
A shared applier for straightforward plans, and the place to compose actions across tools. Three wired adapters: patch, users, firewall. It does not replace the tools' own apply — it is the lighter path when their specifics are not needed.
3

What They Actually Share

Not a command surface. A posture, and two modules.

Tools that pretend to be uniform when they are not will lie to thee somewhere. These are honestly different shapes — but four things hold everywhere, and they are the reason the family is worth thinking of as one.

I. Looking and acting are different invocations

Every tool that can change something reaches that behaviour through a separate verb or an explicit flag. linux-harden previews unless thou passest --apply; linux-patch will not reboot without --reboot; linux-audit, linux-drift and linux-certs cannot write to a target at all, under any flag.

II. Live re-validation before acting

This is the real signature. Between the plan and the apply, someone deployed something, and every one of these tools assumes it. linux-harden re-reads each finding, linux-patch re-runs check-update and installs only the intersection, linux-users re-runs discovery and drops anyone who logged in since, linux-diskspace re-measures every candidate. Anything that no longer holds is dropped rather than forced.

III. A human approves, and the default is no

Per-host confirmation with N as the default. Several tools have an escape hatch for automation — linux-firewall's -y, linux-motd's --yes — and linux-harden deliberately has none at all, refusing to run --apply without a TTY.

IV. Failure is a row, not a crash

A host thou canst not reach goes to an Errors sheet with the reason, and the run carries on through the other seventy-nine. A fleet tool that stops at the first unreachable host is a single-host tool with ambitions.

The shared code is smaller than the shared posture: two modules, ssh_exec.py for transport and xlsx_safe.py for report safety, carried by the tools that need them.

4

The Common Rule

Three refusals, and one shared inventory

No agent. Ever.

Nothing is installed on a target. The tools need SSH, sudo, and standard utilities that are already there. Every remote probe is guarded, so a missing tool is skipped rather than fatal. When thou art finished, thy fleet is exactly as agent-free as when thou began.

No password ever reaches argv

Passwords come from a prompt or an environment variable and travel by stdin, because /proc is world-readable and another user's ps is not a place for thy service account's password. The flags are the same everywhere: --ask-ssh-pass, --ssh-pass-env, --ask-sudo-pass, --sudo-pass-same-as-ssh.

No spreadsheet formula, ever

Everything a host reports is written to the workbook as text. A compromised server cannot hide a payload in its hostname, a package name or a GECOS field and have it fire when thou openest the report. This lives in xlsx_safe.py and it is not a joke in any of them. linux-audit even ships check_xlsx.py so thou canst verify a report contains no formula cells before opening it.

Two guards, not one

Both protect thy own access, and they are genuinely different mechanisms — worth knowing which thou art relying on.

linux-harden's sshd watchdog is three layers: sshd -t validates the edit, an on-host watchdog is armed that restores the old config by itself if it is not disarmed within --watchdog seconds (default 90), and then the controller opens a fresh SSH session as proof. The session that made the change proves nothing about whether new logins still work.

linux-firewall's SSH lockout guard blocks any rule change that would drop thy control access, and lists what it blocked. --force disables it. linux-apply's firewall adapter separately drops any removal of the ssh service or the control port before it runs.

One inventory, written down once

linux-fleet-common is the shared inventory model — one inventory.yaml with defaults, groups, hosts and vars, resolved host > group > defaults > builtin, where every resolved value records which layer supplied it. Used by linux-audit, linux-harden and linux-motd. Unknown keys are errors rather than silent no-ops, and selecting a host that does not exist fails instead of succeeding against nothing.

The other tools take a plain host list file: one host per line, blank lines and # comments ignored, accepting host, user@host, host:port and user@host:port.

5

The Rite of Installation

Examine, then absolve — the path the toolkit was built around

# receive the first book git clone https://github.com/vikozs/linux-audit.git cd linux-audit python3 -m pip install -r requirements.txt
# examine — read-only, and the plan is what linux-harden consumes python3 linux_audit.py -H hosts.txt -u local.user \ --ask-ssh-pass --sudo-pass-same-as-ssh \ -o audit.xlsx --plan-out plan.json
# preview — the default; prints what it would offer and exits python3 linux_harden.py plan.json
# absolve — interactive, one check at a time, default always no python3 linux_harden.py plan.json --apply -u local.user \ --ask-ssh-pass --sudo-pass-same-as-ssh

Each of the other books installs the same way — clone, install requirements, run. Most also accept python3 -m pip install . if thou preferest a command on thy path. Python 3.9+ and openpyxl on the controller; sshpass only for password SSH login. linux-apply needs neither.

6

The Communion of Tools

Nothing here works alone, and neither shouldst thou

The fleet is one testament in several books. Each does one job, writes a report thou canst hand to anyone, and refuses to be clever with thy servers.