Ubuntu Server Security Auditing with Lynis: Vulnerability Scanning and Practical Hardening

Ubuntu tutorial - IT technology blog
Ubuntu tutorial - IT technology blog

Quick start: Server security scan in 5 minutes

Just spun up a VPS and not sure what vulnerabilities exist? Run the commands below immediately. The Lynis package in Ubuntu’s default repository is usually outdated, so fetching it directly from the official CISOfy repository is the best approach.

# 1. Add the official CISOfy repository
sudo apt update && sudo apt install -y curl apt-transport-https
curl -fsSL https://packages.cisofy.com/keys/cisofy-software-public.key | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/cisofy.gpg
echo "deb [arch=amd64] https://packages.cisofy.com/community/lynis/deb/ stable main" | sudo tee /etc/apt/sources.list.d/cisofy-lynis.list

# 2. Install Lynis
sudo apt update
sudo apt install -y lynis

# 3. Run a full system audit
sudo lynis audit system --quick

The --quick flag lets the process run seamlessly without pausing for user input. After about 2 to 3 minutes, the terminal will display your Hardening index score on a 100-point scale, along with a list of Warnings (high risk) and Suggestions (remediation tips).

How Lynis works and how to interpret results

Lynis is a host-based security auditing tool. Unlike Nmap, which only scans network ports externally, Lynis runs directly inside the OS with root privileges. It deeply inspects the kernel, sensitive file permissions, system accounts, systemd services, SSH configuration, and missing security patches.

When I first migrated from CentOS to Ubuntu, I assumed that simply having UFW and Fail2ban enabled meant the server was secure. But when I ran Lynis for the first time on a machine hosting a Node.js web app, the Hardening Index scored a mere 58/100. The tool exposed a wide range of loose configurations that are easily overlooked.

Reading log files and categorizing risk levels

Every time a scan finishes, the system writes two critical files:

  • /var/log/lynis.log: Contains detailed output for each executed test and command.
  • /var/log/lynis-report.dat: A concise machine-readable report containing technical parameters and suggestion IDs.

Instead of wading through thousands of lines of raw logs, you can quickly grep for the two core sets of data:

# Filter critical red warnings
sudo grep -E "^Warning:" /var/log/lynis.log

# Filter all recommendations with their test IDs
sudo grep -E "^Suggestion:" /var/log/lynis.log

Each suggestion is tied to a specific identifier, such as SSH-7408 or FILE-7524. To view detailed remediation guidance directly from the author, run:

lynis show details SSH-7408

3 most common recommendations on Ubuntu

Across a fleet of 8 production servers, I found these 3 configuration areas consistently lower scores the most:

  • Kernel hardening (KRNL-5830): The /etc/sysctl.conf file has not enabled protection against IP spoofing or SYN flood exhaustion.
  • SSH configuration (SSH-7408): Port forwarding remains enabled, ClientAliveInterval 300 is not enforced, or weak ciphers are still permitted.
  • Permissive /tmp partitions: The /tmp and /var/tmp directories are mounted without the noexec,nosuid,nodev flags. An attacker could drop malicious scripts here and execute them directly.

Automating audit schedules and alerts

Running a manual scan once and forgetting about it will not solve security drift. Every code deployment, user modification, or package installation can alter your security baseline. Scheduling automated audits during off-peak hours gives you continuous risk visibility.

Creating a custom profile to filter false positives

Every server has a dedicated role. A pure Nginx web server obviously does not need checks for Apache or CUPS print services. Create a custom profile to bypass unnecessary tests:

sudo nano /etc/lynis/custom.prf

Add the following concise configuration:

# Skip Apache tests on Nginx-only servers
skip-test=HTTP-6622

# Skip printer service tests (CUPS)
skip-test=PRNT-3802

# Silence warnings without a clear description
silence_warnings_without_description=yes

Setting up a weekly audit cron job

Create a shell script to run Lynis in cron mode (disabling ANSI colors and terminal interaction) and log any detected warnings:

sudo nano /usr/local/bin/run-lynis-audit.sh

Script content:

#!/bin/bash
LOG_FILE="/var/log/lynis-cron.log"
DATE=$(date +'%Y-%m-%d')

# Run background audit using the custom profile
/usr/sbin/lynis audit system --cronjob --profile /etc/lynis/custom.prf > "$LOG_FILE"

# Count new warnings in the log
WARNINGS=$(grep -c "^Warning:" /var/log/lynis.log)
if [ "$WARNINGS" -gt 0 ]; then
    echo "[!] $DATE: Detected $WARNINGS Lynis security warnings!" >> /var/log/security-alerts.log
fi

Make the script executable and schedule it to run every Monday at 3:00 AM:

sudo chmod +x /usr/local/bin/run-lynis-audit.sh
echo "0 3 * * 1 root /usr/local/bin/run-lynis-audit.sh" | sudo tee /etc/cron.d/lynis-weekly

Production takeaways and best practices

After more than six months of running Lynis across staging and production fleets, here are the key lessons learned:

  • Do not obsess over a 100/100 score: An ideal score sits comfortably between 75 and 85. Pushing past 90 requires aggressive kernel hardening and overly restrictive permissions that can easily break the Docker daemon, Node.js runtimes, or GitLab runners.
  • Tweak sysctl with caution: Setting reverse path filtering via net.ipv4.conf.all.rp_filter = 1 frequently clashes with Docker network bridges or WireGuard VPNs. Always test thoroughly in development before rolling it out to production.
  • Monitor file integrity with AIDE: Lynis only checks configuration weaknesses at the moment of scanning. To detect unauthorized modifications to system files under /etc or /bin, pair it with AIDE or Tripwire.
  • Centralize your logs: If you manage three or more nodes, avoid manually checking lynis-report.dat on each server. Stream metrics directly to Grafana Loki or a centralized rsyslog server for centralized monitoring and dashboards.
Share: