FAQ

这是 Arcivéo Security Monitor 的安装、配置与维护指南。 各章节按主题分组:总体概览、面板部署、接入安全工具、内置模块及诊断。 命令可通过右侧按钮复制。

从何开始

01. 安装面板 — 请选择方式

面板安装说明位于各自独立的分步页面。请选择方式:

如果不确定,请选择自动方式。本手册始终是 SSL、工具、cron 与诊断的统一参考来源 — 各安装页面均引用其中的章节,不做任何重复。

概览

02. 什么是 Arcivéo Security Monitor

Arcivéo Security Monitor 是一款服务器安全面板。它收集已安装工具(Fail2ban、UFW、Lynis、ModSecurity、AIDE、ClamAV、Auditd、CrowdSec、Suricata、Falco 等)的数据,并在统一界面中呈现,包含仪表盘、攻击地图以及每个工具的详情页。

该监控面板本身不是主动防护手段,不会自行拦截攻击。它的职责是聚合已在运行的各类工具的信息,并以易读的方式呈现。

03. 监控如何在服务器上运行

监控仅在本地运行——它必须安装在所监控的同一台服务器上。不使用任何 SSH 或远程 API。

所有命令(fail2ban-client、ufw status、ipset list 等)都由面板以 Web 服务器用户身份执行(通常为 www-data,在主机面板上则为站点账户),并仅授予极小范围的 sudo 权限——只针对特定工具,而非全局 root 访问权限。结果会被解析并显示在浏览器中。

两种工作节奏。 面板的页面在打开的那一刻才去询问各个工具,因此您看到的始终是当前状态。除此之外,还有按计划(cron)运行的后台任务:

  • 每 5 分钟 —— 为“性能”页面采集一次负载快照;
  • 每 5 分钟 —— 安全事件传感器:新的封禁、IDS 告警、停止的防护服务、新开放的端口(“安全事件”一节);
  • 每天一次 —— 通过 Telegram 和电子邮件发送安全报告,并执行较重的检查(Lynis、AIDE、ClamAV、debsums、Logwatch);
  • 每 15 分钟 —— 若已开启,向个人中心发送服务器概要。

任务的完整清单见“所有 cron 任务集中一处”一节。

若有多台服务器,请在每台上分别安装监控,各用一个独立域名。要在同一个列表里看到它们,请开启服务器概览(“个人中心的服务器概览”一节)。

04. 安全评分如何计算

评分从满分开始,每发现一个问题就相应扣分:

  • UFW 未启用 — −30
  • Fail2ban 未运行(无活动 jail)— −25
  • 没有 WebAuthn 安全密钥 — −15
  • Lynis hardening index < 60 — −20;60–79 — −10
  • IPset ipsum 未加载 — −10
  • ClamAV 发现威胁 — −20
  • AIDE 检测到文件变更 — −15
  • SSL 已过期 — −30,将在 <14 天内过期 — −15,<30 天 — −5
  • CrowdSec 已安装但未运行 — −5
  • Suricata 已安装但未运行 — −5
  • 数据库/缓存(MySQL、PostgreSQL、Redis…)可从外部访问 — −10
  • 允许 root 通过 SSH 登录(PermitRootLogin yes)— −20
  • 允许通过 SSH 使用密码登录(PasswordAuthentication yes)— −10
  • SSH 细项设置:开启了 X11Forwarding、MaxAuthTries 大于 3,或所有账户都能读取 sshd_config — −5
  • Web 服务器加固:响应中暴露 Web 服务器或 PHP 版本、开启了 TRACE 方法,或缺少 Strict-Transport-Security 或 X-Frame-Options 响应头 — −5
  • 有待安装的 security 更新 — −5

结果:80+ = 受保护,60–79 = 需注意,<60 = 有风险。

只有在相应工具已安装时,才会对 ClamAV、AIDE、CrowdSec 和 Suricata 进行扣分。Lynis 和 AIDE 在数据库未初始化时显示为“无数据”,不会扣分。今日攻击次数会显示在仪表盘上,但不影响安全评分。 如果没有任何网站响应检查,Web 服务器加固不计入评分。

设置与许可证

05. WebAuthn — 双因素认证

WebAuthn — 一种通过硬件密钥实现的无密码认证标准。支持 YubiKey、Touch ID、Face ID、Windows Hello、Passkey。

使用密码登录后,系统会要求通过已注册的密钥进行确认。即使密码泄露,没有实体密钥或生物识别也无法登录。

如需配置,请在侧边菜单中打开 WebAuthn 密钥,然后点击“注册密钥”。请一次注册两个密钥:如果唯一的密钥丢失或损坏,将无法用它登录面板。

WebAuthn 仅在 HTTPS 下有效。在 HTTP 连接上无法注册和使用密钥登录。

06. 通知:Telegram、电子邮件、webhook 与推送

共有四条通道:Telegram、电子邮件、webhook 以及浏览器推送。它们彼此独立 —— 想开哪几条都行,四条全开也可以。每日安全报告、负载告警和事件传感器的发现都走同样的通道;只需在这里,“设置”中配置一次。

Telegram。需要机器人令牌和 chat id:

  1. 在 Telegram 中向 @BotFather 发送 → /newbot → 获取形如 令牌 123456:ABC... 的字符串。
  2. 向您新建的机器人发送任意一条消息(让它能够回复您)。
  3. 查询您的 chat id:向 @userinfobot 发送消息,或打开 https://api.telegram.org/bot<TOKEN>/getUpdates 并找到 "chat":{"id":...}。
  4. 将令牌和 chat id 填入“设置”→ Telegram,点击“保存并发送测试”。

电子邮件。在“设置”→ Email 中可二选一:

  • SMTP — 主机、端口(465/SSL 或 587/TLS)、您邮箱的登录名和密码;
  • Resend — 现代化 API:填写 API 密钥(re_...)和已验证的发件域名。

Webhook。 一个能接收 POST 的地址就覆盖了 Slack、Discord、Mattermost、n8n 以及您自己写的处理程序。“设置” → Webhook:粘贴地址并打开开关。

  • 发往 Slack 和 Mattermost 的是 {"text": …},发往 Discord 的是 {"content": …}(Discord 会拒收超过 2000 个字符的消息,因此正文会被截断);
  • 其他任何地址收到的都是结构化 JSON:text、event、severity、source、count、host、ts —— 无需再去解析消息正文。

推送通知。 消息直接送到浏览器;如果把面板添加到了手机主屏幕,就会像普通应用通知那样弹出 —— 中间既不需要机器人,也不需要邮件服务器。“设置” → 推送通知:打开开关,并在每一台需要接收通知的设备上点击“连接此设备”。“已连接设备”计数会显示共有多少台。

推送只在 HTTPS 下工作(和 WebAuthn 一样),而且需要两次授权:先在浏览器里 —— 点击“连接此设备”后会弹出请求 —— 然后还要在操作系统本身里允许。如果在 Windows 中关闭了设置 → 系统 → 通知的总开关,浏览器会给出权限,面板也会报告已发送,屏幕上却什么都不会出现。“勿扰”模式,以及在 macOS 中关闭浏览器通知(系统设置 → 通知),也是同样的结果。
每条通道都有各自的“发送测试”按钮,可以立即验证。自动报告的时间安排由 cron 负责(“所有 cron 任务”一节):cron 触发发送,通道则取自设置。

报告状态:“注意”或“正常”。标题仅在出现真实问题或有待处理操作时才显示“注意”:发现 ClamAV 威胁、AIDE 检测到文件变更、Falco 严重事件(过去 24 小时内的 Emergency/Alert/Critical)、Monit 中有服务宕机、需要重启、SSL 即将到期(≤14 天)或有待安装的安全更新。背景噪声——机器人的 SSH 暴力尝试、被 fail2ban 封禁的 IP、Suricata 告警、Lynis 警告以及已被拦截的 ModSecurity 请求——不会提升状态,因此报告中的这些数字本身并不意味着“注意”。

07. 许可证 —— 输入与激活

详细监控模块(Lynis、UFW、ModSecurity、攻击地图、AIDE、ClamAV 等)需在拥有有效许可证时才会开放。没有许可证时,仪表盘、设置和账户仍可使用,而各模块会显示“需要许可证”卡片。

在个人中心购买后,您会获得一个形如 ARCIVEO-XXXX-XXXX-XXXX-XXXX 的激活码。需要将它“激活”到您面板的域名上——这会把激活码转换为已签名的许可证文件([license] 区块),再将其粘贴进面板。

如何激活(3 步):

  1. 获取激活码。个人中心 my.arciveo.com → “许可证”/“许可证激活”栏目——复制 ARCIVEO-… 激活码。
  2. 将激活码激活到您的域名。同样在个人中心打开“许可证激活”,输入:激活码、您的电子邮箱和面板域名(打开监控所用的地址,例如 monitor.example.com)。点击激活——系统会生成绑定到该域名的许可证文件,并显示在带有“复制”按钮的字段中。
  3. 将密钥粘贴进面板。复制整段许可证文本 → 在面板中打开“设置”→“许可证”区块,粘贴后点击“保存”。模块会立即解锁。

面板会以加密方式验证密钥:签名、域名绑定和有效期。

激活时的域名必须与面板地址完全一致。请从 config.php 中的 APP_URL 常量取值,且只输入主机名——不含 https://、不含 www 前缀。激活为一次性操作:激活码会转换为所输入域名的许可证,无法再次激活——若域名填错,密钥将无法用于您的面板,而激活码也会被消耗。因此请仔细输入域名。
有效期已过或域名已更换——面板顶部会出现警告。许可证永久绑定到域名,不能转移到其他域名:延长有效期或更换域名都需要一个新密钥(在个人中心购买并一次性激活)。

08. config.php 文件 — 面板的全部设置

面板的所有主要参数都通过普通的 define() 常量集中在根目录(与 public/ 文件夹同级)的单个 config.php 文件中。该文件在安装时创建;很少需要手动修改——主要是在更换域名、迁移或连接到其他数据库时。任何修改后请重启 PHP-FPM(否则由于 OPcache,改动不会生效)。

请将您自己的值填入高亮处;其余部分保持原样:

// --- 数据库 --- define('DB_HOST', 'localhost'); // 保持不变 define('DB_NAME', 'db_name'); // 创建数据库时设置的值 define('DB_USER', 'user'); // 创建数据库时设置的值 define('DB_PASS', 'db_password'); // 创建数据库时设置的值 define('DB_CHARSET', 'utf8mb4'); // 保持不变 // --- 应用 --- define('APP_URL', 'https://monitor.example.com'); // 面板地址,末尾不带斜杠 define('TIMEZONE', 'Asia/Shanghai'); // 您的时区 // --- 会话时长 --- define('SESSION_LIFETIME', 28800); // 空闲到需重新登录的时长,秒(28800 = 8 小时)

数据库。 连接 MySQL/MariaDB 的凭据:

  • DB_HOST — 数据库主机,几乎总是 localhost;
  • DB_NAME — 面板数据库的名称;
  • DB_USER — 数据库用户(仅能访问自己的数据库);
  • DB_PASS — 该用户的密码;
  • DB_CHARSET — 连接编码,请保持 utf8mb4。

应用。

  • APP_URL — 面板的完整地址(如 https://monitor.example.com)。必须与激活许可证所用的域名一致——否则密钥将被拒绝(见“许可证”一节);
  • TIMEZONE — PHP 时区:仅影响面板显示日期和时间的方式。对 cron 任务的运行时间没有影响——那里使用的是系统时区(见“所有 cron 任务”)。

会话时长。 SESSION_LIFETIME — 会话空闲超时秒数(滑动式:有活动时刷新)。默认 28800 = 8 小时;空闲超过该时长后,面板会要求重新登录。例如 3600 = 1 小时,86400 = 一天。

错误日志。 错误永远不会显示给访客,而是写入 logs/php_errors.log——可在“应用日志”页面查看。这些行(display_errors=0、log_errors=1、error_log 路径)通常无需修改——设置直接写在文件中,不依赖 php.ini。

config.php 是机密文件。 其中含有数据库密码。它位于面板根目录(与 public/ 同级),而本面板的 Web 根目录(DocumentRoot)正是面板根目录,而非 public/。文件本身不会“泄露”:根目录的 .htaccess 对它设有明确禁止(Require all denied)——服务器会返回 403。即便没有这条规则,源码也不会泄露:这是 PHP——服务器会执行它,而不是以文本形式输出。以防万一:请勿将其发布到公共代码库,也不要把含真实密码的文件发给技术支持。文件权限为 640。
在迁移或恢复访问时,此文件是凭据的主要来源:数据库名称、用户和密码都取自这里(见“面板升级与迁移”和“恢复访问”两节)。

09. 只读 API(JSON 与 Prometheus)

面板由密码登录守着,若开启了 IP 过滤则还多一道。程序两者都用不上,因此外部系统另有入口 —— 两个由令牌保护的只读地址。令牌不会改变服务器上的任何东西:用它只能取回数据。

开启方式 —— “设置” → “只读 API”:开关“开启 API”;令牌会在首次保存时自动生成。“新令牌”按钮会签发另一个令牌,并在同一刻切断所有仍在使用旧令牌的一方。

  • /api/status.php —— JSON:安全评分、各模块状态、含占用最高进程的最近一次负载测量,以及最近 24 小时的安全事件;
  • /api/metrics.php —— 同样的数据,采用 Prometheus 文本格式(arciveo_* 指标:arciveo_security_score、arciveo_fail2ban_banned、arciveo_cpu_busy_pct 等)。
curl -H "Authorization: Bearer <TOKEN>" https://monitor.example.com/api/status.php curl -H "Authorization: Bearer <TOKEN>" https://monitor.example.com/api/metrics.php

如果您的客户端不会设置请求头,令牌也可以作为参数传入 —— ?token=…。请注意这种形式下它会落进 Web 服务器的访问日志,所以还是用请求头更好。

Prometheus。 可直接用于 scrape_configs 的配置块:

scrape_configs: - job_name: arciveo metrics_path: /api/metrics.php scheme: https authorization: credentials: <TOKEN> static_configs: - targets: ['monitor.example.com']
响应会缓存 5 分钟:这些数字背后是与仪表盘相同的那套较重的采集。因此频繁抓取不会给服务器带来额外负担 —— 数值只是重复到下一次刷新为止 —— 但把间隔设得短于一分钟同样没有意义。
401 —— 令牌不对或没有传。404 并带有 api disabled 文本 —— API 在设置中被关闭了。被拒绝的请求会连同来源 IP 一起写入应用日志,因此暴力猜测令牌的行为在“应用日志”页面上一目了然。

10. 个人中心的服务器概览

面板是按服务器分别安装的,回答“这台机器状况如何”很在行。但它回答不了“我所有的机器状况如何” —— 为此,每个面板都可以向个人中心发送一份简短概要,那里会把服务器排成一个列表。

需要在个人中心取用的东西 my.arciveo.com → “我的服务器”一节:这里会签发一个令牌(形如 fk_…),并给出接收地址 https://my.arciveo.com/api/fleet.php。令牌按账户签发,一个账户一个,您所有的服务器共用同一个。

需要在面板中设置的东西 —— “设置” → “服务器概览”:接收地址、令牌、服务器名称(列表中显示的名字;留空则使用面板的域名),以及“发送概要”开关。“立即发送”按钮会立刻验证连通性,不必等到计划时间。

之后概要便由 cron 每 15 分钟发送一次:

# 每 15 分钟 —— 向个人中心发送服务器概要 3,18,33,48 * * * * /usr/local/bin/fleet-push-all.sh >> /path/to/monitor/logs/cron.log 2>&1

具体发送的内容: 服务器名称与域名、操作系统、运行时长、PHP 版本;安全评分;各模块状态(UFW、Fail2ban 的 jail 数量、今日封禁数、CrowdSec/Suricata/Falco 是否在运行、ClamAV 威胁、AIDE 变更、Lynis 指数、待安装更新、是否需要重启、证书剩余天数、危险开放端口数);最近一次负载测量(load、CPU、内存、磁盘、inode);最近 24 小时的事件计数以及最后一条严重事件。概要中没有日志、没有密码,也没有文件内容。

是服务器主动发送,而不是个人中心来问。 因此无需从外部向您的网络开放任何入口,被 IP 过滤挡住的面板照样能上报。节点按面板域名识别,所以重启或更换 IP 之后列表也不会重复。
如果个人中心里某台服务器被标记为“无响应”,说明概要已经很久没送达了。请按顺序检查:“发送概要”开关、cron 那一行(sudo crontab -l),以及“立即发送”按钮给出的回应。发送错误会带着 FLEET 标记写入应用日志 —— 从那里可以看出是令牌被拒(HTTP 401),还是连接根本没有建立。

安全工具

11. UFW 防火墙

UFW(Uncomplicated Firewall)是 nftables/iptables 的简易接口。它会关闭所有入站端口,仅放行明确允许的端口。“UFW 防火墙”页面显示状态和规则。

sudo apt install ufw # 放行 SSH(务必在启用之前!)和网站端口 sudo ufw allow OpenSSH sudo ufw allow 80,443/tcp # 对外关闭数据库(仅允许本地访问) sudo ufw deny 3306 # 启用并检查 sudo ufw enable sudo ufw status verbose
在执行 ufw enable 之前,请务必放行 SSH(ufw allow OpenSSH),否则您将失去对服务器的访问。
仪表盘上的“对外暴露”会考虑 UFW:被 deny 规则关闭的端口不会被视为可从外部访问。
Skipping adding existing rule 并非错误。 UFW 以此提示完全相同的规则已存在,因而不会重复添加。重新运行自动配置(该操作是幂等的)时出现此提示属正常情况,无需处理。

12. 安装 Fail2ban

当失败登录尝试次数超过阈值时自动封禁 IP。可分析 SSH、nginx、Apache 及其他服务的日志。

sudo apt install fail2ban sudo systemctl enable --now fail2ban # 查看状态: sudo fail2ban-client status
可用的配置(包含数十个 jail 以及基于 ipsum 的自动封禁的 jail.local)见下一节。

13. Fail2ban + ipsum 实用配置

基础安装见上文。这里是可产生数十个活跃 jail 和数千条封禁记录的实用配置:通用设置、关键 jail,以及从 ipsum 列表自动封禁恶意 IP。

文件 /etc/fail2ban/jail.local — 通用设置和最重要的 jail:

[DEFAULT] bantime = 1w findtime = 900 maxretry = 3 backend = systemd banaction = nftables-multiport ignoreip = 127.0.0.1/8 ::1 <YOUR_IP> <TRUSTED_NETS> # 渐进式封禁:每次重犯,封禁时间更长 bantime.increment = true bantime.factor = 2 bantime.maxtime = 5w bantime.rndtime = 300 [sshd] enabled = true maxretry = 5 bantime = -1 # 对 SSH 暴力破解永久封禁 findtime = 3600 # 惯犯:被封禁多次者,永久封禁 [recidive] enabled = true logpath = /var/log/fail2ban.log banaction = %(banaction_allports)s bantime = -1 findtime = 86400 maxretry = 2 [http-get-dos] enabled = true maxretry = 100 findtime = 300 bantime = 1w # Web 服务 (apache-*, nginx-*, php-url-fopen, phpmyadmin-syslog): [nginx-http-auth] enabled = true port = http,https [apache-badbots] enabled = true port = http,https # … 以及其他按服务划分的 jail (dovecot, exim, postfix-sasl, # mysqld-auth, vsftpd, portscan, pam-generic) — enabled = true
请务必在 ignoreip 中填入您自己的 IP 和受信任网段,否则可能会把自己封禁。修改后执行:sudo fail2ban-client reload。

把 ipsum 封禁列表自动加载到 root 的 crontab(sudo crontab -e):level 1(10 万余个 IP)会加载到 ipsum 集合中,并在防火墙上拦截(详见“IPset 封禁列表”一节):

# 04:00 — 更新 ipset ipsum (level 1,覆盖范围最大): 0 4 * * * /usr/local/bin/load-ipsum.sh >> /path/to/monitor/logs/cron.log 2>&1
该集合必须命名为 ipsum——仪表盘正是读取它(“IPset ipsum”卡片)。级别:levels/1.txt — 覆盖范围最大,levels/3.txt — 更精确(3 个以上来源)。

为何“安全监控”分为两个区域。 防护在两个层面运作,仪表盘不会将它们混在一起:

  • 真实攻击(被动响应) — 所有被 fail2ban 捕获的行为:实际的入侵尝试(sshd、apache-*、nginx-* 等 jail)以及严重惯犯(recidive jail——已被多次封禁者)。这些是真正试图入侵您的 IP——它们会出现在攻击地图和“时间轴”上。
  • 预防性拦截(主动防御) — 公开的已知恶意 IP 封禁列表 ipset ipsum,在防火墙上以 DROP 规则拦截。这些地址大多根本没有接触过您的服务器——会被提前拦截;“IPset ipsum”计数器显示预防性拦截了多少个。

区别很简单:被动响应是“这些发起了攻击并被封禁”,预防性是“这些在尝试之前就已被拦截”。以前 recidive 会人为塞入 ipsum 的 list-3(因此有旧的“列表型 recidive”划分);现在 recidive 只包含真正的惯犯,预防性拦截全部由防火墙负责。

14. IPset 封锁列表(ipsum)

ipsum 是一个每日更新的公开恶意 IP 列表。监控面板会在仪表盘和攻击地图上显示已加载的地址数量,并将其纳入安全评分(若未加载该集合则 −10)。

不使用 fail2ban 的最简方案——单独一个 ipsum 集合,通过 iptables 进行封锁:

# 创建集合(仅一次): sudo ipset create ipsum hash:ip # 更新脚本 /usr/local/bin/update-ipsum.sh: #!/bin/bash ipset flush ipsum for ip in $(curl -s https://raw.githubusercontent.com/stamparm/ipsum/master/levels/3.txt); do ipset add ipsum "$ip" 2>/dev/null done iptables -C INPUT -m set --match-set ipsum src -j DROP 2>/dev/null \ || iptables -I INPUT -m set --match-set ipsum src -j DROP # Cron(sudo crontab -e,每日 4:00): 0 4 * * * /usr/local/bin/update-ipsum.sh
使用 fail2ban-recidive 的进阶方案——见“Fail2ban + ipsum 工作配置”一节。
ipset 驻留在内存中,重启后会丢失。仅靠每日 cron,从重启到下次运行之间集合会一直为空(仪表盘显示 0)。请在系统启动时也加载集合——把加载逻辑放进脚本并挂到 @reboot 上。顺便用 create … -exist 命令设定上限 maxelem 300000(默认 65536——level 1 装不下,会报“Hash is full”):
# /usr/local/bin/load-ipsum.sh #!/bin/bash curl -s https://raw.githubusercontent.com/stamparm/ipsum/master/levels/1.txt \ | grep -v '^#' | sed 's/^/add ipsum /' \ | (echo "create ipsum hash:ip hashsize 131072 maxelem 300000 -exist"; echo "flush ipsum"; cat) \ | ipset restore -exist # sudo crontab -e —— 每日 04:00 及每次启动时: 0 4 * * * /usr/local/bin/load-ipsum.sh @reboot sleep 60 && /usr/local/bin/load-ipsum.sh
自动安装时的工作方式。脚本会将完整的 level 1 列表(10 万多个 IP)加载到 ipsum 集合中,如果防火墙由安装程序管理(全新 VPS——“完整”/“精简”配置),则会用 DROP 规则把该集合接入 UFW——来自这些 IP 的流量会被真正封锁。该规则位于 ESTABLISHED,RELATED 之后,因此现有连接(包括您的 SSH)不会中断——只拦截来自列表的新连接。集合会在 ipsum-load.service 服务加载时、先于防火墙恢复(否则 UFW 无法启动),并由 cron 在 04:00 更新。在已配置好的服务器上(面板、自有防火墙),安装程序不会改动防火墙——此时 ipsum 仅作为仪表盘和攻击地图的列表,需要时可手动添加 DROP 规则(最简方案用 iptables … --match-set ipsum … -j DROP——见上文)。自动安装时无需手动做任何操作。

15. 安装 CrowdSec

Fail2ban 的现代替代方案,具备集体威胁情报能力:既有来自社区的封禁,也支持自定义规则。需要单独的 bouncer 才能将封禁应用到防火墙。

curl -s https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.deb.sh | sudo bash sudo apt install crowdsec sudo systemctl enable --now crowdsec # 用于 iptables/nftables 的 bouncer: sudo apt install crowdsec-firewall-bouncer-iptables # 检查状态: sudo systemctl status crowdsec sudo cscli decisions list sudo cscli bouncers list
面板中显示“未运行” = 组件已安装,但服务未激活(监控通过 systemctl is-active crowdsec 检查)。启动:sudo systemctl enable --now crowdsec;如果崩溃,请查看 sudo journalctl -u crowdsec -n 30。任何处于“未运行”状态的服务(Suricata、Falco、Monit、MySQL)都适用同样的规则。
仪表盘上显示“0 个场景”或“0 个 bouncer”。 CrowdSec 开箱时几乎是空的——没有集合它就检测不到任何东西,而没有注册的 bouncer,封禁就不会应用到防火墙。请安装基础集合并确认 bouncer 已在列表中:
# 基础集合(Linux + SSH + Web 服务器): sudo cscli collections install crowdsecurity/linux crowdsecurity/sshd crowdsecurity/base-http-scenarios sudo systemctl reload crowdsec # bouncer 必须在列表中并处于连接激活状态: sudo cscli bouncers list
bouncer 日志中出现 stream halted / 封禁未生效。 这是遗留的 api 密钥:bouncer 已从 cscli bouncers list 中删除,但它的旧密钥仍留在 /etc/crowdsec/bouncers/*.yaml 中。请重新注册 bouncer 并填入新的密钥:
sudo cscli bouncers add fw-bouncer # 会输出新的 api_key # 将该密钥填入 /etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml 的 api_key: sudo systemctl restart crowdsec-firewall-bouncer
自动安装(“完整防护”配置文件)会自行安装集合并注册 firewall-bouncer——只有在手动安装或手动改动 CrowdSec 后,才需要手动执行这些操作。

16. 安装 AIDE

AIDE(Advanced Intrusion Detection Environment)会对文件系统做一次快照,并在每次检查时报告 /etc、/bin、/usr 中的改动。安装完成后必须初始化数据库(aideinit)。

sudo apt install aide # 初始化数据库(5–15 分钟): sudo aideinit sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db # Ubuntu 24.04:/var/lib/aide 目录以 700 模式创建(属主为 _aide), # 面板(www-data)看不到数据库 → 显示“未初始化”。 # 为目录开放通行权限(数据库文件仍保持 600): sudo chmod 755 /var/lib/aide # 首次检查并写入监控读取的日志。 # 在 Ubuntu/Debian 上 aide 需要显式的 --config(否则报“missing configuration”; # 新版本已不再提供 aide.wrapper 二进制文件): sudo bash -c 'aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1'
执行 aideinit 期间,终端会在 Running aide --init... 这一行停留 5–15 分钟——这是正常的(对整个文件系统做哈希,磁盘负载较高)。请勿按 Ctrl+C 中断。如果进程“卡住”却没有任何输出——可能它在等待一个隐藏提示 Overwrite existing aide.db.new [Yn]? 的回答(请按 Y)。可从另一个会话检查其活动状态:pgrep -af aide。
aideinit 错误:“21_aide_spamassassin … printf: invalid number”(return code 20)——这是 Ubuntu 22.04 中 AIDE 配置片段的已知缺陷。数据库无法创建。请移除损坏的片段后重试:
sudo mv /etc/aide/aide.conf.d/21_aide_spamassassin /etc/aide/21_aide_spamassassin.disabled sudo aideinit sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db
仪表盘上的状态。“未初始化”= 面板看不到数据库文件:要么没有运行过 aideinit,要么(Ubuntu 24.04)/var/lib/aide 目录以 700 模式创建、www-data 无法访问——用 sudo chmod 755 /var/lib/aide 解决(见上方代码块)。“尚未检查”= 数据库已存在,但还没执行过检查——这不是错误。监控从 /var/log/aide/aide.log 读取结果。
定期检查 → 供面板使用的日志。在新版 Ubuntu/Debian 上,系统自带的 /etc/cron.daily/aide 可能不会以所需格式写入 /var/log/aide/aide.log(且这些系统中已不再有 aide.wrapper)。更可靠的做法是添加自己的、带显式 --config 的 cron——它以 root 身份、644 模式写入日志,监控无需额外分组即可读取:
# sudo crontab -e —— 每天 02:00 检查: 0 2 * * * /usr/bin/aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1 # 立即运行,不必等待计划任务: sudo bash -c 'aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1'
自动安装已经完成了这一切:chmod 755 /var/lib/aide 以及 02:00 的检查 cron——无需任何手动操作。
请在干净的服务器上——即安装 Web 应用之前——完成首次初始化。在进行合法改动后,请重新创建数据库:sudo aideinit && sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db。

17. 安装 ClamAV

Linux 上的反病毒扫描器。特别适合扫描 /var/www 以查找 PHP shell 和恶意代码。

sudo apt install clamav clamav-daemon sudo systemctl enable --now clamav-daemon # 更新病毒特征库: sudo freshclam # 手动扫描目录: sudo clamscan -r /var/www --infected
执行 enable --now 后 clamd 守护进程仍显示“未激活”?三个常见原因:

1. 配置文件中残留 Example 行——只要它存在,clamd 就拒绝启动:

sudo sed -i '/^Example/d' /etc/clamav/clamd.conf sudo systemctl restart clamav-daemon

2. 未下载病毒特征库——没有它 clamd 无法启动:

sudo systemctl stop clamav-freshclam sudo freshclam sudo systemctl start clamav-freshclam sudo systemctl restart clamav-daemon

3. 只是正在加载——clamd 将约 800 万条特征加载进内存需要 30–60 秒。请稍候再检查:systemctl is-active clamav-daemon(状态为 activating → 仍在加载)。

诊断:sudo journalctl -u clamav-daemon -n 30 --no-pager。
仪表板上显示“已扫描文件数:0”/“上次扫描:—”?clamd 守护进程只是将特征保存在内存中,它本身不会按计划扫描任何内容。面板显示的是计划扫描的结果,因此需要一个执行扫描并写入日志的 cron。自动安装会部署封装脚本 /usr/local/bin/clamav-scan.sh 和一个 01:30 的 cron——首次运行后“已扫描文件数”和“上次扫描”即会填充。无需等待计划立即运行:sudo /usr/local/bin/clamav-scan.sh。

18. 安装 Linux Malware Detect(maldet)

Linux Malware Detect (LMD) — 针对 Web 威胁的恶意软件扫描器:PHP shell、Web 后门、下载器。它使用 ClamAV 引擎,并以自有签名加以补充。

wget https://www.rfxn.com/downloads/maldetect-current.tar.gz tar -xzf maldetect-current.tar.gz dir=$(ls -d maldetect-*/ | head -1) && cd "$dir" && sudo bash install.sh && cd ~ rm -rf maldetect-* maldetect-current.tar.gz # 更新签名: sudo maldet -u # 扫描 /var/www: sudo maldet -a /var/www
LMD 与 ClamAV 搭配使用效果很好。最近一次报告:maldet --report。
安装时可能出现 update-rc.d: error: unable to read /etc/init.d/maldet 这一行——它是无害的。maldet 不使用 init.d,签名更新和扫描通过 /etc/cron.daily/maldet 运行。如果下面能看到 installation completed,则说明一切安装成功。
页面上 LMD 显示“未安装”,但其实已安装?maldet 不是通过 apt 安装的,而是安装在 /usr/local/maldetect;在启用 open_basedir 时,它是否存在会通过 shell 检查——请参见“页面为空,但服务器上有数据”一节。

19. 安装 Suricata

网络入侵检测系统:在数据包层面分析流量,并掌握数千种攻击特征。它是对 ModSecurity 的补充(后者工作在 HTTP 层,Suricata 工作在 TCP/IP 层)。

sudo add-apt-repository ppa:oisf/suricata-stable sudo apt update && sudo apt install suricata # 下载最新规则: sudo suricata-update sudo systemctl enable --now suricata
Suricata 显示"运行中",但面板不显示告警/事件数为 0? Suricata 以 root 身份将日志写入 /var/log/suricata/eve.json,而目录权限为 750,Web 服务器(www-data)无法读取。请为该目录开放进入权限——目录内的文件仍受保护:
sudo chmod o+rx /var/log/suricata
自动安装会自行完成此操作——无需手动执行。

20. 安装 Falco

通过 eBPF/内核模块拦截系统调用,实时检测异常:nginx 派生的 shell、Web 进程读取 /etc/passwd、写入 /bin 等。

curl -fsSL https://falco.org/repo/falcosecurity-packages.asc > /tmp/falco.asc gpg --dearmor < /tmp/falco.asc | sudo tee /usr/share/keyrings/falco-archive-keyring.gpg > /dev/null sudo chmod 644 /usr/share/keyrings/falco-archive-keyring.gpg && rm /tmp/falco.asc echo "deb [signed-by=/usr/share/keyrings/falco-archive-keyring.gpg] https://download.falco.org/packages/deb stable main" \ | sudo tee /etc/apt/sources.list.d/falcosecurity.list sudo apt update && sudo apt install falco sudo systemctl enable --now falco
监控通过 journalctl -u falco 读取 Falco 事件(无需 sudo — 通过 systemd-journal 组)。请确认 www-data 已加入该组 — 参见手动安装页面上的“配置 sudo”(第 2 项)。
“24 小时内 0 个事件”是正常现象,而非错误。 Falco 是事件驱动的:一切正常时它保持静默,只有出现异常时才记录事件(Web 进程派生 shell、读取 /etc/passwd、写入系统目录)。在平稳运行的服务器上一天内零个严重事件,正是健康状态。
对面板而言文件输出更可靠。 通过 journalctl 读取需要访问日志的权限;为让面板稳定地看到事件,自动安装会为 Falco 启用 file_output → /var/log/falco/falco.log,并为服务设置 UMask=0022(日志可被 Web 服务器读取)。在全新安装上无需手动配置这些。

21. 安装 ModSecurity(WAF)

ModSecurity — 面向 Apache 或 Nginx 的 Web 防火墙(WAF)。可拦截应用层攻击:SQL 注入、XSS、路径遍历和扫描器。

# Apache: sudo apt install libapache2-mod-security2 sudo a2enmod security2 # OWASP Core Rule Set 规则集: sudo apt install modsecurity-crs # 必须执行:缺少此文件时规则引擎处于关闭状态 sudo cp /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf sudo sed -i 's/^SecRuleEngine DetectionOnly/SecRuleEngine On/' /etc/modsecurity/modsecurity.conf sudo apache2ctl configtest && sudo systemctl reload apache2 # 验证:应返回 403 curl -s -o /dev/null -w '%{http_code}\n' "https://monitor.example.com/?id=1%20UNION%20SELECT%201,2--"
仅安装软件包本身并不能提供任何防护。 Apache 通过 IncludeOptional /etc/modsecurity/*.conf 这一行加载配置,而软件包只放置了 modsecurity.conf-recommended —— 它不符合 *.conf 通配符。如果不将它复制为 modsecurity.conf,SecRuleEngine 就仍为 Off:模块已加载、CRS 规则已加载,但流量不会被检查,也不会生成审计日志。中间模式 DetectionOnly 只会将事件写入日志,而不拦截请求 —— 面板会以黄色显示该状态。

面板对审计日志的访问权限。 日志 /var/log/apache2/modsec_audit.log 归 root 所有(权限 640),Web 用户无法读取。面板通过一个包装脚本获取数据 —— 请创建它:

sudo tee /usr/local/bin/monitor-modsec >/dev/null <<'EOF' #!/bin/sh echo "ENGINE=$(grep -hE '^SecRuleEngine[[:space:]]+' /etc/modsecurity/*.conf 2>/dev/null | tail -1 | awk '{print $2}')" echo "---LOG---" tail -n 3000 /var/log/apache2/modsec_audit.log 2>/dev/null echo "---RULES---" for f in /etc/modsecurity/crs/rules/*.conf /usr/share/modsecurity-crs/rules/*.conf /etc/modsecurity/custom-rules.conf; do [ -f "$f" ] && { echo "===FILE:$(basename "$f")==="; cat "$f"; } done EOF sudo chown root:root /usr/local/bin/monitor-modsec sudo chmod 755 /usr/local/bin/monitor-modsec # 在 /etc/sudoers.d/monitor 中(用户 = 运行 PHP-FPM 的那个用户): # www-data ALL=(ALL) NOPASSWD: /usr/local/bin/monitor-modsec
包装脚本取的是最后一条没有缩进的 SecRuleEngine 指令:带缩进的行位于 <LocationMatch>/<Directory> 块内部(例如为 phpMyAdmin 关闭 WAF),并不决定全局模式。
sudoers 中的用户必须与 FPM 进程池的用户一致:在普通的 Apache/Debian 上是 www-data,在 HestiaCP 中站点进程池以站点所有者身份运行(例如 admin)—— 请用 grep -E '^user' /etc/php/*/fpm/pool.d/monitor.example.com.conf 检查。
如果站点位于 Nginx 代理之后(HestiaCP),Apache 看到的客户端就是代理本身 —— 面板会从 X-Forwarded-For 头中读取攻击者的真实 IP。统计中只会包含触发了规则的事务:SecAuditLogRelevantStatus 指令会将任何 4xx/5xx 响应写入审计日志,因此普通的 403/500 也会进入其中 —— 面板不会将它们计为 WAF 事件。
---RULES--- 块用于“所有已启用规则”栏目 —— 面板不仅显示已触发的规则,还会显示所有已加载的 CRS 规则和自定义规则。循环 for f in … 中的三个路径是 CRS 规则和本地补充规则的典型位置;如果您的布局不同(软件包将文件放到自己的目录,或自定义规则不在 /etc/modsecurity/custom-rules.conf),请用 sudo grep -rl 'IncludeOptional\|^Include ' /etc/apache2/mods-enabled/security2.conf /etc/apache2/conf-enabled/*.conf 2>/dev/null 命令找出实际路径并将其填入列表。如果包装脚本较旧(没有此部分)—— 该栏目只会显示“不可用”警告,页面其余部分仍照常工作。

22. 安装 Auditd

Auditd(Linux Audit Daemon)在内核层记录系统调用:登录与登出、sudo 命令、失败的身份验证尝试、文件更改。监控面板会显示当天的登录、失败尝试和 sudo 命令。

sudo apt install auditd audispd-plugins sudo systemctl enable --now auditd # 检查状态和事件: sudo systemctl status auditd sudo ausearch -m USER_LOGIN -ts today
监控面板通过 ausearch(/usr/sbin/ausearch)读取事件,必要时用 tail 命令从 /var/log/audit/audit.log 读取。两者都必须加入 sudoers。

23. 安装 Monit

监控各项服务(nginx、php-fpm、mysql 等),并在其崩溃时自动重启。可通过 email 发送告警。

sudo apt install monit sudo systemctl enable --now monit # 配置文件: sudo nano /etc/monit/monitrc ls /etc/monit/conf.d/
监控面板通过 monit status 获取服务列表。/etc/monit/monitrc 中必须启用 HTTP 接口(set httpd 块并带 allow localhost),否则 monit status 会返回错误。
仪表板显示“0 个受监控服务”? 有两个原因。(1) HTTP 接口未启用——monitrc 中的 set httpd 行被注释掉了(默认为 # set httpd port 2812 …)。请取消该块的注释并允许 localhost。(2) 仅启用 httpd 本身并不会监控任何东西——Monit 只统计由 check 语句声明的项;没有这些语句,即便接口正常运行列表也是空的。最小可用配置:
# /etc/monit/conf.d/00-httpd — 供 localhost 使用的 HTTP 接口: set httpd port 2812 use address localhost allow localhost # check 语句示例(监控哪些项): check process sshd with pidfile /run/sshd.pid start program = "/usr/bin/systemctl start ssh" stop program = "/usr/bin/systemctl stop ssh" check filesystem rootfs with path / if space usage > 90% then alert
sudo monit -t # 检查语法(Control file syntax OK) sudo systemctl reload monit sudo monit status
自动安装会放置一份现成的 conf.d,其中 httpd 监听 2812 并附带一组检查项——全新安装无需手动配置。
服务处于“出错”状态? 监控面板只显示状态,且有意不从 Web 面板重启服务(那将等同于在安全面板中远程执行 root 命令)。诊断与重启请通过 SSH 使用 Monit 完成:
sudo monit status <service> # 错误原因 sudo monit restart <service> # 通过 Monit 重启 # 若 Monit 无法拉起服务——请查看它自身的单元: sudo systemctl status <unit> --no-pager sudo journalctl -u <unit> -n 50 --no-pager

24. 安装 PSAD(检测端口扫描)

PSAD 分析 iptables 日志,识别端口扫描和网络攻击,并为每个来源分配威胁等级(1–5)。可与 fail2ban 和 Suricata 配合使用。

sudo apt install psad # PSAD 读取 iptables 日志 — 需要开启日志记录(UFW 会自动完成)。 # 对于纯 iptables,请在 INPUT/FORWARD 链中添加 LOG 规则。 sudo psad --sig-update sudo systemctl enable --now psad
监控面板通过 psad --Status 读取数据(需加入 sudoers)。若未开启 iptables 日志记录,此页面将为空 — 在没有扫描发生前,这属于正常现象。

25. AppArmor / SELinux(访问控制)

Mandatory Access Control(强制访问控制)会限制程序能访问哪些文件和资源,即使程序被攻破也是如此。Ubuntu/Debian 默认使用 AppArmor(通常已安装并处于启用状态)。

# AppArmor (Ubuntu/Debian): sudo apt install apparmor apparmor-utils sudo systemctl enable --now apparmor sudo aa-status # 检查配置文件
监控面板通过 aa-status 读取状态(需加入 sudoers)。它会显示处于 enforce/complain 模式的配置文件数量,以及没有配置文件的进程。

“已加载配置文件”多于 enforce + complain 属于正常现象。 AppArmor 4.x(Ubuntu 24.04 及更新版本)新增了 unconfined 模式:配置文件已加载到内核,但不施加任何限制。Ubuntu 会将数十个使用 user namespaces 的程序(浏览器、torrent 客户端等)的配置文件标记为该模式。存在此类配置文件时,“已加载配置文件”卡片会变为琥珀色并显示其数量——例如已加载 120 个、enforce 26 个时显示 unconfined: 90。真正提供保护的只有 enforce 模式下的配置文件;Ubuntu 22.04(AppArmor 3.x)没有此模式,数字始终吻合。

sudo aa-status | grep -E "profiles are" # 按模式细分 sudo aa-enforce /etc/apparmor.d/profile-name # 将配置文件切换为 enforce
将 Ubuntu 有意保留在 unconfined 模式的配置文件切换到 enforce,只有在充分了解后才应进行:它们被禁用并非出于失误,而是因为否则会导致相应程序无法正常工作。complain 模式下的配置文件则不同:规则早已写好,只是尚未生效而已。

26. 安装 debsums(软件包完整性)

debsums 会校验已安装软件包的文件是否与仓库中的校验和一致——用于发现被篡改的系统二进制文件(作为 AIDE 的补充)。完整校验需要 1–2 分钟,因此通过 cron 运行,面板则从 data/debsums/debsums.log 读取结果并自动按类别归类(只有二进制文件和库才重要)。

任务写入 root 的 cron(sudo crontab -e)。现成的封装脚本 debsums-scan.sh 放入 /usr/local/bin/(chmod +x;参见 cron 任务汇总),它会自动将报告写入面板的 data/debsums/。

sudo apt install debsums # Cron 行(每天 4:30): 30 4 * * * /usr/local/bin/debsums-scan.sh >> /path/to/monitor/logs/cron.log 2>&1

封装脚本 debsums-scan.sh 会自动找到面板的 data/——无需指定路径。

服务器上 /etc/(配置文件)和 /usr/share/(资源)的变更通常是正常的——面板会用单独的颜色标记它们。真正值得警惕的是二进制文件和库的变更(/bin、/sbin、/usr/lib 等)——“二进制文件 / 库”卡片显示的正是这些。

27. 配置 Lynis 报告

Lynis 可手动运行,也可通过 cron 运行。报告应保存到项目的 data/lynis/ 目录——监控会读取 lynis-report.dat 文件。

# 单次运行(请替换为您的面板根目录路径): sudo lynis audit system --report-file /path/to/monitor/data/lynis/lynis-report.dat # 每日审计——cron 行(现成的 lynis-scan.sh 封装脚本位于 /usr/local/bin/,参见摘要): 0 3 * * * /usr/local/bin/lynis-scan.sh >> /path/to/monitor/logs/cron.log 2>&1
首次运行后,“Lynis 审计”页面会立即显示 hardening index、警告和建议。
Lynis 页面上的“运行审计”按钮。 它可直接从面板在后台运行 lynis-scan.sh(无需等待 cron):显示“扫描中……”,并在完成后自动刷新报告。为此,Web 用户需要一条 sudoers 规则来运行该脚本——安装程序会自动将其添加到 /etc/sudoers.d/monitor。如果面板是手动安装或较早安装的,请用文件中已指定的同一用户补上这条规则:
u=$(sudo awk '/NOPASSWD/ && !/lynis-scan/ {print $1; exit}' /etc/sudoers.d/monitor) [ -n "$u" ] && echo "$u ALL=(ALL) NOPASSWD: /usr/local/bin/lynis-scan.sh" | sudo tee -a /etc/sudoers.d/monitor sudo visudo -c && sudo chmod 440 /etc/sudoers.d/monitor

28. 配置 Logwatch 报告

Logwatch 应将每日报告以 .txt 格式保存到项目的 data/logwatch/ 目录。监控面板会显示最新报告及归档。

# 每天(6:00)——cron 行(现成封装脚本 logwatch_daily.sh 位于 /usr/local/bin/,详见概览): 0 6 * * * /usr/local/bin/logwatch_daily.sh >> /path/to/monitor/logs/cron.log 2>&1

面板模块

29. 网络监控(内置)

网络监控无需安装——它是面板的内置页面。该页面从本地来源展示服务器的网络状态:

  • 接口和流量——来自 /proc/net/dev;
  • 链路状态(UP/DOWN)和 IP——通过 ip;
  • 连接和监听端口——通过 ss;
  • 过去 24 小时的内核网络事件——通过 journalctl -k。

前三个来源无需 sudo即可工作,因此接口、流量、连接和端口会立即显示。“内核事件”模块使用 journalctl -k——它通过 systemd-journal 组读取(“sudo 配置”,第 2 步),无需 sudo。检查 Web 用户是否都能访问:

# 以 www-data 身份检查(PHP 在该用户下运行): sudo -u www-data bash -c 'cat /proc/net/dev' sudo -u www-data bash -c 'ip -o link show' sudo -u www-data bash -c 'ss -s' sudo -u www-data journalctl -k --no-pager -n 5
“内核网络事件”模块显示内核网络栈的事件(链路 up/down 切换、载波错误、“network unreachable”)。防火墙的 UFW BLOCK 记录不会出现在这里——它们位于“UFW 防火墙”和“攻击地图”页面。模块为空并显示绿色对勾 = 一天内没有网络故障。

30. 磁盘与 SMART

内置页面会显示三项内容:

  • 文件系统 — 分区占用情况(df);占用率 ≥90% 时刻度条变红;
  • 存储设备 — 磁盘列表(lsblk),仅显示真实设备(loop/snap 已隐藏);
  • 健康状况(SMART) — 磁盘状态与属性(smartctl)。

剩余空间和设备列表无需配置即可使用。SMART 需要安装 smartmontools 软件包。Web 进程无法直接访问磁盘设备,因此 SMART 数据通过 cron 采集并写入 data/disk/smart.txt 文件,再由面板读取。

该任务需加入 root 的 cron(sudo crontab -e)。现成的封装脚本 smart-scan.sh 放入 /usr/local/bin/(chmod +x;参见 cron 任务汇总),会自动写入面板的 data/disk/。

sudo apt install smartmontools # Cron 行(每 30 分钟一次): */30 * * * * /usr/local/bin/smart-scan.sh >> /path/to/monitor/logs/cron.log 2>&1

封装脚本 smart-scan.sh 会自动找到面板的 data/ —— 无需手动填写路径。内部使用 lsblk -e7,11 排除 loop/cdrom。

在虚拟磁盘(QEMU/KVM 等)上,通常只能获取总体状态 “健康:OK”,而温度、通电时长和重映射扇区等可能为空 —— 这属于正常现象。在物理服务器上则会显示全部属性。

31. 性能(CPU/RAM/网络/磁盘)

本页展示服务器过去 24 小时的负载历史——Load Average、CPU 占用与 I/O 等待、RAM/Swap、网络流量(接收/发送)、磁盘 I/O(读取/写入)、磁盘与 inode 占用、打开的文件描述符和 MySQL 连接数,以及当前的 TCP 连接数和进程数。 默认是 24 小时窗口:用顶部的按钮可切换到 7 天或 30 天。

数据由 cron/collect_metrics.php 采集——每 5 分钟将一份计数器的“原始”快照(/proc/loadavg、/proc/meminfo、/proc/stat、/proc/net/dev、/proc/diskstats、df/df -i、/proc/sys/fs/file-nr、SHOW GLOBAL STATUS LIKE 'Threads_connected')写入数据库表 system_metrics;百分比和速率由本页根据相邻两份快照的差值自行计算(磁盘/inode/描述符占用及 MySQL 连接数为瞬时值,不做换算)。无需 sudo——这些数据源无需 root 权限即可读取。

两级保存深度。 五分钟一次的“原始”快照保存一天(再加一小时余量,好让图表上最早的那个点仍有前一个点用于计算)。每结束一个小时就折叠成一行;这份按小时的数据保存 30 天,“7 天”和“30 天”视图读取的正是它。这样一个月的历史只需约 720 行,而不是大约 8600 行。

在 7 天和 30 天的区间里显示的是按小时的平均值,而不是逐次测量 —— 五分钟的短暂尖峰在这个尺度上会被抹平,页面上也有相应提示。对于 Load Average,小时行同时保存了均值和该小时的峰值,所以图上会有两条线。刚更新完时,长区间是空的(“该区间暂无历史”)—— 按小时的数据从这一刻起开始积累。
# Cron 行(每 5 分钟): */5 * * * * /usr/local/bin/collect-metrics-all.sh >> /path/to/monitor/logs/cron.log 2>&1

封装脚本 collect-metrics-all.sh(见 cron 任务汇总)会自动查找服务器上所有已安装的面板实例,并以站点所有者身份运行每个实例的 cron/collect_metrics.php。

在采集器至少运行两次之前(安装后的前约 10 分钟),本页会显示“正在采集数据”——图表至少需要一对相邻数据点才能计算速率和百分比。

占用最高的进程。 图表下方有两张表,各列出 CPU 占用最高的五个进程:

  • “最近一次测量时的进程” —— 最后一次快照那一刻是什么在给服务器加压(时间写在卡片标题里);
  • “当天峰值时刻的进程” —— 同样五个,但取自最近 24 小时内 Load Average 最高的那次测量。当图上的尖峰早已过去时,它回答的正是“夜里那阵是怎么回事”。

即使图表显示的是一个月,这两张表描述的也始终是最近 24 小时:只有五分钟一次的记录才带有进程快照,而它们保存不超过一天。

测量自身的进程(ps、负责采集的 PHP 脚本,以及它之上的 cron/sudo 链)会从列表中排除 —— 否则它们会占据每一次快照的最前面:ps 在刚启动的那一刻显示的 CPU 接近 100 %。而同一分钟内运行的其他 cron 任务照常出现,这张表存在的意义正在于此。

负载告警(“设置” → “负载告警”一节)—— 当 CPU/内存/磁盘/inode 超过阈值时,面板会通过所有已开启的通道发送通知(Telegram、电子邮件、webhook、推送 —— 与每日报告用的是同一批通道,无需为告警另行开启),指标恢复正常时再发一条。在阈值持续被突破期间不会反复打扰:下一条通知只会在经历“恢复正常 → 再次超标”的循环之后才发出。

阈值由同一个 collect_metrics.php 在每次运行时(每 5 分钟)检查——无需单独的 cron。“是否已通知”的状态保存在 data/alerts_state.json 中,阈值则保存在面板设置里。

32. 安全事件(每 5 分钟一次的传感器)

每日报告回答的是“服务器状况如何”。传感器回答的是另一个问题 —— “刚刚发生了什么”。它每 5 分钟询问一次那些反应快的数据源,只记录自上次检查以来发生变化的内容;菜单中的“安全事件”页面会显示发现列表。

慢速检查 —— Lynis、debsums、软件包更新、SSL 有效期 —— 传感器不去碰:这些数字反正一天才变一次,仍归每日报告负责。

它会注意到什么 —— 九个来源:

  • Fail2ban —— 新的封禁(比较的是被封地址的清单,因此解封不算事件,而一条仍在生效的封禁只会报告一次);
  • CrowdSec —— 新的决策;
  • Falco —— Emergency、Alert 和 Critical 级别的事件;
  • Suricata —— 新的告警;标记 [Priority: 1] 会把该发现提升为严重;
  • ClamAV —— 扫描日志中新增的 FOUND 行;
  • AIDE —— 依据最近一次检查得出的文件变更(也就是说,不会比它自己按计划运行的频率更密);
  • SSH —— 以 root 身份成功登录(严重),以及来自此前未见过地址的登录(警告);
  • 防护服务 —— UFW、Fail2ban、CrowdSec、Suricata、Falco:服务停止时一条消息,重新运行时再一条;
  • 端口 —— 变得可从外部访问的端口;若其背后是数据库或缓存,则判为严重。

未安装的工具,或面板读不到其日志的工具,只会保持沉默 —— 这既不是错误,也不是事件。

第一次检查什么也不会发送。 每个来源都保存着“已经读到哪里”的标记(文件 data/secwatch_state.json),首次运行只写下这个标记。否则一开启监视,就会收到关于日志里积攒了好几个月的全部内容的通知。

配置 —— “设置” → “事件监视”:

  • 开启监视 —— 只要还关着,cron 任务就空转,列表也不会有新内容;
  • 通知起始级别 —— “警告”或“严重”。低于该级别的发现仍会进入列表,但不会进入通知;
  • 间隔(分钟) —— 两次通知之间的最短间隔,默认为 15;
  • 安静时段 —— 形如 23-7 的时段,在此期间不发送警告。
严重级别的发现会立即发出 —— 间隔和安静时段都拦不住它们:5 分钟传感器存在的意义正是为此。不过保险还是有的:每小时最多 6 条通知,以防某个来源开始“抽风”。

通知走的通道与每日报告相同(Telegram、电子邮件、webhook、推送)—— 无需另行开启。所有发现无论通知是否发出都会写入列表,并保存 90 天。

事件列表。 筛选条件:时间范围(24 小时、7 天、30 天或 90 天)、重要程度和来源;显示最近 500 条记录。“立即检查”按钮执行的与 cron 完全相同 —— 刚配置好时很方便,不必等上五分钟;当没有发出通知时,它还会说明原因(“低于重要程度阈值”“安静时段”“未配置任何通知通道”)。菜单项旁的红色计数是最近 24 小时内的严重事件数量。

地址卡片。 点击事件列表中的一行,或关联地址板块中的一行,就会打开关于某个地址的全部已知信息:其事件的时间顺序、国家,以及第一行给出的结论。当事件恰好只涉及一个地址时,列表行会打开卡片;若涉及多个地址,则行内的地址本身就是链接。结论共有四种:

  • 可能已被访问:该地址有过成功登录,并且同一地址也被其他防护抓到,或者登录后随即触发了 AIDE、ClamAV,或有新端口被打开。这是唯一值得立刻核实的情况;
  • 来自该地址的登录:只有一次登录,此外别无记录。通常是所有者从新地点登录,因此卡片不会标红;
  • 已封禁:地址已被抓到并封禁,防护已经完成了它的工作,无需处理;
  • 需要查看:有检出但没有封禁。若该地址反复出现,请手动封禁。

时间顺序下方是同一时间服务器上发生了什么板块:这些事件根本没有地址。AIDE 不知道是谁改了文件,ClamAV 不知道是谁带来了被感染的文件。这里显示的是落在该地址活动期间以及其后六小时内的事件。正是登录与紧随其后的动静合在一起,才把一次尝试和真正的访问区分开。

关联是依据地址建立的。 更换地址的攻击者会把这条线索切断。卡片只展示防护看到的内容,并不承担区分入侵与您本人从新地点登录的责任。

计划任务。 该任务写入 root 的 crontab(sudo crontab -e),与指标采集错开 2 分钟,避免两者同时启动:

# 每 5 分钟 —— 检查安全事件 2-57/5 * * * * /usr/local/bin/security-watch-all.sh >> /path/to/monitor/logs/cron.log 2>&1
只能通过包装脚本运行,切勿直接调用 php cron/security_watch.php。 包装脚本会找出服务器上面板的所有安装,并以站点属主的身份执行检查 —— 使用的正是面板从网页端所用的那套 sudo 权限。若从 root 的 crontab 直接调用,会生成属主为 root 的状态文件 data/secwatch_state.json,面板里的“立即检查”按钮就会失效。
如果服务器是用自动安装准备的,该任务已经添加好了 —— 只需在设置中开启监视即可。

33. Web 服务器:请求与响应码

“Web 服务器”页面解析访问日志,显示共有多少请求、其中多少以错误告终,以及是谁发来的。ModSecurity 回答的是“什么被拦下了”,这个页面回答的是“到底有什么到达了站点”。

页面上能看到:请求数、RPS、错误占比、单独的 5xx 计数以及发出的流量;按小时的请求图;2xx/3xx/4xx/5xx 各码的分布;高频地址、高频 IP、高频错误(状态码加路径)和客户端(User-Agent)。时间范围为 24 小时或 7 天。

数据源是 combined 格式的访问日志,因此 Nginx 和 Apache 都适用,也不需要额外模块。面板会在以下标准文件中查找日志:

  • /var/log/nginx/access.log
  • /var/log/apache2/access.log
  • /var/log/apache2/other_vhosts_access.log
  • /var/log/httpd/access_log

除此之外,面板还会检查 /var/log/nginx/、/var/log/apache2/ 和 /var/log/httpd/ 中名为 *access*.log 和 *requests*.log 的文件。这样,站点通过 CustomLog 指令写入自有文件的日志也能被找到,例如由 Arcivéo 安装程序创建的 arciveo-monitor_requests.log。

只读取一份日志:当 Nginx 位于 Apache 之前时,把两份日志加起来会把每个请求算成两次。面板取用上面清单中最近一天内有写入的第一份日志(按名称模式找到的文件随后按从新到旧的顺序尝试);如果一天内没有任何日志被写入,则取最新的非空日志。因此,被 logrotate 清空的日志或已停用的 Web 服务器遗留的日志不会再遮住正在使用的日志。读取的是日志的最后一段(3 MB),结果缓存 5 分钟 —— 因此在访问量大的站点上,所选时间范围可能未被完全覆盖;实际覆盖到的范围标注在图表上方。

“面板无法读取 Web 服务器日志”。 原因几乎总是权限:日志及其目录属于 adm 组,而 Web 用户并不在该组里。Arcivéo 安装程序会把 Web 用户加入该组;如果是手动安装,可以用一个属于 root 的小包装脚本解决,它只被允许做一件事 —— 把日志打印出来(ModSecurity 页面也是同样的做法)。包装脚本按照与面板相同的规则选择日志。该命令在页面上就有,这里也列一遍。请整段粘贴 —— 这是一条命令:
sudo tee /usr/local/bin/monitor-weblog >/dev/null <<'EOF' && sudo chmod 755 /usr/local/bin/monitor-weblog && echo 'www-data ALL=(root) NOPASSWD: /usr/local/bin/monitor-weblog' | sudo tee /etc/sudoers.d/monitor-weblog >/dev/null && sudo visudo -c #!/bin/sh L="/var/log/nginx/access.log /var/log/apache2/access.log /var/log/apache2/other_vhosts_access.log /var/log/httpd/access_log $(ls -t /var/log/nginx/*access*.log /var/log/apache2/*access*.log /var/log/apache2/*requests*.log /var/log/httpd/*access_log 2>/dev/null)" for f in $L; do [ -r "$f" ] && [ -n "$(find "$f" -size +0 -mmin -1440 2>/dev/null)" ] && exec tail -c 3000000 "$f"; done for f in $(ls -t $L 2>/dev/null); do [ -r "$f" ] && [ -s "$f" ] && exec tail -c 3000000 "$f"; done EOF

Web 用户不一定是 www-data —— 请查看站点的 PHP-FPM 进程池以哪个用户运行(ps -o user= -C php-fpm),并把该用户填进 sudoers 那一行。若日志名称中既不含 access 也不含 requests,请把它的路径添加到包装脚本内 L=… 这一行的开头。

“所选时间范围内没有记录” —— 日志已经读到了,只是里面没有符合条件的行:要么站点确实没人打开,要么日志刚刚轮转过(logrotate),从头开始了。

已知漏洞的利用尝试。面板将日志中的请求与约二十种常见漏洞利用的特征进行比对:PHP-CGI、Apache 目录遍历、PHPUnit、Log4Shell,以及路由器、摄像头和 VPN 网关的漏洞。对每次尝试,面板都会依据事实说明它能否在本服务器上得逞:已安装的 Apache 和 PHP 版本、是否装有 Java,以及服务器对该文件请求返回的响应码。只有确实可能得逞的项目才会标红。

Web 服务器加固。面板通过 127.0.0.1 请求服务器上的每个网站,检查访客收到的内容:Web 服务器和 PHP 版本是否可见、是否开启了 TRACE 方法、是否具备防护响应头 Strict-Transport-Security, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy 和 Content-Security-Policy。网站列表来自打开面板所用的地址,以及面板可读取时的虚拟主机文件。结果每 10 分钟更新一次。

如有缺失,表格下方会出现一条命令。它会创建单独的文件 zz-arciveo-hardening.conf,并且只在配置测试通过后才重新加载 Web 服务器;网站已自行发送的响应头不会重复。CSP 不以一行的形式提供:策略需要针对具体网站编写,否则网站将无法加载自己的脚本、样式和字体。在 Apache 上撤销更改:

sudo a2disconf zz-arciveo-hardening && sudo systemctl reload apache2
如果 Web 服务器版本已经隐藏,错误页面上的签名(ServerSignature)不会被标记:没有版本号时,它只显示服务器名称和主机名。

34. 攻击地图(GeoIP)

“攻击地图”页面通过 geoiplookup 命令根据 IP 判定所属国家。如未安装 GeoIP 包,将无法识别国家,地图上也不会出现标记点:

sudo apt install geoip-bin geoip-database # 检查: geoiplookup 8.8.8.8
无需 sudo——数据库 /usr/share/GeoIP/GeoIP.dat 所有人均可读取,结果缓存于 tmp/geoip_cache.json。地图本身(Leaflet + OpenStreetMap 瓦片)在浏览器中加载——打开面板的电脑需连接互联网。

35. 外部暴露、更新与自动更新

两张内置的仪表盘卡片,展示的不是工具的“开/关”,而是服务器的真实安全状态。无需安装,本地读取,无需 sudo。

外部暴露 — 有多少服务监听所有网卡(0.0.0.0/[::])并可从外部访问。若数据库或缓存(MySQL、PostgreSQL、Redis、MongoDB、Memcached、Elasticsearch)向外暴露,会以红色高亮 — 这是直接的漏洞(安全评分 −10)。来源:ss -tuln。

若卡片为红色 — 请对外关闭数据库:绑定到 127.0.0.1(MySQL/PostgreSQL 配置中的 bind-address,Redis 中的 bind 127.0.0.1),或在 UFW 中关闭该端口。
“端口开放”≠“可从外部访问”。监听 127.0.0.1(loopback)的服务仅服务器自身可见 — 即使端口“开放”,外部也无法访问。因此绑定到 loopback、监听 25 端口的 Postfix 是安全的:自动配置会设置 inet_interfaces = loopback-only(外加中性的 smtpd_banner — 消除 Lynis 关于版本泄露的 MAIL-8818 提示)。“外部暴露”卡片仅将监听 0.0.0.0/[::] 的服务计为对外暴露;loopback 服务不计入其中。
手动处理 Lynis MAIL-8818(若您自行搭建了邮件服务):在 /etc/postfix/main.cf 中设置 smtpd_banner = $myhostname ESMTP(不含版本和操作系统)与 inet_interfaces = loopback-only,然后执行 sudo systemctl restart postfix。

安全更新 — 有多少安全补丁待安装,以及内核更新后是否需要重启(存在补丁时安全评分 −5)。来源:/usr/lib/update-notifier/apt-check、文件 /var/run/reboot-required。详细列表见“安全更新”页面。

# 安装更新: sudo apt update && sudo apt upgrade # 检查哪些服务对外监听: ss -tuln | grep -E '0\.0\.0\.0|\[::\]'
更新卡片在 Ubuntu/Debian 上工作(update-notifier-common)。若缺少 apt-check — 监控面板会通过 apt-get -s upgrade 统计补丁。

安全更新自动安装(unattended-upgrades) — 在“安全更新”页面有一张单独的卡片,显示是否已启用安全补丁的自动安装以及上次运行时间。无需 sudo — 状态通过 apt-config dump 读取。

sudo apt install unattended-upgrades sudo dpkg-reconfigure -plow unattended-upgrades # 启用 # 检查是否已启用: apt-config dump | grep Unattended-Upgrade

维护

36. 备份

备份是最重要的保障:数据丢失比任何入侵都更可怕。需要两样东西——服务器/网站备份,以及单独的面板数据库备份(其中包含用户、WebAuthn 密钥、设置和许可证)。

方案 A —— HestiaCP:在用户的 Backup 选项卡 → 点击创建备份按钮(或在服务器设置中按计划自动执行)。备份会包含网站及其数据库。

方案 B —— 手动(cron):转储数据库 + 打包面板的 data/ 目录:

# root 定时任务(sudo crontab -e)——每天 2:30 备份(请替换为您自己的名称/路径): 30 2 * * * mysqldump -u root MY_DB | gzip > /var/backups/monitor-db-$(date +\%F).sql.gz 40 2 * * * tar czf /var/backups/monitor-data-$(date +\%F).tar.gz -C /path/to/monitor data # 删除 14 天以上的归档文件: 0 3 * * * find /var/backups -name 'monitor-*' -mtime +14 -delete
同一台服务器上的备份能挽救误操作,但无法应对服务器丢失。请将归档文件复制到外部存储(另一台服务器、S3、用 rclone 传到云端)。并验证恢复确实可用。

37. 面板的更新与迁移

更新到新版本。 请先做好备份。然后重新上传代码文件,同时保留您的数据:

  • 覆盖(代码):public/、includes/、assets/、cron/、database/,以及根目录下的 .htaccess(前端控制器——路由不能沿用旧版本)、manifest.json、sw.js;
  • 请勿改动: config.php(数据库信息)、data/(报告)、logs/、tmp/(会话与缓存)。
# 上传后——清除 PHP 缓存(若启用了 opcache): sudo systemctl reload php*-fpm
FileZilla 报错 SSH_FX_PERMISSION_DENIED——Permission denied。面板文件归 www-data 所有(安装时就是这样设置的),而 SFTP 客户端是以您自己的用户连接的,没有写入权限。把整个面板都交给 www-data“为了能用”正是导致这个错误的原因;下面给出三种方法,任何一种都能解决问题。
# 方案 A(推荐)——区分属主:代码归您,工作目录归 Web 服务器。 # Web 服务器完全不会获得对面板代码的写入权限: sudo chown -R deploy:www-data /path/to/monitor sudo chown -R www-data:www-data /path/to/monitor/data /path/to/monitor/tmp /path/to/monitor/logs sudo find /path/to/monitor -type d -exec chmod 755 {} \; sudo find /path/to/monitor -type f -exec chmod 644 {} \; sudo chmod 750 /path/to/monitor/data /path/to/monitor/tmp /path/to/monitor/logs sudo chmod 640 /path/to/monitor/config.php # 方案 B——在当前属主之上叠加 ACL(不迁移任何东西): sudo apt install -y acl sudo setfacl -R -m u:deploy:rwX /path/to/monitor sudo setfacl -R -d -m u:deploy:rwX /path/to/monitor # 方案 C——通过 www-data 组。更简单,但 Web 服务器同样获得对面板文件的 # 写入权限(PHP 出现漏洞时代码可能被替换): sudo usermod -aG www-data deploy sudo find /path/to/monitor -type d -exec chmod 2775 {} \; sudo find /path/to/monitor -type f -exec chmod 664 {} \; sudo chmod 640 /path/to/monitor/config.php sudo chmod 2750 /path/to/monitor/data /path/to/monitor/tmp /path/to/monitor/logs
为什么方案 A 是安全的。面板只写入三个目录——data/(报告)、tmp/(会话与缓存)、logs/;它们仍归 www-data 所有。其余都是代码,Web 服务器只需读取,而 www-data 组凭 644 权限即可读取。附带的好处是:PHP 出现漏洞时也无法改写面板文件。在主机面板(HestiaCP 之类)上无需方案 A:那里站点文件本就归您用来 SFTP 登录的账户所有,而 Web 服务器按组读取它们。
方案 B 的陷阱:之后对文件执行的任何 chmod 都会重置 ACL 掩码,访问权限便悄然消失。如果在“整理权限”之后上传再次遇到 Permission denied——请重新执行两条 setfacl 命令。
方案 C 中的 2 即 setgid:通过 SFTP 上传的文件仍留在 www-data 组中,否则面板无法覆盖它们。采用方案 C 后,请在 FileZilla 中重新连接——新的用户组只在重新登录后生效。检查:id deploy(应出现 www-data 组)和 ls -ld /path/to/monitor(drwxrwsr-x——字母 s 表示 setgid 已设置)。

迁移到另一台服务器:

  1. 在新服务器上部署站点 + HTTPS(参见手动安装页面)。
  2. 复制面板的所有文件,连同 config.php、data/。
  3. 迁移数据库:在旧服务器上执行 mysqldump → 导入到新服务器;并在 config.php 中更新数据库信息。
  4. 在新服务器上重新配置:sudoers、adm 组成员身份、cron 任务。
  5. 许可证绑定到域名——如果域名相同,密钥将继续有效。

38. 在这台服务器上再建第二个站点(再加一个域名)

面板已经在运行,而同一台服务器上还需要一个站点——在监控面板旁边使用自己的域名。自动配置后的服务器就是普通的 Apache + PHP-FPM,因此站点只需要三样东西:自己的目录、自己的 vhost 和自己的证书。面板本身不用动:它监控的是整台服务器,不需要为第二个域名再装一份。

本节适用于没有主机管理面板的服务器。如果已安装 HestiaCP 或类似面板,请在面板里添加域名,而不要手工配置。如果计划托管很多站点(还要带邮件),请在自动配置之前安装面板:脚本会检测到它并切换到增量模式;而事后安装的面板会重建 Apache、防火墙和邮件的配置。
命令里要替换什么。 site2.example.com 是新站点的地址。它既可以是独立域名(mycompany.com),也可以是子域名(shop.mycompany.com)——对 Apache 没有区别,只有 DNS 记录(第 1 步)和证书签发(第 5 步)不同。site2 只是站点的简称:目录 /var/www/site2 和配置文件 site2.conf 用的就是这个名字。它与域名没有任何关系,Apache 只从 ServerName 读取地址;名字可以随意——例如 shop.mycompany.com 用 shop 就很方便:目录 /var/www/shop,配置 shop.conf。

1. DNS。 在域名注册商的 DNS 面板中创建指向本服务器 IP 的 A 记录:

# 子域名 (shop.mycompany.com): 类型:A 名称:shop 值:203.0.113.10 TTL: 3600 # 独立域名 (mycompany.com) —— 两条记录,让带 www 的地址也能用: 类型:A 名称:@ 值:203.0.113.10 TTL: 3600 类型:A 名称:www 值:203.0.113.10 TTL: 3600

几分钟后检查:dig +short site2.example.com 应当返回服务器的 IP。

2. 站点目录。

sudo mkdir -p /var/www/site2 sudo chown -R www-data:www-data /var/www/site2

3. vhost。 整段命令一次性粘贴到终端——PHP-FPM 套接字路径会自动识别:

PHPSOCK=$(ls -1 /run/php/php*-fpm.sock 2>/dev/null | head -1) # 自动识别 PHP-FPM 套接字 sudo tee /etc/apache2/sites-available/site2.conf > /dev/null <<'EOF' <VirtualHost *:80> ServerName site2.example.com # 独立域名:去掉下面一行的 #,让站点也能用 www 打开。 # 子域名不需要这一行。 #ServerAlias www.site2.example.com DocumentRoot /var/www/site2 <Directory /var/www/site2> Options -Indexes +FollowSymLinks AllowOverride All Require all granted </Directory> <FilesMatch \.php$> SetHandler "proxy:unix:__PHPSOCK__|fcgi://localhost" </FilesMatch> ErrorLog ${APACHE_LOG_DIR}/site2_error.log CustomLog ${APACHE_LOG_DIR}/site2_access.log combined </VirtualHost> EOF sudo sed -i "s#__PHPSOCK__#${PHPSOCK}#" /etc/apache2/sites-available/site2.conf

4. 启用站点,并在重载前检查配置:

sudo a2ensite site2.conf sudo apache2ctl configtest # 预期结果: Syntax OK sudo systemctl reload apache2

5. SSL。 自动配置已经安装好 certbot;此时 DNS 必须已经指向服务器:

# 子域名 —— 一个地址: sudo certbot --apache -d site2.example.com # 独立域名 —— 直接连 www 一起(第 1 步的两条 A 记录都必须指向服务器, # 并且 www 要写在 ServerAlias 中,否则 certbot 无法验证第二个地址): sudo certbot --apache -d site2.example.com -d www.site2.example.com

第三个以及后续域名的添加方式相同——更换域名和目录后重复第 1—5 步。

谁来响应“外来”请求。 如果 Host 与任何 ServerName 都不匹配(通过 IP 访问,或别人的域名指向了您的服务器),Apache 会返回 /etc/apache2/sites-enabled/ 中按字母顺序排在最前的已启用站点。标准的 000-default 已被自动配置停用,因此这个“值班”角色留给了面板的 vhost(arciveo-monitor.conf)——这不影响用各自域名打开的站点。若希望通过 IP 打开另一个站点,请把它的文件命名成按字母顺序更靠前的名字(例如 000-site2.conf)。
防护是整台服务器级别的:UFW、Fail2ban、CrowdSec、ModSecurity 和 Suricata 已经在运行,会自动覆盖新站点,无需单独配置。80/443 端口也已开放。站点会有自己的日志:/var/log/apache2/ 下的 site2_error.log 和 site2_access.log。
不要把新站点的目录放进面板的目录里——面板的网站根目录由它自己的 .htaccess(前端控制器)管理,嵌套在里面的站点会返回与预期不同的内容。请把站点并列放置:/var/www/monitor、/var/www/site2。

39. 恢复访问权限(丢失安全密钥、密码、IP 封锁)

如果您无法登录——所有问题都可以直接在服务器的数据库中修复。打开数据库(名称见 config.php):

sudo mysql MY_DB

丢失 WebAuthn 安全密钥(无法通过第二因素)——请关闭 2FA,使用密码登录,然后注册新的安全密钥:

UPDATE users SET webauthn_enabled = 0;

忘记密码——请设置新的哈希值(在服务器上生成后替换进去):

# 生成新密码的哈希值: php -r "echo password_hash('NEW_PASSWORD', PASSWORD_BCRYPT), \"\n\";" # 在数据库中(填入得到的哈希值): # UPDATE users SET password = '$2y$10$...' WHERE username = 'admin';

用 IP 过滤把自己封了——请关闭该限制:

UPDATE settings SET value = '0' WHERE name = 'ip_restriction_enabled';
您始终可以访问数据库:在服务器上运行 sudo mysql,或使用 phpMyAdmin / 主机面板中的数据库版块。恢复后请重新启用 WebAuthn 和 IP 过滤。

40. 所有 cron 任务集中一处

任务汇总位于服务器的 root crontab中(通过 sudo crontab -e 添加)。只保留您所用工具对应的行;并按您的服务器调整路径。

# 监控面板的服务器 crontab(root)——通过以下命令录入:sudo crontab -e # 01:30 — ClamAV 扫描危险路径(web、home、temp)→ 卡片“已检查文件数”和“上次扫描” 30 1 * * * /usr/local/bin/clamav-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 02:00 — AIDE 文件完整性检查(需显式指定 --config) 0 2 * * * /usr/bin/aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1 # 启动时——恢复 /var/lib/aide 的权限(软件包的 tmpfiles 文件 # aide-common.conf 会把权限重置为 0700,导致面板无法看到数据库) @reboot chmod 755 /var/lib/aide # 03:00 — Lynis 安全审计 0 3 * * * /usr/local/bin/lynis-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 04:00 — 更新 ipsum 封禁列表(level 1) 0 4 * * * /usr/local/bin/load-ipsum.sh >> /path/to/monitor/logs/cron.log 2>&1 # 启动时的 ipsum 集合由 ipsum-load.service 加载(在防火墙之前,否则 # UFW 在 before.rules 中看不到该集合)——不是 cron。这里仅有上面的每日刷新。 # 06:00 — Logwatch 报告 0 6 * * * /usr/local/bin/logwatch_daily.sh >> /path/to/monitor/logs/cron.log 2>&1 # 每 30 分钟——SMART 磁盘检查 */30 * * * * /usr/local/bin/smart-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 04:30 — debsums 软件包完整性 30 4 * * * /usr/local/bin/debsums-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 08:00 — 定时报告发送至 Email 和 Telegram 0 8 * * * /usr/local/bin/daily-report-all.sh >> /path/to/monitor/logs/cron.log 2>&1 # 每小时 — 刷新软件包列表(用于“安全更新”卡片) 0 * * * * /usr/bin/apt-get update -qq >/dev/null 2>&1 # 每 5 分钟——采集资源快照(CPU/RAM/网络/磁盘),用于“性能”页面 */5 * * * * /usr/local/bin/collect-metrics-all.sh >> /path/to/monitor/logs/cron.log 2>&1 # 每 5 分钟,与上面的快照错开 —— 安全事件传感器: # 新的封禁、IDS 告警、停止的防护服务、新开放的端口。 # 在设置中开启监视之前,此任务什么也不做。 2-57/5 * * * * /usr/local/bin/security-watch-all.sh >> /path/to/monitor/logs/cron.log 2>&1 # 每 15 分钟 —— 向个人中心发送服务器概要,以便在一个列表中看到多台服务器。 # 在设置中开启概要之前,此任务什么也不做。 3,18,33,48 * * * * /usr/local/bin/fleet-push-all.sh >> /path/to/monitor/logs/cron.log 2>&1
各项详情见对应章节。备份任务(上一章节)也添加到这同一个 crontab 中。修改后请检查:sudo crontab -l,并确认 cron 服务处于活动状态。
cron 时间 = 服务器时区,而非 config.php 中的 TIMEZONE。 TIMEZONE 常量只影响 PHP(面板如何显示日期),但 cron 守护进程按操作系统的系统时间运行任务。如果服务器时区与您不一致,“08:00”的报告就会在错误的时间送达。示例:服务器处于另一时区(Asia/Bangkok,UTC+7),而您在上海(UTC+8)→“08:00”的报告在您本地时间的 09:00 才送达。请检查,并在需要时把系统时区改为您所在时区:
# 查看服务器当前时区: timedatectl # 设置您所在时区(示例)并重启 cron: sudo timedatectl set-timezone Asia/Shanghai sudo systemctl restart cron
之后 0 8 * * * 这一行就会在本地时间 08:00 触发。否则就得去移动 cron 本身,但夏令时/冬令时切换时偏移又会再次错开——因此正确做法是配置系统时区。
现成的包装脚本。 它们的可用副本和 crontab 样例(crontab.txt)位于项目旁的 system/ 文件夹中,在 public_html 之外。这不是网站的一部分——无需上传到 web 根目录;请按系统路径放置到服务器上(如上面的 crontab):
  • lynis-scan.sh → /usr/local/bin/(chmod +x)——运行 lynis audit system,扫描期间设置标志 /tmp/lynis-running,并把 lynis-report.dat 复制到面板的 data/lynis/;
  • logwatch_daily.sh → /usr/local/bin/(chmod +x)——在 data/logwatch/ 中生成每日 Logwatch 报告(sshd、fail2ban、sudo、postfix);
  • smart-scan.sh → /usr/local/bin/(chmod +x)——把磁盘状态(smartctl)记录到 data/disk/;
  • debsums-scan.sh → /usr/local/bin/(chmod +x)——在 data/debsums/ 中检查软件包完整性(debsums);
  • clamav-scan.sh → /usr/local/bin/(chmod +x)——ClamAV 对危险路径(web、home、temp)进行杀毒扫描;把汇总写入 /var/log/clamav/scan.log,ClamAV 页面从此处读取(上面 crontab 中的 01:30 行);
  • load-ipsum.sh → /usr/local/bin/(chmod +x)——就地更新 ipset 集合 ipsum(level 1),不中断生效中的防火墙规则(上面 crontab 中的 04:00 行);
  • daily-report-all.sh → /usr/local/bin/(chmod +x)——运行面板的报告 cron/daily_report.php(上面 crontab 中的 08:00 行);
  • daily_report.php ——已包含在面板中(cron/daily_report.php),通过 daily-report-all.sh 运行,无需单独安装;
  • collect-metrics-all.sh → /usr/local/bin/(chmod +x)——运行面板的 cron/collect_metrics.php(“性能”页面,上面 crontab 中的 */5 行);collect_metrics.php 已包含在面板中,无需单独安装;
  • security-watch-all.sh → /usr/local/bin/(chmod +x)—— 以站点属主的身份运行面板的 cron/security_watch.php(“安全事件”页面,上面 crontab 中 2-57/5 那一行);security_watch.php 已随面板提供,无需单独安装;
  • fleet-push-all.sh → /usr/local/bin/(chmod +x)—— 通过面板的 cron/fleet_push.php 把服务器概要发送到个人中心(上面 3,18,33,48 那一行);
  • crontab.txt (system/cron/) ——任务样例;把需要的行通过 sudo crontab -e 录入。
crontab 中的脚本路径必须与您实际放置的位置一致。
如何把脚本放入 /usr/local/bin/。 无法直接从 FileZilla 写入该目录——它归 root 所有,SFTP 客户端会收到 SSH_FX_PERMISSION_DENIED。步骤如下:先把文件上传到 /tmp(所有人都可写入),再用一条命令把它移动到位:
# 在 FileZilla:在“远程站点”栏输入 /tmp 并把脚本上传到那里, # 然后通过 SSH(install 会直接设置所有者和权限,无需 chown/chmod): sudo install -o root -g root -m 755 /tmp/lynis-scan.sh /usr/local/bin/lynis-scan.sh rm -f /tmp/lynis-scan.sh # 检查:文件已就位,权限为 rwxr-xr-x,语法完整 bash -n /usr/local/bin/lynis-scan.sh && ls -l /usr/local/bin/lynis-scan.sh
不要弄错目录:需要的是服务器根目录下的 /tmp——不是 /var/tmp,也不是面板内部的 tmp/(后者归 www-data 所有,对您的用户是封闭的)。在 FileZilla 的目录树中,/tmp 是顶层分支,与 var 并列,而不在它内部。
用自动配置搭建的服务器? 这些包装脚本及其 cron 任务已由脚本自动安装(位于 /usr/local/bin/,日志为 /var/log/arciveo-cron.log)——无需手动操作。
脚本如何查找面板。 这些包装脚本与域名无关:它们通过遍历 /home/*/web/*/public_html 和 /var/www/* 来查找面板安装位置,并把报告放到各自的 data/。如果面板位于其他路径——请把它加到脚本内的 for app in … 行中,否则 Lynis/SMART/debsums/Logwatch 的报告不会进入面板。
cron.log 与访问权限。 logs/cron.log 文件由 root crontab 首次创建——它将归 root 所有,面板中的“cron 日志”标签页既无法读取也无法清空。请提前以 web 用户身份创建该文件(网站目录的所有者;在 HestiaCP 上是相应账户,例如 admin)——这样 root crontab 就只会追加内容,而不更改所有者:
# 以 web 用户身份提前创建(在添加 cron 行之前): sudo -u OWNER touch /path/to/monitor/logs/cron.log # 如果 cron.log 已由 root crontab 创建——把它交给 web 用户: sudo chown OWNER:OWNER /path/to/monitor/logs/cron.log sudo chmod 644 /path/to/monitor/logs/cron.log
查看目录所有者:stat -c %U /path/to/monitor。
从面板管理。 “系统”板块中有一个 “Crontab”页面——无需 SSH 即可查看和添加任务。面板只编辑通过它自己添加的任务(root crontab 中由专用注释标记的独立区块);crontab 中已有的一切(上面的列表)以只读列表“服务器的其他任务”显示,并附带“复制到编辑器”按钮——该按钮只把计划/命令搬到添加表单中,不改动原始行。要把现有任务“转为”面板管理——请把它复制到编辑器,保存,然后手动删除旧行(sudo crontab -e),否则它会被执行两次。
服务器上的一次性配置。 该页面需要一个特权包装脚本——不是裸的 sudo crontab(那会让任何获得面板会话访问权的人直接提权到 root),而是一个只有两条命令(list/set)的狭窄脚本,它只操作专用注释之间的自有区块。安装一次即可:
sudo install -m 0755 -o root -g root /dev/stdin /usr/local/sbin/arciveo-cron <<'ARCIVEO_CRON_EOF' #!/bin/bash set -euo pipefail BEGIN='# >>> ARCIVEO-CRON-MANAGED (edited from the panel; do not edit by hand) >>>' END='# <<< ARCIVEO-CRON-MANAGED <<<' cmd=${1:-} case "$cmd" in list) [ "$#" -eq 1 ] || { echo "usage: ${0##*/} list" >&2; exit 2; } crontab -l -u root 2>/dev/null || true ;; set) [ "$#" -eq 1 ] || { echo "usage: ${0##*/} set (body on stdin)" >&2; exit 2; } body=$(cat) if grep -qF "$BEGIN" <<<"$body" || grep -qF "$END" <<<"$body"; then echo "invalid body: markers not allowed" >&2; exit 2 fi current=$(crontab -l -u root 2>/dev/null || true) tmp=$(mktemp); trap 'rm -f "$tmp"' EXIT { if grep -qF "$BEGIN" <<<"$current"; then awk -v b="$BEGIN" '{print} $0==b{exit}' <<<"$current" else [ -n "$current" ] && printf '%s\n' "$current" echo "$BEGIN" fi printf '%s\n' "$body" echo "$END" if grep -qF "$END" <<<"$current"; then awk -v e="$END" 'f{print} $0==e{f=1}' <<<"$current" fi } > "$tmp" crontab -u root "$tmp" ;; *) echo "usage: ${0##*/} <list|set>" >&2; exit 2 ;; esac ARCIVEO_CRON_EOF echo "www-data ALL=(ALL) NOPASSWD: /usr/local/sbin/arciveo-cron" | sudo tee -a /etc/sudoers.d/monitor sudo visudo -c
web 用户可能不是 www-data——请检查网站的 PHP-FPM 池以哪个用户运行(ps -o user= -C php-fpm),并把它填入 sudoers 行。
新文件以错误的所有者上传——页面回应“Access denied.”。 如果 public/crontab_monitor.php 文件通过 FTP/SFTP 以与网站其他文件不同的系统用户(例如 root)上传,web 服务器将无法读取它。请与相邻文件核对所有者和权限并使其一致:
ls -la public/crontab_monitor.php public/ssl_monitor.php sudo chown OWNER:OWNER public/crontab_monitor.php sudo chmod 644 public/crontab_monitor.php

诊断

41. 工具已安装,却显示“未安装”

监控面板通过 dpkg-query(APT 软件包数据库)检测工具是否存在。如果工具不是通过 apt 安装(手动安装、来自 snap 或从源码编译),dpkg 就无法识别它。

# 通过 dpkg 检查: dpkg -l fail2ban | grep '^ii' dpkg -l auditd | grep '^ii' # 查找二进制文件路径: which ufw fail2ban-client auditctl # 以 www-data 身份测试 sudo: sudo -u www-data sudo fail2ban-client status sudo -u www-data sudo ufw status verbose

42. 故障排查(500、无数据)

500 错误——请检查 PHP、nginx 及监控端自身的日志:

tail -50 /var/log/nginx/error.log tail -50 /var/log/php*-fpm.log # 监控端日志: tail -50 logs/monitor_$(date +%Y-%m-%d).log # 目录权限: ls -la data/ tmp/ logs/
面板运行在主机控制面板上(HestiaCP、ISPmanager、cPanel)? 那里 PHP 不是以 www-data 运行,而是以用户账户身份运行(例如 admin——站点目录的所有者)。所有 sudo 规则以及组成员身份(adm、systemd-journal)都必须配置到这个用户上,否则即使服务正在运行,模块也会显示“未激活 / 0”。查看 PHP 的真实用户:ps -o user= -C php-fpm | sort -u,或站点目录的所有者 stat -c '%U' /path/to/monitor。之后下面所有命令中都用它替换 www-data。自动安装会自行识别 Web 用户并将 sudoers 配置到该用户上。

数据不显示——几乎总是因为未配置 sudo 权限。请以 Web 用户身份检查具体命令(将 www-data 替换为您自己的)。-n 标志 = 不使用密码,与 PHP 一致——如果提示输入密码,就说明 sudoers 里没有该规则:

sudo -u www-data sudo -n fail2ban-client status sudo -u www-data sudo -n ufw status verbose sudo -u www-data sudo -n ipset list -t ipsum sudo -u www-data sudo -n /usr/sbin/ausearch -m USER_LOGIN -ts today sudo -u www-data sudo -n /usr/sbin/aa-status sudo -u www-data sudo -n /usr/sbin/psad --Status sudo -u www-data journalctl -u falco --no-pager -n 5
模块显示“未激活”/“0”,但工具其实在运行(例如终端里 sudo aa-status 能列出配置文件,而“AppArmor”页面却显示“未激活”)。原因:Web 用户没有该模块对应命令的 sudo 权限。请从上面列表中检查该命令:如果提示输入密码——就在 /etc/sudoers.d/monitor 中补上缺失的行(“配置 sudo”)。常见的“新增”命令:/usr/sbin/aa-status(MAC)、/usr/sbin/psad --Status(PSAD)。
如果某个具体页面(Falco、ModSecurity、Auditd、UFW 开放端口)为空——请对照 sudo 相关章节中的列表:很可能是未允许 apache2ctl、ausearch、aa-status 或 ss,或者 Web 用户不在 adm/systemd-journal 组中(fail2ban/auth/modsec 日志以及 journalctl——Falco 和内核事件——都从这里读取)。

43. 服务器上有数据,但页面显示为空

现象:服务器上确实有数据(通过 shell 可以看到),但页面却显示"无数据"或状态不正确——例如 AIDE 显示"未初始化",但数据库其实已创建。

原因在于 open_basedir:许多面板和主机商会将 PHP-FPM 进程池限制在域名目录内,因此 PHP 函数 file_exists()、file_get_contents()、filemtime() 访问系统路径(/var/lib/aide、/var/log、/proc……)时会被阻止。监控通过标准系统命令(cat、test、stat)读取这些路径,从而绕过该限制。

# 通过 shell 能否看到文件(监控就是这样读取的): sudo -u www-data bash -lc 'test -e /var/lib/aide/aide.db && echo VISIBLE || echo NO' # 域名进程池当前的 open_basedir 值: grep -ri open_basedir /etc/php/*/fpm/pool.d/ 2>/dev/null
如果 shell "能看到"文件(VISIBLE),而页面看不到——那就是 open_basedir 的问题。正确的解决办法是使用系统命令读取(AIDE 和网络监控已经这样做了)。无需将 open_basedir 扩展到 /var、/proc,那样做也不够安全。

44. SSL 页面无法使用

监控面板通过 443 端口直接连接域名来检查证书。如果服务器自身无法访问该域名,或端口被防火墙拦截,检查将无法通过。

# 手动检查证书: echo | openssl s_client -connect monitor.example.com:443 2>/dev/null \ | openssl x509 -noout -dates # 检查可访问性: curl -I https://monitor.example.com
监控面板会自动从 Nginx 配置(/etc/nginx/sites-enabled/、/etc/nginx/conf.d/)和 Apache 配置(/etc/apache2/sites-enabled/)中获取域名,并加上 HTTP_HOST 中的当前主机。
子域名自动识别。 系统会自动从公开的 Certificate Transparency 日志中识别子域名,并通过网络进行检查——即使这些子域名托管在其他服务器上也可以。无需手动添加任何内容。

45. 多个数据库中只显示一个

监控面板以 config.php 中的用户连接 MySQL,该用户只能访问自己的数据库。MySQL 在 information_schema 中只显示有权限的数据库,因此其余的看不到。

要让监控面板看到所有数据库,请给该用户仅授予只读权限(用 root 执行一次;替换为 config.php 中的用户名):

sudo mysql -u root GRANT SELECT, PROCESS, SHOW DATABASES ON *.* TO 'DB_USER'@'localhost'; FLUSH PRIVILEGES; EXIT;
SELECT ON *.* 仅授予读取权限——无法修改、删除或创建任何内容,对监控而言是安全的。
没有这条 GRANT,面板只能看到自己的数据库——这不是错误,而是权限限制。面板不会使用任何 sudo mysql:数据库列表通过它自己的 PDO 连接获取。

46. PostgreSQL 未显示在“数据库”页面

PostgreSQL 需要 postgres 用户级别的访问权限,而面板的 Web 用户并不具备该权限。从 PHP 中开放宽泛的 sudo psql 并不安全——面板改为调用一个不带参数的受限包装脚本,它只输出版本、连接数以及各数据库及其大小的列表。请创建它:

sudo tee /usr/local/bin/monitor-pgstat >/dev/null <<'EOF' #!/bin/sh # Arciveo Security Monitor - read-only PostgreSQL version, connections and per-database size sudo -u postgres psql -tAc "SELECT version();" | grep -oE 'PostgreSQL [0-9.]+' echo "---" sudo -u postgres psql -tAc "SELECT count(*) FROM pg_stat_activity;" echo "---" sudo -u postgres psql -tAc "SELECT datname || '|' || pg_size_pretty(pg_database_size(datname)) FROM pg_database WHERE datistemplate = false;" EOF sudo chown root:root /usr/local/bin/monitor-pgstat sudo chmod 755 /usr/local/bin/monitor-pgstat # 在 /etc/sudoers.d/monitor 中(用户 = 运行 PHP-FPM 所用的用户): # www-data ALL=(ALL) NOPASSWD: /usr/local/bin/monitor-pgstat
如果您不使用 PostgreSQL——请从 sudoers 中删除 monitor-pgstat 行(手动安装的第 13 步),并且不要创建该脚本:PostgreSQL 卡片将只是保持未激活状态。

47. 触发告警——该怎么办

面板显示发生了什么;下面说明在常见情形下该怎么办。总的原则:不要慌张,先和合法活动(您的操作、更新、备份)对照核实,再按严重程度处置。

  • 攻击地图 / 大量 fail2ban 封禁——对任何联网服务器来说这都很正常(机器人持续暴力破解 SSH/网站)。关键是封禁要确实生效。请确认 SSH 登录只允许密钥方式(已禁用密码),且您的 IP 已加入 ignoreip。
  • ModSecurity 拦截了请求——WAF 正在抵御针对网站的攻击,这就是它的职责。如果拦截的是您的合法流量(误报),请在详情中找到 rule id,并在 CRS 配置中添加例外。
  • AIDE:文件已变更——请将清单与您的操作对照(更新软件包、修改配置属正常现象)。若您并未动过的系统二进制文件发生变化,则需要警惕。合法变更后请更新 AIDE 数据库。
  • debsums:二进制文件/库被修改(/etc 之外、/usr/share 之外)——可能被替换。请核对软件包:debsums PACKAGE_NAME,如有疑问就重新安装它(apt install --reinstall)。
  • ClamAV / maldet:发现威胁——请查看隔离区中的文件,切勿打开它。如果是网站目录中的 webshell,请隔离服务器并排查入侵点(有漏洞的插件、凭据泄露)。
  • Falco:严重事件(在容器中启动 shell、访问敏感文件)——请分析该事件:是谁的进程、由什么触发。这往往是合法的管理员活动。
  • 外部暴露:数据库/缓存标红——请立即关闭:将服务绑定到 127.0.0.1,或在 UFW 中封闭端口。这是真实的漏洞。
  • SSL 即将过期 / 已过期——请续期证书(Let's Encrypt 会自动续期;若未续期,请检查 certbot renew 或面板中的设置)。
  • 有安全更新待安装——请安装:sudo apt update && sudo apt upgrade;内核更新后请重启服务器。
真实入侵的迹象(陌生进程/用户、被修改的二进制文件、外发垃圾邮件、陌生的 cron 任务):请切断服务器的外部访问,制作备份以供分析;若数据关键,请从可信备份重建一台干净的服务器——彻底清除 rootkit 十分困难。