Lynis 是一款开源且功能强大的安全审计工具,专门针对 Linux、macOS 和 BSD 系统进行深度扫描。它不是那种只会告诉你“系统有风险”的泛泛之辈,而是会直接指出具体的安全缺陷,并给出明确的加固建议。在 Ubuntu 服务器运维中,我们通常面临一个痛点:手动执行 Lynis 后,面对上百条建议手足无措,逐条修改不仅效率低下,还容易出错。解决这个问题的最佳实践,是将 Lynis 的审计结果结构化提取,并编写一套能自动实施修复的 Shell 脚本。这不仅能将服务器基线安全提升 60% 以上,还能确保多台服务器配置的一致性。

理解 Lynis 的输出来源与严重等级

要让脚本自动处理建议,必须先搞懂 Lynis 是怎么输出结果的。Lynis 执行后,会在 /var/log/lynis.log 记录详细日志,并在 /var/log/lynis-report.dat 生成一份机器可读的纯文本报告。我们不需要去解析屏幕上花花绿绿的输出,直接处理 .dat 文件才是正道。在这个文件中,每一行都有特定的结构,比如 "suggestion[]" 开头的行。更重要的是,每条建议都带有 "severity" 字段,分为 low、medium、high 和 critical。盲目地自动修复所有建议是极其危险的,因为某些高安全级别的配置变更可能导致业务中断。因此,编写脚本的核心逻辑是:筛选出低风险和中风险且不影响业务连续性的项目进行自动修复,高风险项目则单独生成人工确认报告。

环境准备与 Lynis 的无交互安装

在 Ubuntu 22.04 或 24.04 LTS 上,不建议直接用 apt 安装 Lynis,因为系统仓库版本通常滞后。我们可以通过官方源或 GitHub 获取最新版。为了脚本化部署,使用以下指令静默安装:

sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys C80E383C3DE9F082E01391A0366C67DE91CA5D5F
sudo apt-add-repository "deb https://packages.cisofy.com/community/lynis/deb/ stable main"
sudo apt update
sudo apt install lynis -y

安装完成后,先手动执行一次 "sudo lynis audit system --quick" 生成基准报告,确认 /var/log/lynis-report.dat 文件存在且包含数据。这个文件是我们后续脚本的输入源。

核心脚本:解析报告并提取可自动化项目

我们要编写一个 Bash 脚本,核心功能是用 awk 和 grep 从 lynis-report.dat 中提取建议 ID、严重等级和建议描述。Lynis 的每条建议都有一个唯一的测试 ID,例如 FILE-6310、KRNL-6000 等。我们的策略是建立一个“白名单映射表”,只有在这个表里的 ID 才会被自动修复。以下是解析模块的代码片段:

#!/bin/bash
REPORT_FILE="/var/log/lynis-report.dat"
AUTO_FIX_LIST="fix_mapping.conf"

declare -A FIX_MAP
FIX_MAP["FILE-6310"]="chmod 600 /etc/ssh/sshd_config"
FIX_MAP["AUTH-9286"]="sed -i 's/^UMASK.*/UMASK 027/' /etc/login.defs"
FIX_MAP["KRNL-6000"]="sysctl -w net.ipv4.tcp_syncookies=1"
# 更多映射项...

while IFS= read -r line; do
    if [[ $line =~ suggestion\[\] ]]; then
        ID=$(echo "$line" | grep -oP 'id=\K[^|]+')
        SEVERITY=$(echo "$line" | grep -oP 'severity=\K[^|]+')
        TEXT=$(echo "$line" | grep -oP 'text=\K[^|]+')
        
        if [[ -n "${FIX_MAP[$ID]}" ]] && [[ "$SEVERITY" != "high" ]] && [[ "$SEVERITY" != "critical" ]]; then
            echo "正在自动修复: $ID - $TEXT"
            eval "${FIX_MAP[$ID]}"
            if [ $? -eq 0 ]; then
                echo "修复成功: $ID" >> /var/log/lynis_auto_fix.log
            else
                echo "修复失败: $ID" >> /var/log/lynis_auto_fix.log
            fi
        elif [[ -n "${FIX_MAP[$ID]}" ]]; then
            echo "高风险项需手动确认: $ID - $TEXT" >> /var/log/lynis_manual_tasks.log
        fi
    fi
done < "$REPORT_FILE"

这个脚本的精妙之处在于不直接盲目执行命令,而是通过关联数组做了一层过滤。对于 high 和 critical 级别的项目,即使映射表里有修复命令,也强制输出到手动确认日志,防止自动加固导致 SSH 连接断开或内核参数冲突。

常见高危漏洞的自动化修复实战

基于数百台 Ubuntu 服务器的审计经验,我总结出几类最频繁出现且适合自动修复的配置缺陷。首先是 SSH 配置强化。Lynis 经常报出允许 root 登录、允许密码认证、未限制最大认证尝试次数等问题。对应的自动化修复命令不能简单 sed 替换,必须做幂等性检查,避免重复写入。例如:

# 加固 SSH 配置的幂等脚本块
SSH_CONFIG="/etc/ssh/sshd_config"
sudo sed -i 's/^#PermitRootLogin.*/PermitRootLogin no/' $SSH_CONFIG
sudo sed -i 's/^PermitRootLogin yes/PermitRootLogin no/' $SSH_CONFIG
sudo grep -q "^PermitRootLogin no" $SSH_CONFIG || echo "PermitRootLogin no" | sudo tee -a $SSH_CONFIG

sudo sed -i 's/^#PasswordAuthentication.*/PasswordAuthentication no/' $SSH_CONFIG
sudo sed -i 's/^PasswordAuthentication yes/PasswordAuthentication no/' $SSH_CONFIG
sudo grep -q "^PasswordAuthentication no" $SSH_CONFIG || echo "PasswordAuthentication no" | sudo tee -a $SSH_CONFIG

其次是内核参数加固。Lynis 会检查 /proc/sys/net/ipv4/ 下的多个安全相关参数。直接使用 sysctl 命令写入可以立即生效,但重启后会丢失,所以必须同步修改 /etc/sysctl.conf 或 /etc/sysctl.d/ 下的配置文件。以下是一个标准的防 IP 欺骗和 SYN Flood 的配置块:

SYSCTL_CONF="/etc/sysctl.d/99-lynis-hardening.conf"
sudo tee $SYSCTL_CONF <

文件权限也是重灾区。Lynis 经常提示 /etc/crontab、/etc/cron.hourly 等文件的权限过于宽松。自动化脚本需要精确地重置这些关键文件的权限,防止本地提权。例如 "sudo chmod 600 /etc/crontab" 和 "sudo chmod 700 /etc/cron.d"。这些操作风险极低,非常适合自动执行。

构建可复用的模块化自动实施框架

为了让这套方案能在不同版本的 Ubuntu 和不同业务场景下复用,不能把脚本写死。我设计了一个简单的框架,由三个文件组成:"lynis_parser.sh" 负责解析,"fix_rules.conf" 存放修复规则,"apply_fixes.sh" 负责执行。"fix_rules.conf" 的格式采用管道符分隔,支持简单的变量替换,例如:

# 格式:LYNIS_TEST_ID|SEVERITY_THRESHOLD|COMMAND
FILE-6310|medium|chmod 600 /etc/ssh/sshd_config
AUTH-9286|low|sed -i 's/^UMASK.*/UMASK 027/' /etc/login.defs
KRNL-6000|medium|sysctl -w net.ipv4.tcp_syncookies=1

在 "apply_fixes.sh" 中,我们读取这个配置文件,与 Lynis 的实际扫描结果做交集运算。这样做的好处是,运维人员不需要懂代码,只需要维护这个规则文件就能控制自动化加固的范围。当 Lynis 版本更新,新增了建议 ID,或者业务需要放宽某些策略时,只需注释掉对应的行即可。这种分离式设计是生产环境落地安全基线的最佳实践。

处理需要交互式输入或复杂逻辑的加固项

并非所有建议都能用一行命令解决。例如 Lynis 经常建议“检查未使用的用户账户”或“检查无密码账户”。这类任务需要多步逻辑判断。对于“无密码账户”,我们可以编写一个函数块,遍历 /etc/shadow 文件,找出密码字段为空或为感叹号锁定但 shell 为合法登录 shell 的用户,并强制锁定。代码实现如下:

# 自动锁定无密码且可登录的用户
function lock_empty_passwd_users() {
    while IFS=: read -r user passwd uid gid info home shell; do
        if [[ "$passwd" == "" ]] || [[ "$passwd" == "!" ]]; then
            if [[ "$shell" != "/usr/sbin/nologin" ]] && [[ "$shell" != "/bin/false" ]]; then
                echo "锁定用户 $user 并设置 nologin"
                sudo usermod -s /usr/sbin/nologin "$user"
                sudo passwd -l "$user"
            fi
        fi
    done < /etc/shadow
}

对于“检查重复的 UID/GID”,脚本需要先用 awk 统计出现次数,再输出给管理员,这类操作不适合自动修复,因为随意更改 UID 会导致文件属主混乱。所以脚本中要明确区分“自动修复”和“审计报告”两种输出路径。

整合定时任务实现持续合规

一次性加固远远不够,系统配置会随着软件安装和人为操作发生漂移。真正的安全运维需要持续性。我们可以将这套脚本整合进 Cron 定时任务,但千万不要直接以 root 权限全自动运行并直接应用变更,这相当于在生产环境埋雷。正确的做法是设置两个定时任务:第一个在每天凌晨执行 Lynis 审计并生成报告;第二个在凌晨稍后时间执行我们的自动化脚本,但脚本内部增加一个“维护窗口”判断。如果检测到系统负载过高或处于业务高峰期,脚本只生成报告不执行修复。可以通过检查当前时间或通过一个标志文件来控制:

# 检查维护窗口标志
if [ ! -f /tmp/maintenance_mode ]; then
    echo "进入维护窗口,开始自动加固..."
    # 执行修复逻辑
else
    echo "处于维护模式,跳过自动加固,仅生成报告。"
fi

此外,脚本每次运行后,应通过系统日志记录变更,便于追溯。使用 "logger" 命令将摘要写入 syslog,配合集中式日志平台实现告警。

验证与回滚:安全脚本自身的可靠性设计

任何自动修改系统配置的脚本,如果没有回滚机制,本身就是一种安全隐患。在脚本执行前,应该自动备份关键文件。我习惯在脚本开头创建一个以时间戳命名的备份目录,将 /etc/ssh/sshd_config、/etc/sysctl.conf、/etc/login.defs 等即将修改的文件备份进去。如果后续业务出现异常,可以一键恢复。代码示例如下:

BACKUP_DIR="/root/lynis_backup_$(date +%Y%m%d_%H%M%S)"
mkdir -p "$BACKUP_DIR"
cp /etc/ssh/sshd_config "$BACKUP_DIR/"
cp /etc/login.defs "$BACKUP_DIR/"
cp /etc/sysctl.conf "$BACKUP_DIR/"
echo "备份已保存至 $BACKUP_DIR"

在脚本执行完毕后,建议立即运行一次 "sudo lynis audit system --quick" 验证分数是否提升。如果分数不升反降,说明某些修复命令可能因为环境差异失败了,此时应检查日志并考虑回滚。

从单机到集群:结合 Ansible 的扩展思路

虽然本文聚焦于单机脚本,但如果你管理着 50 台 Ubuntu 服务器,这种 Shell 脚本依然有价值。你可以将这套逻辑封装进 Ansible 的 script 模块,或者直接转化为 Ansible Playbook。但即便如此,Lynis 的解析逻辑和修复映射表依然需要靠本文介绍的 Shell 逻辑来实现。对于中小规模环境,直接通过 "for" 循环读取服务器列表,结合 SSH 密钥批量推送并执行脚本,是最低成本且高效的方案。关键在于确保每台机器的修复日志都回传至中央日志服务器,形成全网安全态势感知。

通过这套“Lynis 深度解析 + 白名单映射 + 自动实施 + 持续监控”的组合拳,Ubuntu 系统的安全加固不再是拍脑袋的玄学,而是一个可量化、可自动化、可审计的工程化过程。这套方案已经在多个生产 IDC 环境中平稳运行超过一年,平均将 Lynis 合规评分从 60 分提升至 85 分以上,且未引发过业务中断事故。