事情是这样的,我想让我的网站的 80 和 443 端口只接受 cloudflare 的访问,直接访问会被拒绝。
嗨,这还不简单嘛!先给入网加规则,当然要记得先加 ssh 的规则,然后再给 policy 设置成 DROP,多大点事!
iptables -I INPUT -p tcp --dport 22 -j ACCEPT iptables -I INPUT -s 192.168.7.67 -p tcp --dport 80 -j ACCEPT iptables -P INPUT DROP
查看下 INPUT 的规则确定一下没问题
[~] iptables -L INPUT Chain INPUT (policy DROP) target prot opt source destination ACCEPT tcp -- 192.168.7.67 anywhere tcp dpt:http ACCEPT tcp -- anywhere anywhere tcp dpt:ssh
嗯,没问题!
大部分情况下,这是没问题的…… 但是如果你的应用是用 docker 起的,比如说
- docker run -d -p 80:80 nginx
那么你会惊喜的发现,这规则好像没用……80 端口还是能够从非 192.168.7.67 以外的 IP 访问。
那上 ufw!
- ufw allow ssh
- ufw allow proto tcp from 192.168.7.67 to any port 80,443
- ufw enable
嗯…… 还是没用,毕竟 ufw 其实也是在操作 iptables 的嘛。
嗯,因为 docker 的话,会新建一个 DOCKER 的链,如果容器使用-p
绑定了端口,那么 doker 会自动去修改对应的 iptables 规则。
所以从现象上来看就是 docker 在你的防火墙上打了一个洞……
需求分析
其实我的需求很简单,非 cloudflare IP 无法访问 80/443。额外的,我还需要开放 UDP 的 80 和 443,wireguard 在用的。22 也是要的,也就顺便开了 UDP 22 备用吧。当然,以上需求包涵 IPv4 和 IPv6。
具体来说,我想到了这样几种有效的解决方案:
- 在厂商防火墙做配置
- nginx 配置 allow deny 规则
- 宿主机 nginx 做 proxy,将 docker 的端口绑定到 127.0.0.1
- 让 docker 臣服于 ufw……
厂商防火墙配置
这是最简单也的方案了,有些 VPS 厂商提供一个防火墙配置,有的也称为安全组,然后绑定到自己的机器就好了,就是 cloudflare 的 IP 有点多…… 改起来麻烦了点……
比如 vultr 的防火墙是这样的,自己改改就差不多了。
nginx 配置 allow deny 规则
以下均是配置容器中的 nginx
一般情况
这个其实很简单的啦,自己补全下 IP
- # https://www.cloudflare.com/ips
- # IPv4
- allow 103.21.244.0/22;
- # IPv6
- allow 2400:cb00::/32;
- deny all; # deny all remaining ips
启用 set_real_ip_from
如果你启用了set_real_ip_from
,那么如果像上面那样操作,那就全部都 403 啦!
正确的操作姿势是这样的……
- geo $realip_remote_addr $cloudflare_ip {
- default 0;
- 103.21.244.0/22 1;
- 2a06:98c0::/29 1;
- }
然后在对应的 server 段中:
- if ($cloudflare_ip != 1) {
- return 403;
- }
嗯,这样就可以了!
宿主机 nginx 做 proxy
还有一种方式是,将 docker 的端口绑定到 127.0.0.1,然后宿主机安装 nginx 做 proxy_pass,之后配置宿主机的 nginx allow deny 或者 iptables。这种方式也可以的,比较简单,就不多说啦~🤣
让 docker 臣服于 ufw
要做到这一点,我们同时要保证:容器互通、并且通外网。当然,禁用 docker 的 iptables 功能也是可以的,就是比较麻烦,咱就想能不能让这二人和谐共处……
操作起来其实很简单。不过先要做一些准备工作,比如说恢复 docker 的 iptables 功能,清除 INPUT 中的可能冲突的规则…… 然后:
修改 ufw 配置
修改 /etc/ufw/after.rule
s 最后加上如下规则
- # BEGIN UFW AND DOCKER
- # BEGIN UFW AND DOCKER
- *filter
- :ufw-user-forward - [0:0]
- :ufw-docker-logging-deny - [0:0]
- :DOCKER-USER - [0:0]
- -A DOCKER-USER -j ufw-user-forward
- -A DOCKER-USER -j RETURN -s 10.0.0.0/8
- -A DOCKER-USER -j RETURN -s 172.16.0.0/12
- -A DOCKER-USER -j RETURN -s 192.168.0.0/16
- -A DOCKER-USER -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN
- -A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16
- -A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8
- -A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12
- -A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 192.168.0.0/16
- -A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 10.0.0.0/8
- -A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 172.16.0.0/12
- -A DOCKER-USER -j RETURN
- -A ufw-docker-logging-deny -m limit --limit 3/min --limit-burst 10 -j LOG --log-prefix "[UFW DOCKER BLOCK] "
- -A ufw-docker-logging-deny -j DROP
- COMMIT
- # END UFW AND DOCKER
重启 ufw
- systemctl restart ufw
添加规则
开始愉快的添加规则吧
- ufw allow 22
- ufw allow proto udp to any port 80,443
- for i in `curl https://www.cloudflare.com/ips-v4`; do ufw route allow proto tcp from $i to any port 80,443; done
- for i in `curl https://www.cloudflare.com/ips-v6`; do ufw route allow proto tcp from $i to any port 80,443; done
- ufw enabler
如果你想额外的添加一些 IP 的话,那就依样画葫芦:
- ufw route allow proto tcp from 1.1.1.1 to any port 80,443;
好的!设置完成!找台机器改 hosts 之后用 curl 试试吧!
我警告你们……
如果你的服务器也开启了 WireGuard,并且是使用了 WireGuard 的转发功能的,那么请记得开 ufw forward。举个例子:
我有 ABC 三台服务器,B 是 WireGuard“服务器”,A 和 C 都连接到了 B。如果 A 想要访问 C 上的 ssh,或者反过来,那么就要开 forward,否则会不通哦。
当然啦,前提是要先开net.ipv4.ip_forward=1
以及net.ipv6.conf.all.forwarding = 1
,否则有没有 ufw,AC 都不通的哦。
- vim /etc/defaut/ufw
- DEFAULT_FORWARD_POLICY="ACCEPT"
- # 或者下面这个也可
- ufw default allow routed
- # 也要记得放行哦
- ufw allow from 192.168.6.0/24 to 192.168.6.1
如果你的容器需要访问宿主机,还要记得这样加一行
- ufw allow from 172.16.0.0/12 to 172.17.0.1
禁用 ufw
如果此时你是用 ufw disable 来禁用 ufw,会发现容器无法在外部访问。此时需要这么做:
- sudo iptables -I DOCKER-USER 1 -j RETURN
这样可以暂时 bypass 掉 ufw 规则了。
使用如下命令查看规则
- sudo iptables -n -L DOCKER-USER
使用这个命令删除规则,不过这个时候就记得要ufw enable
哦
- sudo iptables -D DOCKER-USER 1
参考资料
ufw-docker: https://github.com/chaifeng/ufw-docker
让 docker 向 UFW 低头: https://keng42.com/blog/article/docker-ufw/
after.rules not reloaded unless reboot: https://github.com/chaifeng/ufw-docker/issues/40
-- 本评论由 Telegram Bot 回复~❤️