Most Linux security guides tell you to install Lynis, run lynis audit system, glance at the hardening score, and move on. Honestly, that approach misses the point entirely. Lynis isn't a one-shot tool you run during initial setup — it's the foundation of a continuous security auditing pipeline that catches configuration drift, validates compliance controls, and generates actionable remediation data across your entire fleet.
This guide goes well beyond the basics. You'll learn how to build custom audit profiles tuned to your environment, automate fleet-wide scans with Ansible, integrate Lynis into CI/CD pipelines for container and image hardening, map findings to compliance frameworks like CIS, PCI DSS, and HIPAA, and build a remediation workflow that turns audit output into tracked, verifiable fixes. Every example uses Lynis 3.1.5 on current distributions — RHEL 9, Ubuntu 24.04 LTS, and Debian 12.
What Lynis Actually Does Under the Hood
Lynis is an open-source, host-based security auditing tool maintained by CISOfy. Unlike network scanners that probe systems remotely, Lynis runs directly on the host and performs over 300 individual tests across dozens of categories — kernel parameters, authentication mechanisms, filesystem permissions, network configuration, installed software, running services, and more.
What makes Lynis different from tools like OpenSCAP is its opportunistic scanning approach. It doesn't require a predefined security content datastream or SCAP profile. Instead, it dynamically detects what's installed on the system and tests only the relevant components. No Apache? It skips web server tests. No Docker? Container checks are bypassed entirely. This means Lynis produces useful results on any Linux system without distribution-specific configuration files.
The output breaks down into three categories:
- Warnings — Immediate security risks that need attention. These are findings like world-writable files in sensitive directories, SSH root login enabled, or running services with known vulnerabilities.
- Suggestions — Hardening improvements that would strengthen the system's security posture. These range from enabling compiler protection flags to setting stricter umask values.
- Hardening Index — A score from 0 to 100 based on the ratio of passed tests to total applicable tests. A fresh default install of most distributions scores between 55 and 65. Production servers should target 80 or above.
All findings get written to two key files: /var/log/lynis.log (detailed execution log) and /var/log/lynis-report.dat (machine-parseable key-value report data). The report file is the one you want for automation — it's designed for programmatic consumption.
Installing Lynis 3.1.5 on Modern Distributions
Distribution package repos often ship outdated versions. For the latest release, use the CISOfy official repository or install directly from source.
Method 1: CISOfy Official Repository (Recommended)
The official repository always has the latest stable release and supports Debian, Ubuntu, RHEL, CentOS, Fedora, and openSUSE.
Debian / Ubuntu
# Import the signing 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 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
# Install
sudo apt update
sudo apt install -y lynis
RHEL 9 / AlmaLinux / Rocky Linux
# Create the repository file
sudo tee /etc/yum.repos.d/cisofy-lynis.repo << 'EOF'
[cisofy-lynis]
name=CISOfy Lynis Repository
baseurl=https://packages.cisofy.com/community/lynis/rpm/
gpgcheck=1
gpgkey=https://packages.cisofy.com/keys/cisofy-software-public.key
enabled=1
EOF
# Install
sudo dnf install -y lynis
Method 2: Git Clone (Latest Development)
For environments where you can't add third-party repositories, clone directly from GitHub:
cd /opt
sudo git clone https://github.com/CISOfy/lynis.git
cd lynis
sudo ./lynis audit system
This method requires no installation — Lynis runs directly from the cloned directory. It's also the simplest way to test new releases before deploying them fleet-wide.
Verify the Installation
lynis show version
# Expected output: 3.1.5
lynis show commands
# Lists all available Lynis commands
Running Your First Comprehensive Audit
The simplest invocation runs a full system audit:
sudo lynis audit system
For non-interactive use (automation, scripts, CI/CD), add the quick flag to suppress the pause between sections:
sudo lynis audit system -Q
For a more targeted scan, Lynis supports several useful flags:
# Audit only and output in report format (no colors, no pauses)
sudo lynis audit system --no-colors --quiet
# Pentest mode — includes additional checks relevant to penetration testers
sudo lynis audit system --pentest
# Scan a remote system via SSH
lynis audit system remote 192.168.1.50
# Show only warnings (useful for quick triage)
sudo lynis show warnings
Interpreting the Output
After a scan completes, Lynis prints a summary. Here's what a typical output on an unhardened Ubuntu 24.04 server looks like:
Lynis security scan details:
Hardening index : 62 [############ ]
Tests performed : 284
Plugins enabled : 2
Components:
- Firewall [V]
- Malware scanner [X]
Scan mode:
Normal [V] Forensics [ ] Integration [ ] Pentest [ ]
Lynis modules:
- Compliance status [?]
- Security audit [V]
- Vulnerability scan [V]
Files:
- Test and samples : /opt/lynis
- Configuration file : /opt/lynis/default.prf
- Log file : /var/log/lynis.log
- Report file : /var/log/lynis-report.dat
The [X] next to "Malware scanner" means no malware scanning tool (like ClamAV or rkhunter) was detected. The [?] next to "Compliance status" indicates compliance plugins are available but not fully configured. Each of these is a chance to improve your score.
Building Custom Audit Profiles
The default Lynis profile (default.prf) is designed to work everywhere, which means it's necessarily generic. For production use, you need custom profiles that reflect your organization's actual security requirements, system roles, and acceptable risk tolerances.
How Profiles Work
Lynis loads default.prf first, then overlays any settings from custom.prf in the same directory. You never edit default.prf directly — all customizations go into custom.prf, which overrides matching settings.
# Create your custom profile
sudo touch /etc/lynis/custom.prf
Profile Configuration for a Web Server
Here's a practical custom profile for a production web server running Nginx:
# /etc/lynis/custom.prf — Web Server Production Profile
# Define the machine role for context-aware testing
machine-role=server
# Set compliance standards to audit against
compliance-standards=cis,pci-dss
# Skip tests that are irrelevant for a headless web server
skip-test=FIRE-4540 # Skip iptables check (using nftables)
skip-test=MAIL-8802 # No local mail delivery expected
skip-test=PRNT-2307 # No print services
skip-test=PRNT-2308 # No CUPS daemon
skip-test=SNMP-3302 # SNMP not in use
# Enable relevant plugins
plugin=crypto
plugin=file-integrity
plugin=firewalls
plugin=nginx
# Log settings
log-tests-incorrect-os=no
# Minimum hardening index threshold (useful for CI gates)
# Below this score, consider the audit a failure
minimum-score=75
Profile for a Database Server
# /etc/lynis/custom.prf — Database Server Profile
machine-role=server
compliance-standards=cis,hipaa,pci-dss
# Skip web-server and container checks
skip-test=HTTP-6622
skip-test=HTTP-6624
skip-test=HTTP-6640
skip-test=HTTP-6641
skip-test=HTTP-6702
skip-test=CONT-8004 # Docker daemon check
skip-test=CONT-8106 # Docker info check
# Enable database and crypto plugins
plugin=crypto
plugin=file-integrity
# Strict minimum for compliance environments
minimum-score=80
Verifying Your Profile
After creating a custom profile, verify that Lynis picks it up:
# Show active profiles
lynis show profiles
# Show all active settings (including overrides from custom.prf)
lynis show settings --brief
# Dry run to see which tests would be skipped
sudo lynis audit system --quick --verbose 2>&1 | grep "Skipped"
Mapping Lynis Findings to Compliance Frameworks
One of Lynis's most underutilized features (and I've seen this firsthand at multiple organizations) is its compliance mapping capability. When you set compliance-standards in your profile, Lynis annotates findings with the specific framework controls they relate to.
Supported Frameworks
| Framework | Profile Value | Use Case |
|---|---|---|
| CIS Benchmarks | cis | General server hardening baseline |
| PCI DSS | pci-dss | Payment card processing environments |
| HIPAA | hipaa | Healthcare data protection |
| ISO 27001 | iso27001 | Information security management |
Extracting Compliance Data from Reports
The machine-readable report at /var/log/lynis-report.dat contains compliance-tagged findings that you can parse programmatically:
# Extract all PCI DSS related findings
grep "compliance_pci_dss" /var/log/lynis-report.dat
# Extract compliance suggestions with their framework mapping
grep "suggestion\[\]" /var/log/lynis-report.dat | head -20
# Count findings per severity
grep "warning\[\]" /var/log/lynis-report.dat | wc -l
grep "suggestion\[\]" /var/log/lynis-report.dat | wc -l
Building a Compliance Report Script
Here's a script that generates a structured compliance summary from Lynis output:
#!/bin/bash
# lynis-compliance-report.sh — Generate a compliance summary from Lynis report data
set -euo pipefail
REPORT="/var/log/lynis-report.dat"
OUTPUT="/var/log/lynis-compliance-summary.txt"
if [[ ! -f "$REPORT" ]]; then
echo "Error: Lynis report not found. Run 'sudo lynis audit system' first."
exit 1
fi
{
echo "=== Lynis Compliance Summary ==="
echo "Generated: $(date -u +'%Y-%m-%dT%H:%M:%SZ')"
echo "Hostname: $(hostname -f)"
echo ""
# Extract hardening index
SCORE=$(grep "hardening_index" "$REPORT" | cut -d= -f2)
echo "Hardening Index: ${SCORE}/100"
echo ""
# Count findings
WARNINGS=$(grep -c "^warning\[\]" "$REPORT" || true)
SUGGESTIONS=$(grep -c "^suggestion\[\]" "$REPORT" || true)
echo "Warnings: $WARNINGS"
echo "Suggestions: $SUGGESTIONS"
echo ""
# List all warnings with test IDs
echo "=== Warnings (Immediate Action Required) ==="
grep "^warning\[\]" "$REPORT" | while IFS= read -r line; do
TEST_ID=$(echo "$line" | cut -d'|' -f2)
DESCRIPTION=$(echo "$line" | cut -d'|' -f3)
echo " [$TEST_ID] $DESCRIPTION"
done
echo ""
echo "=== Top 10 Hardening Suggestions ==="
grep "^suggestion\[\]" "$REPORT" | head -10 | while IFS= read -r line; do
TEST_ID=$(echo "$line" | cut -d'|' -f2)
DESCRIPTION=$(echo "$line" | cut -d'|' -f3)
echo " [$TEST_ID] $DESCRIPTION"
done
} > "$OUTPUT"
echo "Compliance summary written to $OUTPUT"
Automating Lynis Scans with Cron and Systemd Timers
Running Lynis manually defeats the purpose. Security posture changes continuously — package updates, configuration changes, new services, user modifications — and your auditing needs to keep pace.
Option 1: Cron Job
The simplest automation is a daily cron job that runs Lynis and archives the results:
# /etc/cron.d/lynis-audit
# Run a security audit daily at 03:00 UTC and archive results
0 3 * * * root /usr/bin/lynis audit system --cronjob --quiet 2>&1 | /usr/bin/logger -t lynis-audit
The --cronjob flag is purpose-built for unattended execution. It disables colors, skips interactive pauses, and adjusts output formatting for log ingestion. Combined with --quiet, it produces clean, parseable output.
Option 2: Systemd Timer (Preferred)
Systemd timers offer better logging integration, failure handling, and monitoring capabilities than cron. I'd recommend this approach for most production setups:
# /etc/systemd/system/lynis-audit.service
[Unit]
Description=Lynis Security Audit
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStartPre=/bin/mkdir -p /var/log/lynis-archives
ExecStart=/usr/bin/lynis audit system --cronjob --quiet
ExecStartPost=/bin/bash -c 'cp /var/log/lynis-report.dat /var/log/lynis-archives/lynis-report-$(date +%%Y%%m%%d).dat'
Nice=19
IOSchedulingClass=idle
# /etc/systemd/system/lynis-audit.timer
[Unit]
Description=Daily Lynis Security Audit
[Timer]
OnCalendar=*-*-* 03:00:00
RandomizedDelaySec=1800
Persistent=true
[Install]
WantedBy=timers.target
# Enable and start the timer
sudo systemctl daemon-reload
sudo systemctl enable --now lynis-audit.timer
# Verify the timer is active
systemctl list-timers lynis-audit.timer
The RandomizedDelaySec=1800 prevents all servers in a fleet from running their audit at exactly the same time — which could create unnecessary load spikes on shared infrastructure. Persistent=true ensures that if a server was powered off during the scheduled time, the audit runs as soon as it boots back up.
Score Tracking Over Time
Archiving the report data file after each scan lets you track your hardening score over time and detect drift. This is one of those things that seems optional until you actually need it during an incident review:
#!/bin/bash
# lynis-score-tracker.sh — Track hardening index over time
ARCHIVE_DIR="/var/log/lynis-archives"
SCORE_LOG="$ARCHIVE_DIR/score-history.csv"
# Initialize CSV if it does not exist
if [[ ! -f "$SCORE_LOG" ]]; then
echo "date,hostname,score,warnings,suggestions" > "$SCORE_LOG"
fi
REPORT="/var/log/lynis-report.dat"
DATE=$(date -u +"%Y-%m-%d")
HOSTNAME=$(hostname -f)
SCORE=$(grep "hardening_index" "$REPORT" | cut -d= -f2)
WARNINGS=$(grep -c "^warning\[\]" "$REPORT" || true)
SUGGESTIONS=$(grep -c "^suggestion\[\]" "$REPORT" || true)
echo "$DATE,$HOSTNAME,$SCORE,$WARNINGS,$SUGGESTIONS" >> "$SCORE_LOG"
Fleet-Wide Auditing with Ansible
For environments with more than a handful of servers, running Lynis manually or even via individual cron jobs becomes unmanageable. Ansible lets you deploy Lynis, push custom profiles, run audits, and collect results from your entire fleet in a single playbook run.
Ansible Playbook: Deploy, Configure, and Audit
---
# lynis-fleet-audit.yml — Deploy Lynis and run security audits across all hosts
- name: Lynis Fleet Security Audit
hosts: all
become: true
vars:
lynis_version: "3.1.5"
lynis_install_dir: "/opt/lynis"
lynis_report_dir: "/var/log/lynis-archives"
lynis_min_score: 75
fetch_reports_to: "./audit-reports"
tasks:
- name: Install Lynis from git
ansible.builtin.git:
repo: "https://github.com/CISOfy/lynis.git"
dest: "{{ lynis_install_dir }}"
version: "{{ lynis_version }}"
force: true
- name: Create report archive directory
ansible.builtin.file:
path: "{{ lynis_report_dir }}"
state: directory
mode: "0750"
owner: root
group: root
- name: Deploy custom Lynis profile
ansible.builtin.copy:
src: "files/custom.prf"
dest: "{{ lynis_install_dir }}/custom.prf"
mode: "0640"
owner: root
group: root
- name: Run Lynis security audit
ansible.builtin.command:
cmd: "{{ lynis_install_dir }}/lynis audit system --cronjob --quiet"
register: lynis_result
changed_when: false
- name: Archive report with date stamp
ansible.builtin.copy:
src: "/var/log/lynis-report.dat"
dest: "{{ lynis_report_dir }}/lynis-report-{{ ansible_date_time.date }}.dat"
remote_src: true
mode: "0640"
- name: Extract hardening index
ansible.builtin.shell:
cmd: "grep 'hardening_index' /var/log/lynis-report.dat | cut -d= -f2"
register: hardening_score
changed_when: false
- name: Display audit results
ansible.builtin.debug:
msg: "{{ inventory_hostname }}: Hardening Index = {{ hardening_score.stdout }}/100"
- name: Fail if score is below threshold
ansible.builtin.assert:
that:
- hardening_score.stdout | int >= lynis_min_score
fail_msg: "SECURITY ALERT: {{ inventory_hostname }} scored {{ hardening_score.stdout }}/100 (minimum: {{ lynis_min_score }})"
success_msg: "{{ inventory_hostname }} meets minimum hardening threshold"
- name: Fetch reports to control node
ansible.builtin.fetch:
src: "/var/log/lynis-report.dat"
dest: "{{ fetch_reports_to }}/{{ inventory_hostname }}/lynis-report.dat"
flat: true
Run this playbook against your inventory and every server gets audited, scored, and checked against your minimum threshold in one command:
ansible-playbook -i inventory/production lynis-fleet-audit.yml
The assert task is critical here — it fails the playbook for any host that scores below your threshold, which means you can wire this into your change management process. No deployment proceeds until the target server meets the minimum hardening bar.
Deploying the Systemd Timer via Ansible
To set up ongoing automated audits across your fleet, extend the playbook with tasks that deploy the systemd timer unit files and enable them:
- name: Deploy Lynis audit service unit
ansible.builtin.template:
src: "templates/lynis-audit.service.j2"
dest: "/etc/systemd/system/lynis-audit.service"
mode: "0644"
notify: reload systemd
- name: Deploy Lynis audit timer unit
ansible.builtin.template:
src: "templates/lynis-audit.timer.j2"
dest: "/etc/systemd/system/lynis-audit.timer"
mode: "0644"
notify: reload systemd
- name: Enable and start Lynis audit timer
ansible.builtin.systemd:
name: lynis-audit.timer
state: started
enabled: true
daemon_reload: true
handlers:
- name: reload systemd
ansible.builtin.systemd:
daemon_reload: true
Integrating Lynis into CI/CD Pipelines
Lynis isn't just a production tool — it's equally valuable in CI/CD pipelines where you can audit container images, validate base images, and enforce hardening thresholds before anything reaches staging or production.
GitHub Actions: Audit a Docker Image
This workflow runs Lynis inside a freshly built container image and fails the pipeline if the hardening score drops below 70:
# .github/workflows/security-audit.yml
name: Lynis Security Audit
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
lynis-audit:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Build container image
run: docker build -t app-image:test .
- name: Run Lynis audit inside container
run: |
docker create --name audit-target app-image:test sleep infinity
docker start audit-target
docker exec audit-target apt-get update -qq
docker exec audit-target apt-get install -y -qq git
docker exec audit-target git clone --depth 1 https://github.com/CISOfy/lynis.git /opt/lynis
docker exec audit-target /opt/lynis/lynis audit system --cronjob --quiet --no-colors
docker cp audit-target:/var/log/lynis-report.dat ./lynis-report.dat
docker stop audit-target
- name: Check hardening score
run: |
SCORE=$(grep "hardening_index" lynis-report.dat | cut -d= -f2)
echo "Hardening Index: $SCORE/100"
if [ "$SCORE" -lt 70 ]; then
echo "::error::Hardening score $SCORE is below threshold of 70"
grep "^warning\[\]" lynis-report.dat || true
exit 1
fi
- name: Upload Lynis report
uses: actions/upload-artifact@v4
if: always()
with:
name: lynis-report
path: lynis-report.dat
retention-days: 30
GitLab CI: Security Quality Gate
# .gitlab-ci.yml
lynis-audit:
stage: test
image: debian:12-slim
script:
- apt-get update -qq && apt-get install -y -qq git
- git clone --depth 1 https://github.com/CISOfy/lynis.git /opt/lynis
- /opt/lynis/lynis audit system --cronjob --quiet --no-colors
- SCORE=$(grep "hardening_index" /var/log/lynis-report.dat | cut -d= -f2)
- echo "Hardening Index $SCORE/100"
- "[ \"$SCORE\" -ge 70 ] || (echo 'Score below threshold' && exit 1)"
artifacts:
paths:
- /var/log/lynis-report.dat
when: always
expire_in: 30 days
Using Lynis as a CI/CD security gate means every container image and base system configuration gets validated against a known hardening standard before it can progress through your pipeline. Combined with tools like Trivy for CVE scanning, this creates a solid defense-in-depth approach to image security.
Interpreting Key Test Categories and Remediation
Knowing which tests matter most helps you prioritize remediation effectively. Here are the categories where the biggest security wins typically come from, along with specific remediation actions.
SSH Hardening (SSH-7408)
Lynis evaluates your SSH daemon configuration against current best practices. Common findings include weak key exchange algorithms, permissive authentication settings, and missing hardening options.
# /etc/ssh/sshd_config.d/hardening.conf
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
X11Forwarding no
MaxAuthTries 3
AllowAgentForwarding no
AllowTcpForwarding no
ClientAliveInterval 300
ClientAliveCountMax 2
LoginGraceTime 30
# Strong key exchange and cipher configuration
KexAlgorithms [email protected],[email protected]
Ciphers [email protected],[email protected]
MACs [email protected],[email protected]
Kernel Hardening (KRNL-6000)
Lynis checks sysctl parameters for network stack hardening, ASLR, and other kernel security settings. These are some of the easiest wins you can get:
# /etc/sysctl.d/99-lynis-hardening.conf
# Prevent IP spoofing
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# Disable ICMP redirects
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
# Disable source routing
net.ipv4.conf.all.accept_source_route = 0
# Enable TCP SYN cookies
net.ipv4.tcp_syncookies = 1
# Enable full ASLR
kernel.randomize_va_space = 2
# Restrict dmesg access
kernel.dmesg_restrict = 1
# Restrict kernel pointer exposure
kernel.kptr_restrict = 2
# Restrict BPF JIT compiler
net.core.bpf_jit_harden = 2
# Apply immediately
sudo sysctl --system
File Permissions (FILE-6310 through FILE-6374)
Lynis identifies world-writable files, files without owners, and SUID/SGID binaries that may pose security risks:
# Find and review all SUID binaries flagged by Lynis
grep "suid_file" /var/log/lynis-report.dat
# Review and remove unnecessary SUID bits
sudo chmod u-s /usr/bin/unnecessary-suid-binary
# Set sticky bit on world-writable directories
sudo chmod +t /tmp
sudo chmod +t /var/tmp
Authentication (AUTH-9262 through AUTH-9408)
These tests cover password policies, PAM configuration, and account security:
# /etc/login.defs — strengthen password policies
PASS_MAX_DAYS 90
PASS_MIN_DAYS 7
PASS_MIN_LEN 14
PASS_WARN_AGE 14
# Lock accounts with no password set
sudo awk -F: '($2 == "" ) {print $1}' /etc/shadow | while read user; do
sudo usermod -L "$user"
echo "Locked passwordless account: $user"
done
# Set default umask
echo "umask 027" | sudo tee -a /etc/profile.d/hardening.sh
Firewall (FIRE-4512 through FIRE-4590)
Lynis checks whether a firewall is active, what the default policies are, and whether unnecessary ports are open. If you're using nftables (the modern replacement for iptables):
# Verify nftables is active
sudo nft list ruleset
# Ensure default deny policies exist
sudo nft add table inet filter
sudo nft add chain inet filter input '{ type filter hook input priority 0; policy drop; }'
sudo nft add chain inet filter forward '{ type filter hook forward priority 0; policy drop; }'
sudo nft add chain inet filter output '{ type filter hook output priority 0; policy accept; }'
# Allow established connections
sudo nft add rule inet filter input ct state established,related accept
# Allow SSH
sudo nft add rule inet filter input tcp dport 22 accept
Advanced: Writing Custom Lynis Tests
So, you've mastered the built-in tests and want more. Lynis supports custom test plugins that extend the built-in checks with organization-specific security requirements. Custom tests live in the custom_tests.d directory within the Lynis installation.
#!/bin/sh
# /opt/lynis/custom_tests.d/custom-check-banner.sh
# Custom test: Verify login banner exists and contains required legal text
CUSTOM_TEST_ID="CUST-0001"
BANNER_FILE="/etc/issue.net"
REQUIRED_TEXT="authorized"
if [ -f "$BANNER_FILE" ]; then
if grep -qi "$REQUIRED_TEXT" "$BANNER_FILE"; then
LogText "Result: Login banner contains required legal text"
Display --indent 4 --text "Login banner content" --result "OK" --color GREEN
AddHP 3 3
else
LogText "Result: Login banner missing required legal text"
Display --indent 4 --text "Login banner content" --result "WARNING" --color RED
ReportWarning "$CUSTOM_TEST_ID" "Login banner does not contain required legal notice"
AddHP 0 3
fi
else
LogText "Result: No login banner file found"
ReportWarning "$CUSTOM_TEST_ID" "No login banner file ($BANNER_FILE) found"
AddHP 0 3
fi
Custom tests are automatically picked up on the next audit run. This is powerful for enforcing organization-specific policies — mandatory login banners, required software versions, specific configuration values — that aren't covered by the standard Lynis test set.
Lynis vs. OpenSCAP: When to Use Which
Both tools perform security auditing, but they serve different purposes and work best in different contexts. Understanding when to use each one — or both together — gives you the strongest coverage.
| Feature | Lynis | OpenSCAP |
|---|---|---|
| Approach | Opportunistic — tests what it finds | Policy-driven — evaluates against SCAP profiles |
| Configuration | Works out of the box, no content files needed | Requires SCAP datastream and profile selection |
| Compliance | Tags findings with framework references | Full SCAP/XCCDF evaluation with pass/fail per rule |
| Remediation | Suggestions in log output | Generates Ansible/Bash remediation scripts |
| Distributions | Works on any Linux/Unix | Profiles available for specific distro versions |
| Best For | General auditing, drift detection, CI/CD gates | Formal compliance audits, regulatory reporting |
The strongest security posture uses both: Lynis for continuous, broad security auditing and drift detection, and OpenSCAP for formal, point-in-time compliance evaluations against specific CIS benchmark profiles. They complement each other rather than competing.
Frequently Asked Questions
What is a good Lynis hardening score for production servers?
Aim for 80 or above on production servers. A freshly installed, unhardened Linux distribution typically scores between 55 and 65. After applying the kernel, SSH, authentication, and firewall hardening covered in this guide, most systems reach the low-to-mid 80s. Scores above 90 are achievable but may require trade-offs with functionality — evaluate each remaining suggestion against your operational requirements rather than chasing a perfect score.
Can Lynis break my system or make changes automatically?
No. Lynis is a read-only auditing tool. It examines configurations, checks file permissions, and evaluates system settings, but it never modifies anything. All remediation is manual — you review the suggestions and decide what to apply. This makes it safe to run on production systems at any time, including during business hours.
How often should I run Lynis security audits?
Run automated daily scans via cron or systemd timers as a baseline. For systems in compliance-regulated environments (PCI DSS, HIPAA), daily scans provide the continuous monitoring that auditors expect. Additionally, run on-demand scans after any significant change — package updates, configuration modifications, new service deployments, or kernel upgrades — to verify the change didn't introduce regressions in your security posture.
Does Lynis work inside Docker containers and on container images?
Yes. Lynis can run inside a container to audit the container's configuration and base image. It adapts its tests automatically, skipping checks that are irrelevant in a container context (like bootloader configuration). This makes it great for CI/CD pipelines where you want to validate container images before deployment. Clone Lynis into the container, run the audit, and extract the report file for analysis.
How is Lynis different from vulnerability scanners like Trivy or OpenVAS?
Lynis focuses on configuration auditing and hardening assessment — it checks how your system is configured, not which CVEs affect installed packages. Trivy scans for known vulnerabilities in packages and container layers, while OpenVAS performs network-based vulnerability scanning. They're complementary tools: Lynis catches misconfigurations and hardening gaps, Trivy catches vulnerable packages, and OpenVAS catches network-exposed weaknesses. A mature security program uses all three.