Hands-on Guide to Linux Server Security Auditing and Hardening with Lynis

Security tutorial - IT technology blog
Security tutorial - IT technology blog

Context & Why You Need It

It was 2 AM when my phone buzzed violently. Prometheus alerts were firing relentlessly: CPU utilization on the production database server had spiked to 98%. SSHing in and running htop, I was greeted by dozens of unfamiliar processes constantly forking under the www-data user. The UFW firewall was active. SSH authentication logs showed no signs of brute-force attempts. At that moment, digging manually through every file in /etc/ or running netstat to inspect each port would have taken all night—and vulnerabilities would still slip through the cracks.

In a scenario like this, the fastest lifesaver is Lynis—an open-source security auditing scanner that runs directly in the Linux shell. While remote scanners like Nessus or OpenVAS only probe the external network perimeter, Lynis inspects deep from within the OS (a local scan). It audits everything: file permissions, Kernel sysctl parameters, PAM configurations, the SSH daemon, hidden cron jobs, and potential privilege escalation vectors.

Its biggest advantage? It is extremely lightweight and requires no background daemon. Lynis runs, completes its audit, and exits without chewing up RAM or degrading service performance. After a quick scan taking around two minutes, you get an immediate Hardening Index score alongside an actionable list of critical fixes.

Installation

On production servers, a golden rule is to minimize unnecessary packages. Depending on your objective—emergency incident response or long-term administration—there are two ways to deploy Lynis.

Method 1: Run Directly via Git (Recommended for Incident Response)

When you need to perform an audit late at night during an incident, adding third-party repositories to the system is best avoided. The cleanest solution is to clone the official source repository directly into /opt:

cd /opt
sudo git clone --depth=1 https://github.com/CISOfy/lynis
cd lynis
sudo chown -R root:root .
sudo chmod 700 lynis

Quickly verify the version:

sudo ./lynis show version

Method 2: Install via Package Manager (For Long-Term Management)

If you want Lynis to receive regular updates via apt on Ubuntu/Debian, configure the official CISOfy repository:

# Add CISOfy GPG key
curl -fsSL https://packages.cisofy.com/keys/cisofy-software-public.key | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/cisofy-software-public.gpg

# Add the official repository
echo "deb [arch=amd64,arm64 signed-by=/etc/apt/trusted.gpg.d/cisofy-software-public.gpg] https://packages.cisofy.com/community/lynis/deb/ stable main" | sudo tee /etc/apt/sources.list.d/cisofy-lynis.list

# Update and install
sudo apt-get update
sudo apt-get install lynis -y

Detailed Configuration

By default, lynis audit system generates hundreds of log lines that can easily overwhelm you. In practice, you should create a custom profile to suppress false positives and focus on what truly matters.

Create a Custom Profile: custom.prf

Lynis reads its base configuration from default.prf. Never edit this file directly, as your changes will be overwritten during updates. Instead, create a dedicated override file in /etc/lynis/:

sudo mkdir -p /etc/lynis
sudo nano /etc/lynis/custom.prf

Add options to speed up scans and skip irrelevant tests (such as boot loader hardware checks on a KVM virtualized VPS):

# Skip unnecessary tests on cloud/VPS
skip-test=KRNL-5695
skip-test=BOOT-5121

# Run non-interactively without waiting for Enter
quick=yes

# Output log and report paths
log_file=/var/log/lynis.log
report_file=/var/log/lynis-report.dat

Run Your First Scan

Execute a comprehensive audit with the following command:

sudo lynis audit system --profile /etc/lynis/custom.prf

The scan typically takes 1 to 3 minutes. The output concludes with a Hardening Index score (usually between 55 and 65 on a fresh Ubuntu server). Right below it are two essential sections you must review: Warnings (critical vulnerabilities) and Suggestions (recommended hardening steps).

Fixing 3 Common Vulnerabilities Found Post-Audit

During that midnight shift, Lynis pinpointed three critical vulnerabilities on my server cluster:

  1. SSH allows root login and weak password authentication (Test ID: SSH-7408):
    By default, password authentication is often left enabled for convenience—making the machine an easy target for botnets. Edit /etc/ssh/sshd_config immediately to enforce SSH key-only authentication:

    PermitRootLogin no
    PasswordAuthentication no
    MaxAuthTries 3

    Restart the SSH daemon: sudo systemctl restart sshd.
    As a quick tip, whenever provisioning new sudoer accounts for on-call engineers, I use the password generator tool at toolcraft.app/en/tools/security/password-generator to generate random strings of 24 characters or more. It runs cryptographic algorithms 100% offline via browser JavaScript, eliminating the risk of credentials leaking over the network or getting logged by untrusted online sites.

  2. Unhardened Kernel Network Parameters (Test ID: KRNL-6000):
    Default Linux kernel networking settings are susceptible to SYN flood attacks and IP spoofing. Open /etc/sysctl.d/99-security.conf and add the following parameters:

    # Protect against SYN flood attacks
    net.ipv4.tcp_syncookies = 1
    # Block IP spoofing
    net.ipv4.conf.all.rp_filter = 1
    net.ipv4.conf.default.rp_filter = 1
    # Ignore ICMP redirects to prevent MITM attacks
    net.ipv4.conf.all.accept_redirects = 0
    net.ipv6.conf.all.accept_redirects = 0
    # Disable IP forwarding (unless operating as a NAT/Router)
    net.ipv4.ip_forward = 0

    Reload the configuration immediately without rebooting:

    sudo sysctl --system
  3. Insecure write permissions on cron directory (Test ID: FILE-7524):
    Lynis flagged a script inside /etc/cron.daily/ that had mistakenly been set to 777 permissions. Any compromised service account could overwrite this script with shellcode to gain root privileges the following day. Remediate it immediately:

    sudo chmod 700 /etc/cron.daily/backup-script.sh
    sudo chown root:root /etc/cron.daily/backup-script.sh

After applying these three fixes and rescanning, the server’s Hardening Index surged from 61 to 79.

Verification & Automated Monitoring

Security is meaningless as a one-time effort. After a few weeks in production, newly installed modules or temporary permission tweaks by administrators will inevitably introduce new gaps. Automation is essential to keep your defenses intact.

Extracting Metrics from lynis-report.dat

Upon completion, Lynis writes all audit data into /var/log/lynis-report.dat in key-value format. You can quickly extract essential metrics using a few short commands:

# Count total critical warnings
grep -c "^warning\[\]" /var/log/lynis-report.dat

# View current hardening index
grep "^hardening_index=" /var/log/lynis-report.dat | cut -d '=' -f2

Automate Scans and Telegram Alerting via Script

Create a monitoring script at /usr/local/bin/auto-lynis.sh:

#!/bin/bash
set -euo pipefail

REPORT_FILE="/var/log/lynis-report.dat"
TELEGRAM_BOT_TOKEN="123456789:ABCdefGHIjklMNOpqrSTUvwxYZ"
TELEGRAM_CHAT_ID="-1001234567890"

# Run audit in non-interactive cron mode
/usr/sbin/lynis audit system --profile /etc/lynis/custom.prf --cronjob > /dev/null 2>&1

if [ -f "$REPORT_FILE" ]; then
    HARDENING_INDEX=$(grep "^hardening_index=" "$REPORT_FILE" | cut -d '=' -f2 || echo "0")
    WARNINGS_COUNT=$(grep -c "^warning\[\]" "$REPORT_FILE" || true)

    # Send alert if warnings are found or score drops
    if [ "$WARNINGS_COUNT" -gt 0 ]; then
        MESSAGE="⚠️ [SECURITY ALERT] Issue detected on server $(hostname)!%0A- Hardening Index: ${HARDENING_INDEX}/100%0A- Critical warnings: ${WARNINGS_COUNT}%0ACheck detailed log: /var/log/lynis.log"
        curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
            -d chat_id="${TELEGRAM_CHAT_ID}" \
            -d text="${MESSAGE}" > /dev/null
    fi
fi

Set file permissions and schedule a weekly cron job for 3:00 AM every Sunday:

sudo chmod 700 /usr/local/bin/auto-lynis.sh
sudo crontab -e

Append the cron schedule to the end of the file:

0 3 * * 0 /usr/local/bin/auto-lynis.sh

Now you can sleep soundly at night. If someone accidentally exposes an unauthorized port, loosens root directory permissions, or leaves behind a malicious script, the Telegram bot will sound the alarm before attackers can exploit it.

Share: