使用 Cloudflare 橙云 与 Fail2Ban 加固服务器防护
背景
前阵子与同学拼了20x codex,通过一台公网服务器的cpa进行共享。今天早上发现token用量异常,一晚上用了50%的weekly quota,显然是受到了攻击。于是紧急使用Cloudflare进行防护。
本文记录一种常见的服务器访问控制方案:部分端口直接公网开放,部分端口必须通过 Cloudflare 橙云代理访问,其余端口全部默认拒绝。同时,使用 Fail2Ban 对 SSH 暴力破解进行动态封禁。
目标
假设域名为:
example.com服务器需要开放的端口分为三类。
第一类是直接公网开放的端口:
<SSH_PORT>/tcp SSH 登录端口
<PUBLIC_PORT_A>/tcp 公网业务端口 A
<PUBLIC_PORT_B>/tcp 公网业务端口 B第二类是只能通过 Cloudflare 访问的端口:
<CF_ONLY_PORT_A>/tcp
<CF_ONLY_PORT_B>/tcp第三类是所有其他端口:
默认拒绝入站访问由于Cloudflare默认只能代理常用端口,cpa使用的两个端口不在此列,所以要么使用Cloudflare Tunnel,要么通过Origin Rules进行回源。我选择了后者。
整体原理
整体链路如下:
用户浏览器
→ Cloudflare 橙云代理
→ Cloudflare Origin Rule 改写回源端口
→ 源站服务器指定端口源站服务器上的防火墙只允许 Cloudflare 的回源 IP 访问 <CF_ONLY_PORT_A> 和 <CF_ONLY_PORT_B>。这样即使攻击者知道服务器真实 IP,也无法直接访问这两个端口。
整体防护分层如下:
Cloudflare:
处理公网 Web 入口、代理转发、WAF、速率限制等边缘防护。
UFW:
处理服务器本机端口访问控制。
Fail2Ban:
分析 SSH 登录失败日志,并动态封禁暴力破解来源 IP。需要注意,Fail2Ban 不是防火墙的替代品。UFW 决定连接能否进入服务器,Fail2Ban 则基于服务日志进行动态封禁。已经被 UFW 默认拒绝的端口,不需要再额外用 Fail2Ban 防护。
Cloudflare DNS 配置
我自己的域名是腾讯云的,所以需要先将这个域名连接到 Cloudflare。
然后在 Cloudflare 中为域名 example.com 添加两条 DNS 记录:
Type: A
Name: service-a
Content: 服务器公网 IP
Proxy status: Proxied / 橙云
TTL: AutoType: A
Name: service-b
Content: 服务器公网 IP
Proxy status: Proxied / 橙云
TTL: Auto最终得到:
service-a.example.com
service-b.example.com这两条记录必须开启橙云代理。如果使用灰云,DNS 会直接解析到源站 IP,用户会绕过 Cloudflare 直接访问服务器。
Cloudflare Origin Rules 配置
进入 Cloudflare 控制台:
Websites
→ example.com
→ Rules
→ Origin Rules
→ Create rule创建第一条 Origin Rule。
规则名称:
service-a to origin port A匹配条件:
http.host eq "service-a.example.com"Origin 设置:
Destination port: Rewrite to <CF_ONLY_PORT_A>创建第二条 Origin Rule。
规则名称:
service-b to origin port B匹配条件:
http.host eq "service-b.example.com"Origin 设置:
Destination port: Rewrite to <CF_ONLY_PORT_B>配置完成后,访问链路变为:
用户访问 https://service-a.example.com
→ Cloudflare
→ 回源到 服务器公网IP:<CF_ONLY_PORT_A>以及:
用户访问 https://service-b.example.com
→ Cloudflare
→ 回源到 服务器公网IP:<CF_ONLY_PORT_B>UFW 防火墙配置
服务器侧使用 UFW 做端口访问控制。目标是:
允许 <SSH_PORT>/tcp from Anywhere
允许 <PUBLIC_PORT_A>/tcp from Anywhere
允许 <PUBLIC_PORT_B>/tcp from Anywhere
允许 <CF_ONLY_PORT_A>/tcp from Cloudflare IP ranges only
允许 <CF_ONLY_PORT_B>/tcp from Cloudflare IP ranges only
默认拒绝其他所有入站连接安装 UFW 和 curl:
sudo apt update
sudo apt install -y ufw curl设置默认策略:
sudo ufw default deny incoming
sudo ufw default allow outgoing先开放 SSH 端口,避免启用 UFW 后把自己锁在外面:
sudo ufw allow <SSH_PORT>/tcp comment 'SSH'开放两个公网业务端口:
sudo ufw allow <PUBLIC_PORT_A>/tcp comment 'Public service A'
sudo ufw allow <PUBLIC_PORT_B>/tcp comment 'Public service B'如果这两个业务端口需要 UDP,再额外添加:
sudo ufw allow <PUBLIC_PORT_A>/udp comment 'Public service A UDP'
sudo ufw allow <PUBLIC_PORT_B>/udp comment 'Public service B UDP'然后只允许 Cloudflare IP 段访问两个受保护端口:
for port in <CF_ONLY_PORT_A> <CF_ONLY_PORT_B>; do
curl -fsS https://www.cloudflare.com/ips-v4 | while read ip; do
sudo ufw allow from "$ip" to any port "$port" proto tcp comment "Cloudflare to $port"
done
curl -fsS https://www.cloudflare.com/ips-v6 | while read ip; do
sudo ufw allow from "$ip" to any port "$port" proto tcp comment "Cloudflare to $port"
done
done启用并查看 UFW 状态:
sudo ufw enable
sudo ufw reload
sudo ufw status verbose如果之前错误地将受保护端口开放给所有来源,需要删除对应规则:
sudo ufw status numbered如果看到类似:
<CF_ONLY_PORT_A>/tcp ALLOW Anywhere
<CF_ONLY_PORT_B>/tcp ALLOW Anywhere按编号删除:
sudo ufw delete <规则编号>Fail2Ban 配置
Fail2Ban 用于动态封禁异常来源 IP。本场景中,使用 Fail2Ban 保护 SSH 端口。
安装 Fail2Ban:
sudo apt update
sudo apt install -y fail2ban
sudo systemctl enable --now fail2ban创建 SSH jail 配置:
sudo nano /etc/fail2ban/jail.d/sshd.local写入:
[sshd]
enabled = true
port = <SSH_PORT>
filter = sshd
backend = systemd
maxretry = 5
findtime = 10m
bantime = 1h
banaction = ufw重启 Fail2Ban:
sudo systemctl restart fail2ban查看状态:
sudo fail2ban-client status
sudo fail2ban-client status sshd配置含义如下:
maxretry = 5 允许失败 5 次
findtime = 10m 统计窗口为 10 分钟
bantime = 1h 封禁 1 小时
banaction = ufw 使用 UFW 执行封禁如果有固定公网 IP,可以加白自己的 IP,减少误封风险:
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 <YOUR_FIXED_IP>
bantime = 1h
findtime = 10m
maxretry = 5
[sshd]
enabled = true
port = <SSH_PORT>
filter = sshd
backend = systemd
banaction = ufw保存后重启:
sudo systemctl restart fail2ban验证方法
测试公网开放端口:
nc -vz 服务器公网IP <SSH_PORT>
nc -vz 服务器公网IP <PUBLIC_PORT_A>
nc -vz 服务器公网IP <PUBLIC_PORT_B>这些端口应该可以连接。
测试受保护端口:
nc -vz 服务器公网IP <CF_ONLY_PORT_A>
nc -vz 服务器公网IP <CF_ONLY_PORT_B>从普通公网机器直接访问应失败。
测试 Cloudflare 域名访问:
curl -I https://service-a.example.com
curl -I https://service-b.example.com正常情况下,请求应经过 Cloudflare,并成功回源到对应端口。
查看 Fail2Ban 状态:
sudo fail2ban-client status sshd查看 UFW 状态:
sudo ufw status verbose