Lynis: Host-Based Hardening Audit

Lynis remains the fastest way to get a CIS-style hardening score on a running system. It ships as a single shell script with no runtime dependencies, which matters when you are auditing a locked-down bastion host that has no package manager access.

We tested Lynis 3.1.1 on Ubuntu 24.04, RHEL 9.3, and Debian 12. The hardening index score varies from 58 on a default cloud image to 79 after applying the tool's own suggestions. Run the full audit with:

The report lands in /var/log/lynis.log and the more useful machine-readable version in /var/log/lynis-report.dat. Grep that file for warning= and suggestion= lines to feed directly into a remediation script.

Lynis checks SSH config, PAM settings, file permissions, kernel parameters, boot loader integrity, and installed package vulnerability status. It does not scan containers or network topology - it is strictly a host analysis tool. For CI pipelines, pipe the exit code: Lynis exits 0 on clean runs and non-zero when warnings are present, which integrates cleanly with Jenkins or GitHub Actions.

sudo lynis audit system --quiet --no-colors 2>&1 | tee /tmp/lynis-$(hostname)-$(date +%F).txt

OpenSCAP: Compliance-Driven Auditing

OpenSCAP with the SCAP Security Guide (SSG) is the correct tool when you need to demonstrate compliance against a named benchmark - CIS Level 1/2, DISA STIG, or PCI-DSS. Lynis gives you a score; OpenSCAP gives you a signed HTML report you can hand to an auditor.

Install on RHEL 9: `sudo dnf install openscap-scanner scap-security-guide`. The SSG profiles live in /usr/share/xml/scap/ssg/content/. Run a CIS Level 2 scan against RHEL 9:

The resulting report is an XHTML file with pass/fail per rule, CVE references, and remediation Bash snippets. The --remediate flag on oscap will apply fixes automatically, but test that on a non-production host first - some remediations are aggressive (disabling USB storage, enforcing single-user mode password).

SSG profiles are actively maintained and cover Fedora, Ubuntu, Debian, RHEL, and container base images. The benchmark versions lag slightly behind upstream CIS releases - check the profile version string inside the XML with `oscap info /usr/share/xml/scap/ssg/content/ssg-rhel9-ds.xml` before reporting compliance.

oscap xccdf eval \
  --profile xccdf_org.ssgproject.content_profile_cis_server_l2 \
  --results /tmp/scap-results.xml \
  --report /tmp/scap-report.html \
  /usr/share/xml/scap/ssg/content/ssg-rhel9-ds.xml

Trivy: Container and IaC Vulnerability Scanning

Trivy 0.51+ from Aqua Security has become the default container scanner in most pipelines we manage. It scans OS packages, language-specific dependencies (go.sum, package-lock.json, requirements.txt), Terraform, Helm charts, and Kubernetes manifests in a single binary with no daemon required.

Scanning a local image takes under 30 seconds for most images. The --severity flag lets you fail CI on CRITICAL or HIGH only:

Trivy pulls its vulnerability database from GitHub on first run and caches it locally. On air-gapped systems, download the DB bundle manually: `trivy image --download-db-only` on a connected host, then transfer the ~/.cache/trivy directory. The DB updates every 6 hours in connected environments.

For IaC scanning, `trivy config ./terraform/` checks Terraform files against 150+ misconfiguration checks including open security groups, unencrypted S3 buckets, and missing IMDSv2 enforcement. This is where Trivy overlaps with Checkov, but Trivy's unified output format - JSON, SARIF, or table - makes it easier to consolidate into a single reporting pipeline.

trivy image --severity HIGH,CRITICAL --exit-code 1 --format json \
  --output /tmp/trivy-$(date +%F).json \
  your-registry.io/yourapp:latest
// advertisement

Falco: Runtime Threat Detection

Falco 0.38 by the CNCF watches kernel system calls via eBPF and fires alerts when runtime behavior matches threat rules - a container spawning a shell, a process writing to /etc/passwd, or a binary executing from /tmp. This is the only tool in this list that catches attacks in progress rather than auditing static configuration.

Deploy Falco as a DaemonSet in Kubernetes or as a systemd service on bare metal. The eBPF driver requires kernel 4.14+ with BTF enabled. Check BTF availability: `ls /sys/kernel/btf/vmlinux`. If the file exists, the modern eBPF probe works without compiling kernel headers.

Falco ships with ~100 default rules. The rule below detects any shell spawned inside a container, which should never happen in production:

Falco outputs to stdout, syslog, or a gRPC sink. In production we route via Falcosidekick to Slack and Elasticsearch. The signal-to-noise ratio depends entirely on rule tuning - default rules on a busy cluster generate hundreds of alerts per hour until you whitelist legitimate maintenance operations. Plan two weeks of tuning before treating Falco alerts as actionable.

- rule: Shell Spawned in Container
  desc: A shell was spawned in a container
  condition: container and spawned_process and shell_procs and not known_shell_spawn_binaries
  output: >-
    Shell spawned in container (user=%user.name container=%container.name
    image=%container.image.repository cmd=%proc.cmdline)
  priority: WARNING
  tags: [container, shell, mitre_execution]

Nmap and Nmap Scripting Engine: Network Exposure Audit

Host hardening means nothing if a misconfigured firewall exposes services you thought were internal. Nmap 7.95 with the NSE (Nmap Scripting Engine) covers network-layer auditing that none of the above tools touch.

Run a full TCP SYN scan with service version detection and the vuln script category against your own network range:

The vuln script category runs ~100 scripts checking for specific CVEs, weak SSL ciphers, open Redis without auth, anonymous FTP, and SMB misconfigurations. On a Class C subnet, expect this scan to take 10-20 minutes. Use -T3 (default timing) rather than -T5 in production to avoid triggering IDS alerts on your own infrastructure.

For ongoing network monitoring rather than point-in-time audits, combine Nmap with ndiff to diff scan results between runs: `ndiff scan-2025-01-01.xml scan-2025-02-01.xml`. New open ports between scans appear as additions - this catches shadow IT services before they become incidents.

The `ssl-enum-ciphers` script specifically outputs a grade (A through F) per service, which is useful for TLS posture checks without standing up a full SSL Labs-equivalent tool internally.

sudo nmap -sS -sV -p- --script vuln \
  -oA /tmp/nmap-audit-$(date +%F) \
  192.168.1.0/24

Chkrootkit and RKHunter: Malware and Rootkit Detection

Chkrootkit 0.58 and RKHunter 1.4.6 are host integrity checkers that look for signs of rootkit installation - modified system binaries, hidden processes, suspicious network listeners, and known rootkit file signatures.

Neither tool is a real-time scanner. Run them from cron weekly and store output off-host immediately - a rootkit can modify both tools' output if it detects them running. The recommended pattern is to pipe output to a remote syslog server in real time.

RKHunter is more actively maintained and has a broader signature database. Run a baseline after a clean install, save the file property database, and compare on subsequent runs:

Chkrootkit is faster and catches some things RKHunter misses in the network stack - specifically, checks for promiscuous interfaces and LKM-based packet sniffers. Running both takes under 5 minutes total and the overlap is not redundant enough to justify dropping either.

Important caveat: both tools generate false positives on modern systems. Perl, Python, and some system utilities regularly trigger `strings` checks. Maintain a whitelist file and review it after every OS update. A non-reviewed whitelist that accumulates real rootkit signatures is worse than not running these tools at all.

sudo rkhunter --update && \
sudo rkhunter --check --skip-keypress --report-warnings-only \
  --logfile /var/log/rkhunter-$(date +%F).log
// advertisement

Grype: Dependency and SBOM Vulnerability Scanning

Grype from Anchore fills the gap between Trivy's container focus and source-level dependency auditing. Grype 0.79+ generates an SBOM (Software Bill of Materials) via its companion tool Syft and then scans it for CVEs across 12 vulnerability databases simultaneously.

The practical advantage over Trivy for some workflows is that Grype can scan a directory of source code or a compiled binary directly, not just container images:

Syft generates the SBOM in CycloneDX or SPDX format, which satisfies the SBOM requirements in NIST SP 800-218 and Executive Order 14028. If you are shipping software to US federal agencies or large enterprise customers, generating and signing SBOMs with `cosign` is increasingly a contractual requirement.

Grype's database combines NVD, GitHub Advisory, OSV, and vendor-specific feeds. The match rate on Go binaries is particularly strong because Grype reads the embedded module metadata that `go build` includes by default since Go 1.18.

syft dir:./src -o cyclonedx-json > sbom.json && \
grype sbom:./sbom.json --fail-on high

Wazuh: Centralized SIEM and Compliance Manager

Wazuh 4.9 is the tool you add when you need audit results aggregated across 50+ hosts rather than reviewed per-host. It ingests Lynis output, file integrity monitoring data, log analysis, and vulnerability scan results into a central manager with a Kibana-based dashboard.

Wazuh agents are lightweight (under 10MB RSS) and run on Linux, Windows, and macOS. The agent config in /var/ossec/etc/ossec.conf controls what is shipped to the manager:

Wazuh's built-in CIS benchmark checks run the same underlying tests as OpenSCAP but report into the central dashboard automatically. For teams managing heterogeneous environments - a mix of bare metal, VMs, and containers - Wazuh provides the consolidated view that running five separate tools on each host cannot.

The self-hosted stack requires a 4-core, 8GB RAM manager for under 100 agents. Beyond that, you need the Wazuh indexer cluster. Wazuh Cloud exists as a managed option but the self-hosted version is mature and the community rules library is extensive.

For DevOps teams building automated security workflows, pairing Wazuh's alerting webhooks with an automation platform like taskbotshub.ai lets you trigger remediation playbooks directly from Wazuh alerts - for example, automatically isolating a host via firewall rules when a rootkit signature fires.


  
    3600
    /etc,/usr/bin,/usr/sbin
    /etc/mtab
  

Building a Practical Audit Pipeline

Running these tools in isolation produces reports nobody reads. The operational pattern that works is a layered schedule: Lynis and RKHunter weekly via cron, Trivy and Grype in every CI pipeline on push, Nmap monthly against the full subnet, Falco continuously, and Wazuh aggregating all of it.

For the CI layer, a minimal GitHub Actions job that fails the build on HIGH or CRITICAL container vulnerabilities runs in under 3 minutes:

Store all scan artifacts in object storage with a 90-day retention policy. When an incident occurs, you want historical scan data to determine when a vulnerable package first appeared in your images.

For teams naming internal security tooling projects or setting up dedicated subdomains for dashboards and report portals, choosing a clean, memorable name matters for internal adoption - a service like nicename.me can help validate that a project name is available and readable before you commit it to DNS and documentation.

Schedule remediation sprints aligned to scan cycles. A Lynis audit that generates 23 suggestions with no assigned owner or deadline is noise. Pipe Lynis suggestion lines into your ticketing system automatically using the machine-readable report format and assign them to the server owner group by hostname prefix.

- name: Scan container image
  run: |
    trivy image \
      --severity HIGH,CRITICAL \
      --exit-code 1 \
      --format sarif \
      --output trivy-results.sarif \
      ${{ env.IMAGE_TAG }}
- name: Upload SARIF
  uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: trivy-results.sarif
// advertisement