FAQ

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

从何开始

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

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

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

概览

02. 什么是 Arcivéo Monitor

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

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

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

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

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

如需监控多台服务器,请在每台服务器上单独安装监控,并使用各自独立的域名。

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
  • 有待安装的 security 更新 — −5

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

只有在相应工具已安装时,才会对 ClamAV、AIDE、CrowdSec 和 Suricata 进行扣分。Lynis 和 AIDE 在数据库未初始化时显示为“无数据”,不会扣分。今日攻击次数会显示在仪表盘上,但不影响安全评分。

设置与许可证

05. WebAuthn — 双因素认证

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

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

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

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

06. 通知:Telegram 与电子邮件

面板可将安全报告发送到 Telegram 和邮箱(可手动触发,也可按计划发送)。在“设置”中进行配置。

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_...)和已验证的发件域名。
“发送测试”按钮会立即检测通道。自动报告的计划任务通过 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)。必须与激活许可证所用的域名一致——否则密钥将被拒绝(见“许可证”一节);
  • TIMEZONEPHP 时区:仅影响面板显示日期和时间的方式。对 cron 任务的运行时间没有影响——那里使用的是系统时区(见“所有 cron 任务”)。

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

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

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

安全工具

09. 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 以此提示完全相同的规则已存在,因而不会重复添加。重新运行自动配置(该操作是幂等的)时出现此提示属正常情况,无需处理。

10. 安装 Fail2ban

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

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

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

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

12. 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——见上文)。自动安装时无需手动做任何操作。

13. 安装 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 后,才需要手动执行这些操作。

14. 安装 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

15. 安装 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

16. 安装 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 检查——请参见“页面为空,但服务器上有数据”一节。

17. 安装 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
自动安装会自行完成此操作——无需手动执行。

18. 安装 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 服务器读取)。在全新安装上无需手动配置这些。

19. 安装 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.confSecRuleEngine 就仍为 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 命令找出实际路径并将其填入列表。如果包装脚本较旧(没有此部分)—— 该栏目只会显示“不可用”警告,页面其余部分仍照常工作。

20. 安装 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。

21. 安装 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

22. 安装 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 日志记录,此页面将为空 — 在没有扫描发生前,这属于正常现象。

23. 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 模式下的配置文件则不同:规则早已写好,只是尚未生效而已。

24. 安装 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 等)——“二进制文件 / 库”卡片显示的正是这些。

25. 配置 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

26. 配置 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

面板模块

27. 网络监控(内置)

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

  • 接口和流量——来自 /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 防火墙”和“攻击地图”页面。模块为空并显示绿色对勾 = 一天内没有网络故障。

28. 磁盘与 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”,而温度、通电时长和重映射扇区等可能为空 —— 这属于正常现象。在物理服务器上则会显示全部属性。

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

本页展示服务器过去 24 小时的负载历史——Load Average、CPU 占用与 I/O 等待、RAM/Swap、网络流量(接收/发送)、磁盘 I/O(读取/写入)、磁盘与 inode 占用、打开的文件描述符和 MySQL 连接数,以及当前的 TCP 连接数和进程数。

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

# 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/RAM/磁盘/inode 超过阈值时,面板会向 Telegram/Email 发送通知(与每日报告使用相同的渠道,无需单独为告警启用),并在指标恢复正常后再发送一条通知。在超阈值持续期间不会重复刷屏:只有经历“恢复正常 → 再次超限”的循环后,才会发送下一条通知。

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

30. 攻击地图(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 瓦片)在浏览器中加载——打开面板的电脑需连接互联网。

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

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

维护

32. 备份

备份是最重要的保障:数据丢失比任何入侵都更可怕。需要两样东西——服务器/网站备份,以及单独的面板数据库备份(其中包含用户、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 传到云端)。并验证恢复确实可用。

33. 面板的更新与迁移

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

  • 覆盖(代码):public/includes/assets/cron/database/,以及根目录下的 .htaccess(前端控制器——路由不能沿用旧版本)、manifest.jsonsw.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/monitordrwxrwsr-x——字母 s 表示 setgid 已设置)。

迁移到另一台服务器:

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

34. 恢复访问权限(丢失安全密钥、密码、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 过滤。

35. 所有 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
各项详情见对应章节。备份任务(上一章节)也添加到这同一个 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 已包含在面板中,无需单独安装;
  • 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

诊断

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

监控面板通过 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

37. 故障排查(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 规则以及组成员身份(admsystemd-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 相关章节中的列表:很可能是未允许 apache2ctlausearchaa-statusss,或者 Web 用户不在 adm/systemd-journal 组中(fail2ban/auth/modsec 日志以及 journalctl——Falco 和内核事件——都从这里读取)。

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

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

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

# 通过 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,那样做也不够安全。

39. 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 日志中识别子域名,并通过网络进行检查——即使这些子域名托管在其他服务器上也可以。无需手动添加任何内容。

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

监控面板以 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 连接获取。

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

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

sudo tee /usr/local/bin/monitor-pgstat >/dev/null <<'EOF' #!/bin/sh # Arciveo 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 卡片将只是保持未激活状态。

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

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

  • 攻击地图 / 大量 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 十分困难。
Arcivéo - Security Monitor © 2026