1
lfme.kosir.info · agentless · MIT
=GREET(thy.fleet)

Linux Fleet MOTD Editor

Every server thou ownest says something when thou enterest. On most of them it says nothing at all, and on the rest it says something a colleague typed in 2019 and then left the company. This writes the same true greeting on all of them, keeps it current, and remembers what was there before.

And lo, the banner said WELCOME TO WEB01, and it was web04. Greetings 1:1
sa.vko@web01 — the greeting
==============================================================================
 PRODUCTION  |  web01.example.com
==============================================================================
 IP        : 10.20.4.11      Platform : kvm
 OS        : Red Hat Enterprise Linux 9.4 (Plow)  (5.14.0-427.el9.x86_64)
 Owner     : Platform
 Support   : [email protected]

 Uptime    : 41d 6h 12m
 Load      : 0.42 / 0.51 / 0.48   (8 cores, 0.05 per core)
 Memory    : 11.2G / 15.5G  (72%)
 Disk /    : 18.1G / 40.0G  (46%)
 Disk /var : 7.9G / 8.0G  (98 !!%)
 Sessions  : 2

 Updates   : 14 pending, 3 !! security
 !! REBOOT REQUIRED
 !! 2 failed unit(s): chronyd.service,rsyslog.service

 INTERNAL. Authorised use only. Activity is logged.
 Raise changes at https://jira.example.com/servicedesk
==============================================================================
2

The Greeting

One template. Every host. No agents.

It reads a list of hosts, connects with the keys thou already hast (or a password, if thy fleet is honest about what it is), escalates with sudo, and writes a small shell renderer that draws the banner at each login. Not a static file that rots. A renderer, so the uptime is the real uptime.

It backs up first

Everything it touches is copied, hashed, and written into a manifest before a single byte changes. Including the files that were not there.

It reads back

Every write is re-read and compared. A write that claims success and did not land is a failure here, not a pass.

It can undo

One command puts the host back exactly as it was, including deleting the files it created. Most tools forget that part.

Where the banner actually comes from

There is no single MOTD mechanism, which is the first thing everyone gets wrong. It detects and adapts, per host.

FamilyMechanismThe catch
Debian, Ubuntu /etc/update-motd.d/ pam executes these. They all run, so adding one gives thee two banners, not one.
RHEL / Rocky / Alma 8, 9 /etc/motd.d/ pam reads these. It does not execute them. A script dropped here will be printed to the user as source code.
RHEL / CentOS 7 /etc/profile.d/ That pam reads /etc/motd and nothing else. No drop-ins exist.
They dropped the script into motd.d, and pam printed it, and every user read the source. Lamentations 4:4
3

The Fields

Weighed at every login, or weighed on a timer

Anything drawn at login runs on every SSH session, including thy monitoring checks and thy backup jobs. So the fields are split by what they cost, and the expensive ones never touch the login path.

CostFieldsWhen
Fast hostname, IP, OS, kernel, platform, uptime, load, memory, swap, disk, sessions Every login. Reads /proc and nothing else. Budget: 50ms.
Slow pending updates, security updates, reboot required, failed units, last audit A systemd timer, every 30 min with a random delay. Cached to a file.
Policy environment, owner team, support contact, ticket queue, change freeze, classification From thy inventory. Costs the host nothing.

A renderer that shells out to dnf adds seconds to every login. Multiply that by a check that SSHes into two hundred hosts every minute and thou hast built thyself an outage, with a banner on it.

Thresholds

Values past their threshold are coloured and marked. Both, not either: the static delivery mode cannot carry colour, so a breach that relied on red would be invisible in exactly the mode that reaches every login.

ResultFieldDetail
OKDisk /46% — below 85, drawn plain
!!Disk /var98% — coloured, and marked, and still marked when piped
!!Security updates3 — the number that ends up in the incident review
OKLoad per core0.05 — the machine is asleep and content
4

The Sin of the Login Shell

Breaking the fourth wall, because this one is not a joke

A script in /etc/profile.d that prints to stdout without checking will corrupt scp, sftp and rsync on every host thou hast touched. Those protocols read the SSH stream as data. Thy banner becomes the first few bytes of the file.

The symptom is truncated transfers and mysterious checksum failures across the fleet, and nothing about it points back at a login banner. People have lost days to this.

# the first four lines of the generated hook, and the reason for them
case $- in *i*) ;; *) return 0 ;; esac   # interactive shells only
[ -t 1 ] || return 0                     # stdout must be a terminal
[ -n "${LINUX_MOTD_QUIET:-}" ] && return 0
[ -n "${LINUX_MOTD_SHOWN:-}" ] && return 0

The test suite runs a real scp and a real sftp get against an installed host on every supported image. Reading the code is not testing it. That part is also not a joke.

5

The Rite of Installation

Look before thou writest

# receive the canon
pip install linux-motd
# see what one host would say. writes nothing.
linux-motd preview -i inventory.yaml -H web01.example.com
# the whole fleet, as a plan with diffs. still writes nothing.
linux-motd plan -i inventory.yaml
# now write it, one approval at a time
linux-motd push -i inventory.yaml --ask-ssh-pass --sudo-pass-same-as-ssh

The gate offers [y]es / [n]o / [a]ll / [q]uit / [d]etail. --dry-run writes nothing at all. --yes skips the asking, for machines, and still takes every backup.

6

The Penitence

Undoing, properly

Most rollback restores what it overwrote and calls it done. But nearly every file this tool installs is new, and restoring only what existed leaves all of them in place. The manifest records the absence explicitly, so the undo deletes.

linux-motd rollback -H web01.example.com
linux-motd remove   -i inventory.yaml
It remembersSo that
Content hash, mode, ownerThe file comes back exactly, not approximately.
SELinux contextA file with the right bytes and the wrong label is ignored by pam. It checks.
existed: falseFiles it created are deleted, not left behind wearing a fresh timestamp.
The mode of thy distro's own scriptsUbuntu's banners are disabled by clearing a bit, and the bit comes back.
The rollback reported success, and on the next reboot the change returned. Numbers 7:9
7

What This Actually Is

Plainly, for a moment

A real tool, written by a sysadmin who got tired of logging into a box and not knowing which one it was. Agentless, parallel, MIT licensed, and it fails per host: one unreachable machine is logged with the reason and the run carries on.

The banner is shown to anyone who authenticates, including a compromised account, so every field that is useful reconnaissance can be switched off per group. The pre-auth banner in /etc/issue is stricter still: it uses a separate template whose loader rejects every hostname, address, version and patch-state field by name. Not by convention, at load time, with an error.

It also never writes a spreadsheet formula. Everything a host reports is written to the report as text, so a compromised server cannot hide a payload in its hostname and have it fire when thou openest the workbook. That part is not a joke either.

Built by vK, who has been paged at 03:00 and did not enjoy it. Companion to Linux Fleet Audit, which reads thy fleet, and linux-harden, which mends it. This one merely announces the result.

Kneel also at the parish and read at the scriptorium. The Cluster abides. The Loop reconciles.