1Panel 装好后 ufw 仍是 inactive:一次服务器安全盲区复盘

目录

[1. 现场:面板是绿的,日志是脏的](#1. 现场:面板是绿的,日志是脏的)

[2. 起点比想象中更糟:SSH 的默认配置](#2. 起点比想象中更糟:SSH 的默认配置)

[3. 证据:215 条失败记录](#3. 证据:215 条失败记录)

[4. 第一个动作:把 Fail2ban 装上](#4. 第一个动作:把 Fail2ban 装上)

[5. 面板上的六个参数,藏着一个依赖](#5. 面板上的六个参数,藏着一个依赖)

[6. 真正的盲区:ufw 从来没启用过](#6. 真正的盲区:ufw 从来没启用过)

[7. 根因:安装脚本对"没开防火墙"只记一行日志](#7. 根因:安装脚本对"没开防火墙"只记一行日志)

[8. 第二个盲区:规则里躺着的 11 个端口](#8. 第二个盲区:规则里躺着的 11 个端口)

[9. 修复:先把门装上,再收窄](#9. 修复:先把门装上,再收窄)

[9.1. 顺序:先放行 22,再 enable](#9.1. 顺序:先放行 22,再 enable)

[9.2. 删掉多余的放行](#9.2. 删掉多余的放行)

[9.3. 让服务本身不对外监听](#9.3. 让服务本身不对外监听)

[10. 验证:三条必须拿到的证据](#10. 验证:三条必须拿到的证据)

[11. 检查清单](#11. 检查清单)

[12. 写在最后](#12. 写在最后)

[13. 参考资料](#13. 参考资料)


前言:

1Panel 装好后 Fail2ban 显示封了 2 个 IP,SSH 也关了密码认证,看起来安全。直到敲下 ufw status,输出 inactive。这篇复盘用真实截图和安装脚本源码,把这条盲区挖到底。

个人主页:艺杯羹

1. 现场:面板是绿的,日志是脏的

2026-11-14 上午,我在一台云服务器上装完 1Panel v1.10.34-lts。系统是 Ubuntu 22.04,内核 5.15.0-203-generic,4 核 / 3.82 GB 内存 / 38.58 GB 磁盘。

首页上"网站 0、数据库 0、计划任务 0"三个零,说明这台机器还是干净的初始状态。CPU、内存、磁盘三块仪表都在正常区间,运行时间 5 小时 49 分,没有任何红色提示。

这类界面最容易让人提前收工。面板能装、站点能开、容器能起,看起来该做的都做了。

但面板首页反映的只是应用层。真正的问题在系统层,而系统层不会主动弹窗。

2. 起点比想象中更糟:SSH 的默认配置

第一个该看的是 SSH 管理页。

四项内容:

  • 连接端口:22
  • 监听地址:0.0.0.0:22(IPv4) 和 :::22(IPv6)
  • root 用户:允许 SSH 登录
  • 密码认证:打开

翻译成一句人话:公网上任何一个 IP,都可以直接对着 root 账号猜密码,不限来源、不限次数。

22 端口是全网扫描器第一个会敲的门,root 是它们第一个会试的账号,密码认证打开则意味着"猜对了就能进"。这三件事凑在一起,等于把钥匙孔直接对着大街。

这里不是 1Panel 的错。它只是如实展示了 sshd 的状态,而 Ubuntu 的 sshd 默认就是允许 root、允许密码。

3. 证据:215 条失败记录

装完面板还不到六小时,登录日志已经很难看。

这里有个细节值得记下来:45.148.. 这个荷兰 IP,用同一个端口 55334,在 08:33:06、08:33:14、08:33:18 连试三次。间隔只有几秒,说明对面不是人在敲键盘,是脚本在跑循环。

再看总数:

215 条。 这台机器上线还不到一天,面板运行时间显示 5 小时 49 分。

从日志里抽几条,能看清攻击的形态:

登录 IP 归属地 端口 登录方式 用户 结果 时间
45.148.. 荷兰 55334 密码 root 失败 08:33:18
106.51.. 印度 32770 密码 root 失败 08:32:14
37.239.. 伊拉克 63021 空 root 失败 08:28:26
193.47.. 美国 30406 密码 root 失败 08:26:06
92.118.. 美国 37698 密码 root 失败 06:05:36
14.103.. 中国广东 54336 密码 root 失败 06:02:40
182.66.. 印度 46766 密码 invalid 失败 05:52:11
176.53.. 土耳其 16820 密码 invalid 失败 05:42:25

表中的 IP 后两段已脱敏,前缀保留,用来体现攻击来源的网段分布。

两个特征:

  1. 端口全在 30000 至 60000 之间随机跳,这是扫描器随手挑源端口留下的痕迹。
  2. 用户字段有 root,也有 invalid。invalid 的意思是连用户名都没猜对,直接被 sshd 判为不存在的用户。

第 2 点其实说明了一件事:对方连这台机器上有哪些账号都不知道,就已经在批量试了。它们不需要了解目标,只需要 22 端口开着。

4. 第一个动作:把 Fail2ban 装上

1Panel 工具箱里的 Fail2ban 当时是空的。

于是进终端装:

装的是 0.11.2-6,Ubuntu 22.04 源里的版本,依赖 python3-pyinotify 和 whois。装完 systemd 自动建了软链接,fail2ban.service 已经在 multi-user.target.wants 里。

立刻查 jail 状态:

text 复制代码
root@server:~# fail2ban-client status sshd
Status for the jail: sshd
|- Filter
|  |- Currently failed: 2
|  |- Total failed:     38
|  `- File list:        /var/log/auth.log
`- Actions
   |- Currently banned: 2
   |- Total banned:     2
   `- Banned IP list:   106.51.*.* 176.53.*.*

注意封禁列表里的两个 IP:106.51.*.* 在 08:32 刚试过 32770 端口,176.53.*.* 在 05:42 试过 16820 端口。前面日志里的攻击者,在这里变成了被封禁的对象,证据链正好接上。

看到 Currently banned: 2 那一下确实松了口气。晚些时候再查,Total banned 已经涨到 4,Banned IP list 换成了 45.148.10.* 段的另一台和 107.180.*.*------后者在 09:20 还在试密码,然后被收了进去。

5. 面板上的六个参数,藏着一个依赖

进 Fail2ban 的基础配置页。

六个值分别是:

  • 监听 SSH 端口:22
  • 最大重试次数:5
  • 禁用时间:600 秒
  • 发现周期:300 秒
  • 禁用方式:ufw
  • 日志路径:/var/log/auth.log

前五个都好理解:5 分钟内失败 5 次就封,封 10 分钟。10 分钟这个值太短,对方喝口水就能再来,于是改成 86400 秒。

改完这一项,看起来就齐了。但"禁用方式:ufw"这一行没动,当时也没多想------ufw 是 Ubuntu 的默认防火墙工具,填它看起来天经地义。

问题恰恰出在这一行。

6. 真正的盲区:ufw 从来没启用过

后来为了确认防火墙状态,敲了一条命令:

text 复制代码
=== UFW RULES PREP ===
Status: inactive

inactive。 这台机器上没有任何包过滤在运行。

而面板里"禁用方式"写的正是 ufw。

这两件事放在一起,性质就变了。Fail2ban 在"该封谁"这件事上是清醒的,在"怎么封"这件事上却指向了一个没启动的服务。ufw 没启用时,ufw insert ... deny 这类写入动作不会真正落到内核里。

排查到这里,要先证伪几个看起来更合理的猜测,否则方向会越跑越偏:

猜测 验证方式 实际结果
Fail2ban 服务没起来 systemctl status fail2ban 服务在跑,有常驻进程
jail 监听错了日志文件 status sshd 里的 File list /var/log/auth.log,Ubuntu 上的正确路径
封禁规则真的进了内核 iptables -S 查 INPUT 链 banned 计数不能代替这一步

关键在第三行:Currently banned 是 Fail2ban 自己账本上的数字。它只说明"Fail2ban 认为这两个 IP 该封",不说明"内核真的把它们挡在门外"。

这两套东西是分开的:计数器涨了,动作却可能在一直失败,而且默认不会弹窗告诉任何人。一个只读面板的人,看到的就是一片绿色。

7. 根因:安装脚本对"没开防火墙"只记一行日志

问题往上追,会追到 1Panel 的安装脚本。里面有个 Set_Firewall 函数,Debian/Ubuntu 分支走 ufw。把无关分支去掉,骨架是这样:

bash 复制代码
# 摘自 1Panel 官方安装脚本(第 183 至 211 行),只保留 ufw 分支
if which ufw >/dev/null 2>&1; then
    if [[ $(service ufw status >/dev/null 2>&1 && echo active || echo inactive) == 'active' ]]; then
        ufw allow "$PANEL_PORT"/tcp   # 情况 A:ufw 已启用,放行面板端口
        ufw reload
    elif [[ $(service ufw start >/dev/null 2>&1 && echo Success || echo Faild) == 'Success' ]]; then
        ufw allow "$PANEL_PORT"/tcp   # 情况 B:ufw 能启动,先启动再放行
        ufw reload
    else
        log "$TXT_FIREWALL_NOT_ACTIVE_IGNORE"   # 情况 C:起不来,只记一行日志
    fi
fi

最后那个 else 对应的日志文案是"防火墙未启用,忽略"。三个动作概括:跳过、不启用、不提醒。

这段代码的设计假设很清楚------装面板之前,防火墙已经配好了。没配好也不会中断安装,不会在面板首页挂一个待处理项,更不会阻止继续操作。整个安装过程风平浪静,结束时就是一个功能齐全的 1Panel。

这就是面板类工具的共同分界:它们管应用层,对系统层的安全状态只做默认假设,不做强制校验。

8. 第二个盲区:规则里躺着的 11 个端口

比"没有防火墙"更值得警惕的,是这台机器留下的另一处痕迹------它确实往 ufw 里写过规则:

text 复制代码
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw allow 3306/tcp
ufw allow 8080/tcp
ufw allow 8081/tcp
ufw allow 8085/tcp
ufw allow 8088/tcp
ufw allow 8089/tcp
ufw allow 8090/tcp
ufw allow 18900/tcp

11 个。按风险排一遍:

端口 常见用途 风险判断
22 SSH 必须开,但要配合密钥登录与 Fail2ban
80 / 443 Web 入口 必须开
3306 MySQL / MariaDB 高危,数据库不该直接面对公网
8080 至 8090 各类内部服务与调试端口 高危,典型的"上线后忘了收"
18900 内部服务或面板调试端口 中高危,没有对外的理由

这里藏着比"没开防火墙"更隐蔽的一层错觉:规则写进去了,防火墙却是 inactive。看规则以为关着,实际全开着。

规则列表本身会骗人。能相信的只有 ufw status 的第一行。

9. 修复:先把门装上,再收窄

9.1. 顺序:先放行 22,再 enable

顺序错了会把机器锁死。动防火墙之前,先确认 SSH 通道牢靠。

bash 复制代码
sudo ufw allow 22/tcp          # 必须第一步,否则 enable 时 SSH 会一起被挡在外面
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw --force enable

9.2. 删掉多余的放行

bash 复制代码
sudo ufw delete allow 3306/tcp
sudo ufw delete allow 8080/tcp
# 8081、8085、8088、8089、8090、18900 逐条删除
sudo ufw status numbered       # 删完逐条核对,确认只剩 22、80、443

9.3. 让服务本身不对外监听

防火墙是第二道锁,第一道锁是服务不要绑 0.0.0.0。

yaml 复制代码
# docker-compose.yml 片段:内部服务只绑回环地址
services:
  api:
    ports:
      - "127.0.0.1:8080:8080"   # 对外统一由 Nginx 反代进入
  db:
    ports:
      - "127.0.0.1:3306:3306"   # 数据库不需要映射到公网

端口不监听,扫描器就扫不到,连被探测的机会都没有。这比防火墙更彻底。

然后回到 SSH,把密码认证关掉,只留密钥。关掉之后仍然能进,登录日志里能看到密钥登录成功的记录:

爆破没有停。同一批 IP 还在试:

10. 验证:三条必须拿到的证据

改完不算完,要有证据。

bash 复制代码
# ① 防火墙真的活着
sudo ufw status verbose         # 必须是 Status: active

# ② 封禁规则真的进了内核
sudo iptables -S | grep -i f2b  # 应看到 f2b-sshd 链和对应 REJECT

# ③ 从外部验证端口关闭(不要在被测机器上执行)
nc -vz <公网IP> 3306            # 必须是 refused 或 timeout
验证项 合格标准 它证明了什么
ufw status Status: active 包过滤真的在运行
iptables -S 存在 f2b-sshd 链 封禁动作真的落到内核
外部 nc refused 或 timeout 外部真的进不来

第三条最容易被跳过,也最关键。前两条只能证明"规则配了",第三条才证明"外部进不来"。

11. 检查清单

  • ufw status 是 active
  • ufw status numbered 只剩 22、80、443
  • 3306、6379、27017 没有对外放行
  • 容器端口全部绑 127.0.0.1
  • sshd -T 显示 passwordauthentication no
  • permitrootlogin 评估是否改为 no
  • status sshd 的 File list 指向 auth.log
  • 禁用方式与实际启用的防火墙一致
  • 从外部 nc 验证过关键端口
  • 容器配置 restart: unless-stopped

12. 写在最后

这次排查真正改掉的不是某条命令,而是一个判断顺序:以前看到面板变绿就认为结束,现在会把"规则写了"和"规则生效了"当成两件事,分别验证。

1Panel 的安装脚本没有错,它做了合理假设;Fail2ban 也没有错,它确实在拦人。问题出在"装完就算完成"这个默认心理上------而安全恰好是最不能交给默认值的部分。

那 215 条失败记录还在日志里滚动。它们不是 Fail2ban 有用的证明,而是这台机器从上线第一天起,就一直有人在敲门的记录。

区别只在于,门到底装没装。

相关推荐
code2cat1 小时前
【随笔】MCP工具重试:任务已经创建,响应丢了怎么办
数据库·mcp
女神下凡2 小时前
芯参谋(107):杰理AC_中科蓝讯_开发指南
linux·服务器·网络·单片机·嵌入式硬件
落羽的落羽3 小时前
【C++】C++17的主要特性
linux·服务器·开发语言·c++·算法·机器学习
wuyk5553 小时前
Spider-第10讲-SQLite持久化与去重
数据库·爬虫·sqlite
QXWZ_IA3 小时前
具身智能在油气行业规模化应用面临哪些技术挑战?
人工智能·安全
乌暮4 小时前
Spring AOP 与 Spring 事务:用大白话彻底理解原理与实践
java·数据库·spring boot·后端·sql·学习·spring
小此方5 小时前
Re:Mysql数据库基础篇(十二):什么是事务、事务解决了什么问题、事务的四大属性、事务的操作方式
数据库·mysql
笑容掩饰我的失落5 小时前
区块链从小白到精通(十):合约安全与上线——重入攻击、Gas 优化与最佳实践
安全·区块链
flash俊杰6 小时前
v0.5-01 让桌面 AI 助手记得住:预算组装、递归摘要、长期记忆与评估基线
数据库·安全·架构