During a routine audit of a production Linux environment, I used Lynis to measure actual security posture rather than just availability. The host was stable under load, but the final report highlighted 1 warning and 37 suggestions.
In operational practice, "service up" does not equal an "acceptable attack surface." This article documents exactly what I did, step-by-step, file-by-file, and command-by-command, until completing the cycle with post-change validation and a fresh scan.
1) Baseline: running the audit and interpreting findings#
I started the collection with a snapshot of the current configuration and a full system audit using Lynis.
hostnamectl
cat /etc/os-release
uname -r
lynis --version
lynis audit system \
--verbose \
--report-file /var/log/lynis-report-$(date +%F).dat \
--log-file /var/log/lynis-$(date +%F).log
After the run, I focused my analysis on:
- Warnings: Immediate impact, prioritized as P1.
- Suggestions: Technical debt for strengthening, prioritized as P2/P3.
- Plugins: Kernel, auth, and accounting audit plugins.
The primary points addressed in the same maintenance window were:
[KRNL-5830]: Pending reboot required to activate the updated kernel.- Permissive SSH baseline.
- Misaligned password policy and umask for production environments.
- Lack of explicit blocking for unused network protocols.
auditdactive but without useful tracking rules for critical files.
2) Addressing the critical kernel warning ([krnl-5830])#
The warning indicated an updated kernel package was installed but not loaded. In this case, remediation without a reboot does not eliminate the risk.
Pre-reboot checklist:
- Verify the approved maintenance window.
- Check application status and active connections.
- Record the current version and target boot kernel.
rpm -qa | grep -E '^kernel(-core|-modules)?'
uname -r
who -b
systemctl list-units --type=service --state=running | wc -l
Execution:
reboot
Post-reboot validation:
uname -r
journalctl -b -p err --no-pager
Acceptance criteria: Running kernel matches the updated installed version, and no critical boot errors appear in the journal.
3) SSH hardening (the entry point)#
In my workflow, SSH is always at the top of the list. I adjusted /etc/ssh/sshd_config with a least-privilege policy and brute-force mitigation.
Parameters applied:
PermitRootLogin noMaxAuthTries 3LogLevel VERBOSEX11Forwarding noAllowTcpForwarding noPasswordAuthentication no(Key-only for admin accounts)ClientAliveInterval 300ClientAliveCountMax 2
Execution:
cp -a /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date +%F-%H%M)
cat >> /etc/ssh/sshd_config <<'SSHCFG'
PermitRootLogin no
MaxAuthTries 3
LogLevel VERBOSE
X11Forwarding no
AllowTcpForwarding no
PasswordAuthentication no
ClientAliveInterval 300
ClientAliveCountMax 2
SSHCFG
Validation without losing admin access:
sshd -t
systemctl reload sshd
systemctl status sshd --no-pager
Practical verification: I kept one SSH session open, tested a new session with a valid key, and confirmed the password authentication refusal.
4) Identity and password policy (login.defs + PAM)#
The AUTH section of the Lynis report pointed to a permissive stance on passwords and file creation defaults. I corrected this in two layers: password aging and default permissions.
Adjusting /etc/login.defs:
cp -a /etc/login.defs /etc/login.defs.bak.$(date +%F-%H%M)
sed -i 's/^PASS_MAX_DAYS.*/PASS_MAX_DAYS\t90/' /etc/login.defs
sed -i 's/^PASS_MIN_DAYS.*/PASS_MIN_DAYS\t1/' /etc/login.defs
sed -i 's/^PASS_WARN_AGE.*/PASS_WARN_AGE\t14/' /etc/login.defs
sed -i 's/^UMASK.*/UMASK\t027/' /etc/login.defs
In PAM, I enabled strong hashing and minimum password quality using the distribution's modules (pam_pwquality / pam_unix). Applied criteria: minlen=14, character complexity, and remember=5.
Operational validation:
grep -E 'PASS_MAX_DAYS|PASS_MIN_DAYS|PASS_WARN_AGE|UMASK' /etc/login.defs
chage -l <admin_user>
5) Memory, kernel, and network hardening (sysctl + limits)#
5.1 block core dumps#
To reduce memory exposure on disk after a process crash:
echo '* hard core 0' >> /etc/security/limits.conf
5.2 disabling unused protocols#
On the audited host, only HTTP/HTTPS and admin roles were needed. Protocols like dccp, sctp, and rds had no business justification.
cat > /etc/modprobe.d/99-hardening-protocols.conf <<'MODP'
install dccp /bin/true
install sctp /bin/true
install rds /bin/true
install tipc /bin/true
MODP
5.3 secure network parameters#
I applied a sysctl baseline to mitigate spoofing vectors and improper network stack responses.
cat > /etc/sysctl.d/99-lynis-hardening.conf <<'SYSCTL'
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.tcp_syncookies = 1
kernel.randomize_va_space = 2
SYSCTL
sysctl --system
6) Active event auditing with auditd#
Lynis identified a common gap: auditd was active but lacked coverage for critical identity files. I added specific rules to detect changes in accounts, groups, and authentication settings.
cat > /etc/audit/rules.d/99-identity-monitor.rules <<'AUDIT'
-w /etc/passwd -p wa -k identity
-w /etc/group -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/gshadow -p wa -k identity
-w /etc/sudoers -p wa -k priv_esc
-w /etc/ssh/sshd_config -p wa -k ssh_change
AUDIT
augenrules --load
systemctl restart auditd
Testing the trail:
auditctl -l
ausearch -k identity -ts today
7) Anti-malware and integrity with rkhunter#
I implemented periodic scanning with rkhunter to detect known signatures, altered files, and binary anomalies.
dnf install -y rkhunter
rkhunter --update
rkhunter --propupd
rkhunter --check --sk
8) Re-audit and result validation#
With the changes applied, I ran a fresh audit to confirm risk reduction.
Acceptance criteria Checklist:
- [x] Critical warning resolved (Kernel patched and rebooted).
- [x] Significant reduction in SSH/AUTH suggestions.
- [x] Improved hardening index compared to initial baseline.
- [x] Application services remained intact and functional.
Final sanity checks:
systemctl --failed
ss -lntup
journalctl -p err -b --no-pager
Production takeaways#
Real security on Linux isn't just about enabling tools; it's about completing the technical cycle.
In my daily workflow, Lynis serves as the engine for diagnosis and prioritization. The value comes from disciplined execution: interpreting findings, correcting with criteria, validating impact, re-auditing, and documenting evidence. This approach transforms "suggestions" into a concrete, auditable defense.
Was this article helpful?
Leave a quick reaction to help prioritize future technical guides:
This post is licensed under CC BY-NC.



Comments
Join the discussion below.
0 comments