Debian 12 空 VPS 搭建 3x-ui-nginx-certbot-ufw-fail2ban

本文记录一套从空 Debian 12 VPS 开始的基础部署流程:

  • SSH 从 22 改为 54444
  • 使用公私钥登录,禁用密码登录
  • 开启 UFW 防火墙
  • 安装 Fail2ban 防 SSH/nginx 异常访问
  • 安装 3x-ui 面板
  • 使用 nginx + Certbot 管理 HTTPS 证书
  • 通过 HTTPS 域名路径反代访问 3x-ui 面板
  • 创建 VLESS REALITY 和 Hysteria2/HY2 入站

文中使用占位符:

<YOUR_DOMAIN>        你的面板域名
<YOUR_PUBLIC_KEY>    你的 SSH 公钥
<PANEL_PORT>         3x-ui 面板本地端口,例如 36666
<PANEL_PATH>         3x-ui 面板路径,例如 /randomPath/
<REALITY_PORT>       VLESS REALITY TCP 端口,例如 28445
<HY2_PORT>           Hysteria2 UDP 端口,例如 50733

1. 系统初始化

更新系统并安装基础工具:

apt update -y
apt upgrade -y
apt install -y curl wget net-tools vim ufw nginx certbot python3-certbot-nginx fail2ban
timedatectl set-timezone Asia/Shanghai

检查时间同步:

timedatectl status

Debian 12 通常已经使用 systemd 时间同步。如果仍想使用 ntpdate 定时同步:

apt install -y ntpdate
crontab -e

加入:

0 * * * * /usr/sbin/ntpdate pool.ntp.org >/dev/null 2>&1

2. 配置 SSH 公钥登录

先创建 root 的 SSH 目录:

mkdir -p /root/.ssh
chmod 700 /root/.ssh

写入你的 SSH 公钥:

cat >> /root/.ssh/authorized_keys <<'EOF'
<YOUR_PUBLIC_KEY>
EOF
 
chmod 600 /root/.ssh/authorized_keys

此时先不要断开当前 SSH 窗口,另开一个终端测试公钥能否登录:

ssh -p 22 root@<SERVER_IP>

确认公钥登录正常后,再修改 SSH 端口和禁用密码登录。

3. SSH 从 22 改为 54444

新建独立配置文件:

vim /etc/ssh/sshd_config.d/99-custom.conf

写入:

Port 54444
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin prohibit-password

检查 SSH 配置:

sshd -t

先放行新 SSH 端口:

ufw allow 54444/tcp

重载 SSH:

systemctl reload ssh

另开一个终端测试:

ssh -p 54444 root@<SERVER_IP>

确认 54444 可以登录后,再考虑关闭 22

4. 配置 UFW 防火墙

基础端口:

ufw allow 54444/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
ufw status

如果确认 SSH 54444 已经可用,可以删除旧的 22 放行规则:

ufw status numbered
ufw delete <RULE_NUMBER>

3x-ui 面板端口不建议公网开放。面板建议只通过 nginx HTTPS 反代访问。

5. 配置 Fail2ban

Fail2ban 和 UFW 的职责不同:

UFW: 控制哪些端口能访问
Fail2ban: 读取 SSH/nginx 日志,发现异常后动态封 IP

写入配置:

cat > /etc/fail2ban/jail.local <<'EOF'
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
backend = systemd
 
[sshd]
enabled = true
port = 54444
filter = sshd
logpath = %(sshd_log)s
maxretry = 5
 
[nginx-http-auth]
enabled = true
port = http,https
logpath = /var/log/nginx/error.log
maxretry = 5
 
[nginx-botsearch]
enabled = true
port = http,https
logpath = /var/log/nginx/access.log
maxretry = 10
EOF

让 Fail2ban 通过 UFW 执行封禁:

cat > /etc/fail2ban/jail.d/00-firewalld-ufw.conf <<'EOF'
[DEFAULT]
banaction = ufw
banaction_allports = ufw
EOF

启动并检查:

systemctl enable --now fail2ban
systemctl restart fail2ban
fail2ban-client status
fail2ban-client status sshd

ufw 是封禁动作,不是 jail 名称,所以 fail2ban-client status 里一般只会看到:

sshd
nginx-http-auth
nginx-botsearch

测试封禁是否能写入 UFW:

fail2ban-client set sshd banip 203.0.113.123
ufw status numbered
fail2ban-client set sshd unbanip 203.0.113.123

6. 网络参数优化

开启 BBR 和常用 TCP 参数:

cat >> /etc/sysctl.conf <<'EOF'
net.ipv4.tcp_syncookies=1
net.ipv4.tcp_congestion_control=bbr
net.core.default_qdisc=fq
net.core.rmem_max=26214400
net.core.wmem_max=26214400
net.ipv4.tcp_rmem=4096 87380 26214400
net.ipv4.tcp_wmem=4096 16384 26214400
net.ipv4.route.flush=1
EOF
 
sysctl -p
sysctl net.ipv4.tcp_congestion_control

如果输出包含 bbr,说明生效。

7. 安装 3x-ui

最新版安装:

bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)

固定版本安装示例:

VERSION=v2.4.7
bash <(curl -Ls "https://raw.githubusercontent.com/mhsanaei/3x-ui/${VERSION}/install.sh") "${VERSION}"

两种方式二选一即可。

安装过程中建议设置:

面板端口: <PANEL_PORT>
面板路径: <PANEL_PATH>
用户名: 自行设置
密码: 自行设置

安装后查看面板信息:

x-ui

记录:

Panel port
webBasePath
username
password

8. 域名解析与证书

先在 DNS 服务商中添加解析:

<YOUR_DOMAIN> -> 当前 VPS 公网地址

如果使用 Cloudflare,首次签发证书失败时,可以先把代理状态改为 DNS only。

确认 nginx 配置正常:

nginx -t
systemctl reload nginx

申请证书:

certbot --nginx -d <YOUR_DOMAIN>

测试自动续期:

certbot renew --dry-run
systemctl status certbot.timer --no-pager

如果 certbot.timer 正常启用,一般不需要额外写 cron。若系统没有 timer,可手动添加:

0 3 * * * certbot renew --quiet --post-hook "systemctl reload nginx"

9. nginx 反代 3x-ui 面板

编辑 nginx 站点配置:

vim /etc/nginx/sites-enabled/default

<YOUR_DOMAIN> 对应的 server { listen 443 ssl; ... } 块内添加 location。

如果 3x-ui 面板是 HTTP:

location ^~ <PANEL_PATH> {
    proxy_pass http://127.0.0.1:<PANEL_PORT><PANEL_PATH>;
    proxy_http_version 1.1;
 
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

如果 3x-ui 面板自身启用了 HTTPS:

location ^~ <PANEL_PATH> {
    proxy_pass https://127.0.0.1:<PANEL_PORT><PANEL_PATH>;
    proxy_ssl_verify off;
    proxy_ssl_server_name on;
    proxy_http_version 1.1;
 
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

注意:

  • <PANEL_PATH> 建议以 / 开头并以 / 结尾
  • nginx 中的路径必须和 3x-ui 输出的 webBasePath 完全一致
  • 注意数字 0 和字母 O 不要混淆

检查并重载:

nginx -t
systemctl reload nginx

访问:

https://<YOUR_DOMAIN><PANEL_PATH>

10. 创建 VLESS REALITY 入站

在 3x-ui 面板中新建入站:

Protocol: VLESS
Transport: TCP
Security: REALITY
Flow: xtls-rprx-vision
Port: <REALITY_PORT>

UFW 放行 TCP:

ufw allow <REALITY_PORT>/tcp
ufw reload

说明:

  • REALITY 不能通过 nginx location 普通反代
  • 如果 nginx 已占用 443,REALITY 建议使用独立 TCP 端口
  • 客户端配置里的端口必须和 <REALITY_PORT> 一致

11. 创建 Hysteria2/HY2 入站

在 3x-ui 面板中新建入站:

Protocol: Hysteria2
Port: <HY2_PORT>
UDP: enabled

UFW 放行 UDP:

ufw allow <HY2_PORT>/udp
ufw reload

HY2 主要走 UDP,不要只放行 TCP。

12. 检查端口和服务

查看 nginx:

systemctl status nginx --no-pager
nginx -t

查看 3x-ui:

systemctl status x-ui --no-pager
x-ui

查看端口监听:

ss -tulnp | grep -E '80|443|54444|<PANEL_PORT>|<REALITY_PORT>|<HY2_PORT>'

查看 UFW:

ufw status numbered

查看 Fail2ban:

fail2ban-client status
fail2ban-client status sshd

13. 常见问题

nginx reload 提示服务未运行

如果:

nginx.service is not active, cannot reload

先启动:

nginx -t
systemctl start nginx
systemctl status nginx --no-pager -l

nginx 端口被旧进程占用

如果看到:

bind() to 0.0.0.0:443 failed (98: Address already in use)

检查:

ss -tlnp | grep -E ':80|:443'

确认都是残留 nginx 进程时再处理:

pkill -9 nginx
rm -f /run/nginx.pid
systemctl start nginx

certbot 申请失败

常见原因:

域名没有解析到当前 VPS
Cloudflare 代理影响验证
80 端口没有开放
nginx server_name 不匹配

检查:

ufw status
nginx -t
curl -I http://<YOUR_DOMAIN>

反代面板 404

重点检查:

nginx location 路径是否和 3x-ui webBasePath 完全一致
location 是否写进了正确的 443 server 块
proxy_pass 是 http 还是 https

排查命令:

nginx -T | grep -n -A20 -B10 "<PANEL_PATH>"
curl -I http://127.0.0.1:<PANEL_PORT><PANEL_PATH>
curl -kI https://127.0.0.1:<PANEL_PORT><PANEL_PATH>

fail2ban 里没有 ufw jail

正常。ufw 是封禁动作,不是 jail 名称。查看 jail:

fail2ban-client status

通常会看到:

sshd
nginx-http-auth
nginx-botsearch

14. 最终安全建议

公网只建议开放:

80/tcp
443/tcp
54444/tcp
<REALITY_PORT>/tcp
<HY2_PORT>/udp

不建议公网开放:

22/tcp
3x-ui 面板端口
数据库端口
Docker 后端服务端口

部署完成后至少确认:

ssh -p 54444 root@<SERVER_IP>
ufw status numbered
fail2ban-client status
certbot renew --dry-run
nginx -t