From adb2b921ed18413387925dc43f5542c1614475a8 Mon Sep 17 00:00:00 2001 From: juvdiaz Date: Mon, 29 Jun 2026 14:35:12 -0600 Subject: [PATCH] Add report-only Lynis host audit --- docs/jeannie.1.md | 14 +++++++++ jeannie | 10 ++++++- scripts/security-scan | 70 ++++++++++++++++++++++++++++++++++++++++++- security/README.md | 8 +++++ 4 files changed, 100 insertions(+), 2 deletions(-) diff --git a/docs/jeannie.1.md b/docs/jeannie.1.md index fccb3a6..9d77dcf 100644 --- a/docs/jeannie.1.md +++ b/docs/jeannie.1.md @@ -142,6 +142,10 @@ baseline passive scans for targets in `security/targets.txt`. `security-k8s` : Run kube-bench CIS checks against the Debian kubeadm host. +`security-host` +: Run a Lynis host audit on Debian and print only warnings and suggestions. +This command is report-only. + `security-trivy` : Run only Trivy filesystem and IaC/config scans. @@ -219,6 +223,10 @@ smoke test. Defaults to `cloudflare.com`. `SECURITY_NUCLEI_TARGET` : Optional single URL override for `security-nuclei`. +`lynis` +: `security-host` requires Lynis to be installed on the Debian host. Install it +explicitly with `sudo apt install lynis`. + `LAB_AUTO_APPROVE=false` : Disable automatic OpenTofu approval for apply paths that support confirmation. @@ -328,6 +336,12 @@ Run a ZAP baseline scan against one URL: SECURITY_ZAP_TARGET=https://lab2025.duckdns.org/ ./jeannie security-zap ``` +Run a report-only Lynis host audit: + +```sh +./jeannie security-host +``` + Run the blocking pre-apply doctor directly: ```sh diff --git a/jeannie b/jeannie index 51dde66..c74832c 100755 --- a/jeannie +++ b/jeannie @@ -5780,6 +5780,11 @@ security_k8s() { "${REPO_ROOT}/scripts/security-scan" kube-bench } +security_host() { + require_debian_server "security-host" + "${REPO_ROOT}/scripts/security-scan" host +} + security_trivy() { require_debian_server "security-trivy" "${REPO_ROOT}/scripts/security-scan" trivy @@ -5903,6 +5908,9 @@ case "${1:-}" in security-k8s) security_k8s ;; + security-host) + security_host + ;; security-trivy) security_trivy ;; @@ -5920,7 +5928,7 @@ case "${1:-}" in echo "Log: ${JEANNIE_LOG_FILE}" ;; *) - echo "Usage: $0 {up|plan [all|provisioning|cluster|platform|apps|edge]|rebuild-cluster|stop-cluster|start-cluster|status|apps|website-translation-model|website-ollama-listen|ollama-setup|deploy-gitea|rpi-services|bootstrap-gitea-repo|backup-gitea|drill-gitea-restore|install-gitea-runner|move-prometheus-stack-workers|doctor-versions|doctor-edge|doctor-gitea|doctor-rpi|doctor-cluster|preflight|doctor-preapply|inventory-check|state-backup|fix-debian-docker-root|secrets-init|secrets-check|tailnet-policy-check|ai-index|ai-check|security-scan|security-prepare|security-zap|security-k8s|security-trivy|security-nuclei|openwrt|nuke}" + echo "Usage: $0 {up|plan [all|provisioning|cluster|platform|apps|edge]|rebuild-cluster|stop-cluster|start-cluster|status|apps|website-translation-model|website-ollama-listen|ollama-setup|deploy-gitea|rpi-services|bootstrap-gitea-repo|backup-gitea|drill-gitea-restore|install-gitea-runner|move-prometheus-stack-workers|doctor-versions|doctor-edge|doctor-gitea|doctor-rpi|doctor-cluster|preflight|doctor-preapply|inventory-check|state-backup|fix-debian-docker-root|secrets-init|secrets-check|tailnet-policy-check|ai-index|ai-check|security-scan|security-prepare|security-zap|security-k8s|security-host|security-trivy|security-nuclei|openwrt|nuke}" exit 1 ;; esac diff --git a/scripts/security-scan b/scripts/security-scan index 7a5a633..2aff6c1 100755 --- a/scripts/security-scan +++ b/scripts/security-scan @@ -12,7 +12,7 @@ NUCLEI_IMAGE="${NUCLEI_IMAGE:-projectdiscovery/nuclei:latest}" usage() { cat <<'EOF' -Usage: scripts/security-scan {all|prepare|trivy|zap|kube-bench|nuclei} +Usage: scripts/security-scan {all|prepare|trivy|zap|kube-bench|nuclei|host} Environment: SECURITY_REPORT_DIR Report output directory. @@ -182,6 +182,71 @@ run_kube_bench() { "${KUBE_BENCH_IMAGE}" run --targets master,node | tee "${SECURITY_REPORT_DIR}/kube-bench-${stamp}.txt" } +print_lynis_recommendations() { + local report_file="$1" + local warnings + local suggestions + local hardening_index + + hardening_index="$(awk -F= '$1 == "hardening_index" { print $2; exit }' "${report_file}" 2>/dev/null || true)" + warnings="$(sed -n 's/^warning\[\]=//p' "${report_file}" 2>/dev/null | awk -F'|' '{ print $1 (NF > 1 ? " [" $NF "]" : "") }')" + suggestions="$(sed -n 's/^suggestion\[\]=//p' "${report_file}" 2>/dev/null | awk -F'|' '{ print $1 (NF > 1 ? " [" $NF "]" : "") }')" + + printf '\n== Lynis Host Recommendations ==\n' + if [[ -n "${hardening_index}" ]]; then + printf 'Hardening index: %s\n' "${hardening_index}" + fi + + printf '\n-- Warnings\n' + if [[ -n "${warnings}" ]]; then + printf '%s\n' "${warnings}" | sed 's/^/- /' + else + printf 'none\n' + fi + + printf '\n-- Suggestions\n' + if [[ -n "${suggestions}" ]]; then + printf '%s\n' "${suggestions}" | sed 's/^/- /' + else + printf 'none\n' + fi +} + +run_lynis_host() { + local stamp + local report_file + local log_file + stamp="$(timestamp)" + ensure_report_dir + report_file="${SECURITY_REPORT_DIR}/lynis-host-${stamp}.dat" + log_file="${SECURITY_REPORT_DIR}/lynis-host-${stamp}.log" + + if ! command -v lynis >/dev/null 2>&1; then + cat >&2 <<'EOF' +lynis is not installed on this host. + +Install it on Debian with: + + sudo apt install lynis + +Jeannie does not install Lynis automatically because host hardening audits should +remain explicit and report-only. +EOF + exit 1 + fi + + echo "Running Lynis host audit in report-only mode..." + sudo lynis audit system \ + --no-colors \ + --quiet \ + --report-file "${report_file}" \ + --log-file "${log_file}" >/dev/null + + print_lynis_recommendations "${report_file}" + printf '\nFull Lynis report: %s\n' "${report_file}" + printf 'Full Lynis log: %s\n' "${log_file}" +} + case "${1:-all}" in all) run_trivy @@ -202,6 +267,9 @@ case "${1:-all}" in kube-bench) run_kube_bench ;; + host) + run_lynis_host + ;; -h | --help | help) usage ;; diff --git a/security/README.md b/security/README.md index 02ec5b7..f1a3562 100644 --- a/security/README.md +++ b/security/README.md @@ -10,6 +10,7 @@ Use Jeannie from the Debian homelab host: ./jeannie security-scan ./jeannie security-zap ./jeannie security-k8s +./jeannie security-host ``` The default `security-scan` runs: @@ -23,6 +24,8 @@ The focused commands are: images. - `security-zap`: OWASP ZAP baseline scan only. - `security-k8s`: kube-bench CIS checks against the kubeadm host. +- `security-host`: Lynis host audit summary for Debian. This command is + report-only and prints warnings/suggestions. - `security-trivy`: Trivy repo and IaC/config scans only. - `security-nuclei`: low-rate nuclei HTTP exposure scans only. @@ -38,3 +41,8 @@ understand the traffic it will generate. `security/targets.txt` is the public target allowlist. Keep it limited to services you own. + +`security-host` intentionally does not self-fix Lynis findings. Some hardening +recommendations can break SSH access, Docker networking, kubeadm, or public edge +routing. Use Lynis output to choose one narrow remediation at a time, then add an +explicit Jeannie command for that specific change.