事情是这样的,我想让我的网站的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