Check lynis¶
Overview¶
Runs a full security audit across the hosts of a subnet and reports each host’s hardening posture. From a single management host it discovers the targets (the subnet of the default interface, a chosen interface, or an explicit host list), connects to each one over SSH, copies a self-contained copy of the audit tool over, runs a privileged system audit (root via password-less sudo by default), retrieves the machine-readable report, and removes its temporary files. A host that does not answer within the connect timeout is skipped. The check is meant to run at most once per day; the worst per-host result determines the overall state. Security posture is informational drift rather than a time-critical availability event, so by default only WARNING is raised. Hosts are audited in parallel.
Important Notes:
Requires SSH access to every target host. Without parameters the plugin assumes password-less public key authentication and password-less
sudoon the targets. Host aliases from~/.ssh/configare honored.Requires
lynison the management host (where the plugin runs); a self-contained copy is assembled and pushed to the targets, solynisdoes not need to be installed on them.The audit runs privileged (via
sudo) so the hardening index reflects a complete scan. The report is left on each target at/var/log/lynis-report.datfor the admin to inspect.lynisis a script that must be executed, so the plugin uses the first target partition that is writable and not mountednoexec(hardened hosts often mount/var/tmpand/tmpnoexec).The copy is pushed with
rsyncwhen it is available on the management host (faster), otherwise withscp -r; neither requirestaron the target.A security audit is posture drift, not a time-critical availability event, so by default only WARNING is raised. The default critical threshold is empty on purpose, to avoid paging someone at night for a hardening drop.
Data Collection:
The per-host state is the worst of: the hardening index against
--warning/--critical(Nagios ranges, default warns below 65), and the presence of any lynis warning.Every lynis warning raises at least WARNING. A warning is a concrete finding, not noise; accept it on the host (see Troubleshooting), not in this plugin.
Auto-discovery probes raw IP addresses, which do not match per-host
~/.ssh/configaliases. For--network/--interfacediscovery, provide working credentials with--usernameand--identity.
Fact Sheet¶
Fact |
Value |
|---|---|
Check Plugin Download |
https://github.com/Linuxfabrik/monitoring-plugins/tree/main/check-plugins/lynis |
Nagios/Icinga Check Name |
|
Check Interval Recommendation |
Once a day |
Can be called without parameters |
Yes |
Runs on |
Linux |
Compiled for Windows |
No |
Requires on the management host |
|
Help¶
usage: lynis [-h] [-V] [--always-ok] [--audit-timeout AUDIT_TIMEOUT]
[--configfile CONFIGFILE] [--connect-timeout CONNECT_TIMEOUT]
[-c CRIT] [--disable-pseudo-terminal] [-H HOST]
[--identity IDENTITY] [--interface INTERFACE] [--ipv4] [--ipv6]
[--lengthy] [--lynis-auditor LYNIS_AUDITOR]
[--lynis-option LYNIS_OPTION] [--lynis-profile LYNIS_PROFILE]
[--lynis-skip-test LYNIS_SKIP_TEST] [--lynis-source LYNIS_SOURCE]
[--lynis-test LYNIS_TEST]
[--lynis-test-category LYNIS_TEST_CATEGORY]
[--lynis-test-group LYNIS_TEST_GROUP] [--max-workers MAX_WORKERS]
[--network NETWORK] [-p PASSWORD] [--port PORT] [--quiet]
[--ssh-option SSH_OPTION] [--test TEST] [-u USERNAME] [--verbose]
[-w WARN]
Runs a full security audit across the hosts of a subnet and reports each
host's hardening posture. From a single management host it discovers the
targets (the subnet of the default interface, a chosen interface, or an
explicit host list), connects to each one over SSH, copies a self-contained
copy of the audit tool over, runs a privileged system audit (root via
password-less sudo by default), retrieves the machine-readable report, and
removes its temporary files. A host that does not answer within the connect
timeout is skipped. The check is meant to run at most once per day; the worst
per-host result determines the overall state. Security posture is
informational drift rather than a time-critical availability event, so by
default only WARNING is raised.
options:
-h, --help show this help message and exit
-V, --version show program's version number and exit
--always-ok Always returns OK.
--audit-timeout AUDIT_TIMEOUT
Seconds to wait for the remote audit of a single host
to finish. Default: 600 (seconds)
--configfile CONFIGFILE
SSH: Alternative per-user configuration file. If a
configuration file is given on the command line, the
system-wide configuration file (`/etc/ssh/ssh_config`)
will be ignored. The default for the per-user
configuration file is `~/.ssh/config`. If set to
`none`, no configuration files will be read.
--connect-timeout CONNECT_TIMEOUT
Seconds to wait for the SSH connection to a target
host before skipping it. Default: 3 (seconds)
-c, --critical CRIT CRIT threshold for the per-host hardening index
(0-100). Supports Nagios ranges. Empty by default,
because a daily hardening scan is posture drift, not a
time-critical event that should page someone at night.
Default: (no critical)
--disable-pseudo-terminal
SSH: Disable pseudo-terminal allocation.
-H, --host HOST Target host to audit. Overrides subnet auto-discovery.
Can be specified multiple times. If not specified, the
subnet of the default interface is scanned.
--identity IDENTITY SSH: File from which the identity (private key) for
public key authentication is read. You can also
specify a public key file to use the corresponding
private key that is loaded in ssh-agent(1) when the
private key file is not present locally. The default
is `~/.ssh/id_dsa`, `~/.ssh/id_ecdsa`,
`~/.ssh/id_ecdsa_sk`, `~/.ssh/id_ed25519`,
`~/.ssh/id_ed25519_sk` and `~/.ssh/id_rsa`. Identity
files may also be specified on a per-host basis in the
configuration file. It is possible to have multiple
--identity options (and multiple identities specified
in configuration files). If no certificates have been
explicitly specified by the CertificateFile directive,
ssh will also try to load certificate information from
the filename obtained by appending `-cert.pub` to
identity filenames.
--interface INTERFACE
Network interface whose subnet is scanned. Ignored
when --host is given. If not specified, the default
interface (the one carrying the default route) is
used.
--ipv4 SSH: Forces ssh to use IPv4 addresses only.
--ipv6 SSH: Forces ssh to use IPv6 addresses only.
--lengthy Extended reporting.
--lynis-auditor LYNIS_AUDITOR
Name of the auditor to record in the report (lynis
`--auditor`). If not specified, lynis uses its own
default.
--lynis-option LYNIS_OPTION
Additional raw option to pass to the remote `lynis
audit system` call, for options that have no dedicated
parameter here. Can be specified multiple times.
Example: `--lynis-option=--no-plugins`
--lynis-profile LYNIS_PROFILE
Profile file to use for the audit (lynis `--profile`).
The path is resolved on the target host. If not
specified, lynis uses the bundled default profile.
--lynis-skip-test LYNIS_SKIP_TEST
Lynis test ID to skip on every target (injected as
`skip-test` into the pushed profile), for fleet-wide
exceptions controlled from the monitoring
configuration. Can be specified multiple times. Host-
specific exceptions belong in the target's own
`/etc/lynis/custom.prf` instead. Example: `--lynis-
skip-test=MAIL-8818`
--lynis-source LYNIS_SOURCE
Path to a self-contained lynis directory (the
directory that contains the `lynis` executable next to
its `include`, `db` and `plugins` subdirectories).
This is the copy that gets pushed to and run on every
target host. If not specified, a self-contained copy
is assembled from the local lynis installation.
--lynis-test LYNIS_TEST
Only run these lynis tests (lynis `--tests`). Can be
specified multiple times. If not specified, all tests
are run. Example: `--lynis-test=SSH-7408 --lynis-
test=KRNL-5820`
--lynis-test-category LYNIS_TEST_CATEGORY
Only run lynis tests of these categories (lynis
`--tests-from-category`). Can be specified multiple
times. If not specified, all categories are run.
Example: `--lynis-test-category=security`
--lynis-test-group LYNIS_TEST_GROUP
Only run lynis tests of these groups (lynis `--tests-
from-group`). Can be specified multiple times. If not
specified, all groups are run. Example: `--lynis-test-
group=ssh --lynis-test-group=kernel`
--max-workers MAX_WORKERS
Maximum number of hosts to audit in parallel. Default:
10
--network NETWORK Network in CIDR notation to scan for targets via auto-
discovery. Can be specified multiple times. Takes
precedence over --interface. Example:
`--network=192.0.2.0/24`
-p, --password PASSWORD
SSH: Password authentication. NOT RECOMMENDED.
Requires `sshpass`. If you need to use password-based
SSH login, run this plugin only on trusted hosts. `ps`
will expose the SSH password.
--port PORT SSH: Port to connect to on the remote host. This can
be specified on a per-host basis in the configuration
file. Default: 22
--quiet SSH: Quiet mode. Causes most warning and diagnostic
messages to be suppressed.
--ssh-option SSH_OPTION
SSH: Can be used to give options in the format used in
the configuration file. This is useful for specifying
options for which there is no separate command-line
flag. For full details of the options, and their
possible values, see ssh_config(5). Can be specified
multiple times.
--test TEST For unit tests. Needs "path-to-stdout-file,path-to-
stderr-file,expected-retc".
-u, --username USERNAME
SSH: Username. If not specified, ssh determines the
user from `~/.ssh/config` or falls back to the current
local user.
--verbose Makes this plugin verbose during the operation. Useful
for debugging and seeing what is going on under the
hood. Default: False
-w, --warning WARN WARN threshold for the per-host hardening index
(0-100). Supports Nagios ranges. The default alerts
when the index drops below 65. Default: 65:
Usage Examples¶
Audit a single host (using a ~/.ssh/config alias):
./lynis --host=myhost
Audit every reachable host in a subnet, logging in as linuxfabrik with an explicit key, 20 hosts at a time:
./lynis --network=192.0.2.0/24 --username=linuxfabrik --identity=~/.ssh/id_ed25519 --max-workers=20
Show the per-host details and follow what the plugin is doing:
./lynis --host=myhost --lengthy --verbose
Warn below a hardening index of 70, critical below 50:
./lynis --host=myhost --warning=70: --critical=50:
Accept a finding on every target (fleet-wide), controlled from the monitoring configuration:
./lynis --network=192.0.2.0/24 --lynis-skip-test=MAIL-8818
Output of a subnet scan (without --lengthy parameter):
16/254 hosts audited
Host:Report ! Warn ! HIdx ! State
---------------------------------------+------+------+----------
app01:/var/log/lynis-report.dat ! 0 ! 71 !
app02:/var/log/lynis-report.dat ! 0 ! 72 !
proxy01:/var/log/lynis-report.dat ! 0 ! 73 !
mon01:/var/log/lynis-report.dat ! 2 ! 70 ! [WARNING]
mariadb01:/var/log/lynis-report.dat ! 0 ! 71 !
postgresql01:/var/log/lynis-report.dat ! 0 ! 71 !
redis01:/var/log/lynis-report.dat ! 0 ! 71 !
web01:/var/log/lynis-report.dat ! 0 ! 71 !
matomo01:/var/log/lynis-report.dat ! 0 ! 72 !
deploy01:/var/log/lynis-report.dat ! 0 ! 71 !
web02:/var/log/lynis-report.dat ! 0 ! 71 !
cache01:/var/log/lynis-report.dat ! 2 ! 70 ! [WARNING]
vault01:/var/log/lynis-report.dat ! 2 ! 70 ! [WARNING]
backup01:/var/log/lynis-report.dat ! 0 ! 73 !
dns01:/var/log/lynis-report.dat ! 0 ! 73 !
mail01:/var/log/lynis-report.dat ! 0 ! 72 !
A full audit takes roughly one minute per host (measured on Rocky Linux 9). Because hosts are audited in parallel (--max-workers, default 10), wall-clock time for a subnet is far lower: the scan above audited 16 reachable hosts out of a /24 in about 3 minutes.
States¶
OK if the hardening index is within the
--warning/--criticalrange and the host reports no lynis warnings.WARN if the hardening index drops below
--warning(default: 65), or the host reports at least one lynis warning.CRIT if the hardening index drops below
--critical(empty by default, so CRIT is never raised unless a threshold is set).UNKNOWN for a reachable host that could not be audited (SSH authentication failed, no executable work directory, audit produced no report, …). Hosts that do not answer on SSH are skipped silently.
--always-oksuppresses all alerts and always returns OK.
Perfdata / Metrics¶
Name |
Type |
Description |
|---|---|---|
hosts_total |
Number |
Number of target hosts considered. |
hosts_audited |
Number |
Number of hosts that were successfully audited. |
warnings |
Number |
Total number of lynis warnings across all audited hosts. |
suggestions |
Number |
Total number of lynis suggestions across all audited hosts. |
Lynis Profiles¶
Lynis reads its settings from profile files. default.prf is the default profile and ships with lynis; it defines which tests run, their thresholds, and which tests to skip. custom.prf is the place for site-specific overrides and survives package upgrades. Lynis discovers both automatically (searching /etc/lynis and the current directory) and merges them, so you do not need to edit default.prf. Use --lynis-profile to point at a specific profile file on the target instead.
Troubleshooting¶
A host shows SSH authentication failed¶
Cause: The host answered on SSH, but authentication failed. Auto-discovery connects to raw IP addresses, which do not match a Host myhost alias in ~/.ssh/config.
Solution: Pass working credentials with --username and --identity, or add a matching Host / Match block to the SSH config.
A host shows no executable (non-noexec) work directory found¶
Cause: Every candidate partition on the target is mounted noexec, so the pushed lynis script cannot be executed.
Solution: Mount one of /var/tmp, /tmp or the user’s home without noexec, or provide another writable, executable partition.
Accepting a finding you do not want to fix¶
Cause: The plugin never silences individual lynis warnings; every warning raises at least WARNING. Acceptance belongs in lynis itself, where a test is skipped when its ID is listed with skip-test= in a profile.
Solution (single host): add the test ID to the host’s custom.prf. For example, to accept this warning:
warning[]=MAIL-8818|Found some information disclosure in SMTP banner (OS or software name)|-|-|
Run on the affected host:
mkdir --parents /etc/lynis && echo 'skip-test=MAIL-8818' >> /etc/lynis/custom.prf
The next audit no longer reports MAIL-8818.
Solution (fleet-wide): pass --lynis-skip-test, which is injected into the pushed profile on every host and merged with each host’s custom.prf:
./lynis --network=192.0.2.0/24 --lynis-skip-test=MAIL-8818
Best practice: silence recurring, host- or runtime-dependent noise centrally instead of changing host roles. Findings such as ACCT-9622 / ACCT-9626 (process accounting / sysstat not installed), HRDN-7222 (a compiler is present), FIRE-4513 (iptables has no rules), LOGG-2190 (deleted files still in use), BOOT-5264 and AUTH-9282 / AUTH-9284 (password aging) are often not worth a configuration change just to satisfy the audit. Rather than installing packages, rebuilding firewall rules or reworking roles only to lift the index, decide once which of these your organisation accepts and skip them fleet-wide with --lynis-skip-test (or a shared custom.prf). Keep skip-test for consciously accepted findings; fix the rest.
Credits, License¶
Authors: Linuxfabrik GmbH, Zurich
License: The Unlicense, see LICENSE file.