登录
  • 人们都希望被别人需要 却往往事与愿违
  • 在大欺骗的时代, 说出真相就是革命行为!@乔治. 奥威尔 (《1984》作者)

我的 ufw 怎么又不好用了?使用 docker 时如何拒绝非 cloudflare 访问

Linux Benny 小土豆 6260 次浏览 3881 字 5 个评论
文章目录 [显示]
这篇文章在 2021 年 04 月 18 日 20:27:41 更新了哦~

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

  1. docker run -d -p 80:80 nginx

那么你会惊喜的发现,这规则好像没用……80 端口还是能够从非 192.168.7.67 以外的 IP 访问。

那上 ufw!

  1. ufw allow ssh
  2. ufw allow proto tcp from 192.168.7.67 to any port 80,443
  3. ufw enable

嗯…… 还是没用,毕竟 ufw 其实也是在操作 iptables 的嘛。

嗯,因为 docker 的话,会新建一个 DOCKER 的链,如果容器使用-p绑定了端口,那么 doker 会自动去修改对应的 iptables 规则。

所以从现象上来看就是 docker 在你的防火墙上打了一个洞……


我的ufw怎么又不好用了?使用docker时如何拒绝非cloudflare访问

需求分析

其实我的需求很简单,非 cloudflare IP 无法访问 80/443。额外的,我还需要开放 UDP 的 80 和 443,wireguard 在用的。22 也是要的,也就顺便开了 UDP 22 备用吧。当然,以上需求包涵 IPv4 和 IPv6。

具体来说,我想到了这样几种有效的解决方案:

  1. 在厂商防火墙做配置
  2. nginx 配置 allow deny 规则
  3. 宿主机 nginx 做 proxy,将 docker 的端口绑定到 127.0.0.1
  4. 让 docker 臣服于 ufw……

厂商防火墙配置

这是最简单也的方案了,有些 VPS 厂商提供一个防火墙配置,有的也称为安全组,然后绑定到自己的机器就好了,就是 cloudflare 的 IP 有点多…… 改起来麻烦了点……

比如 vultr 的防火墙是这样的,自己改改就差不多了。

我的ufw怎么又不好用了?使用docker时如何拒绝非cloudflare访问

nginx 配置 allow deny 规则

以下均是配置容器中的 nginx

一般情况

这个其实很简单的啦,自己补全下 IP

  1. # https://www.cloudflare.com/ips
  2. # IPv4
  3. allow 103.21.244.0/22;
  4.  
  5. # IPv6
  6. allow 2400:cb00::/32;
  7. deny all; # deny all remaining ips

启用 set_real_ip_from

如果你启用了set_real_ip_from,那么如果像上面那样操作,那就全部都 403 啦!

正确的操作姿势是这样的……

  1. geo $realip_remote_addr $cloudflare_ip {
  2. default 0;
  3.  
  4. 103.21.244.0/22 1;
  5. 2a06:98c0::/29 1;
  6. }

然后在对应的 server 段中:

  1. if ($cloudflare_ip != 1) {
  2. return 403;
  3. }

嗯,这样就可以了!

宿主机 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.rules 最后加上如下规则

  1. # BEGIN UFW AND DOCKER
  2. # BEGIN UFW AND DOCKER
  3. *filter
  4. :ufw-user-forward - [0:0]
  5. :ufw-docker-logging-deny - [0:0]
  6. :DOCKER-USER - [0:0]
  7. -A DOCKER-USER -j ufw-user-forward
  8.  
  9. -A DOCKER-USER -j RETURN -s 10.0.0.0/8
  10. -A DOCKER-USER -j RETURN -s 172.16.0.0/12
  11. -A DOCKER-USER -j RETURN -s 192.168.0.0/16
  12.  
  13. -A DOCKER-USER -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN
  14.  
  15. -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
  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
  17. -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
  18. -A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 192.168.0.0/16
  19. -A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 10.0.0.0/8
  20. -A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 172.16.0.0/12
  21.  
  22. -A DOCKER-USER -j RETURN
  23.  
  24. -A ufw-docker-logging-deny -m limit --limit 3/min --limit-burst 10 -j LOG --log-prefix "[UFW DOCKER BLOCK] "
  25. -A ufw-docker-logging-deny -j DROP
  26.  
  27. COMMIT
  28. # END UFW AND DOCKER
  29.  

重启 ufw

  1. systemctl restart ufw

添加规则

开始愉快的添加规则吧

  1. ufw allow 22
  2. ufw allow proto udp to any port 80,443
  3.  
  4. for i in `curl https://www.cloudflare.com/ips-v4`; do ufw route allow proto tcp from $i to any port 80,443; done
  5. for i in `curl https://www.cloudflare.com/ips-v6`; do ufw route allow proto tcp from $i to any port 80,443; done
  6.  
  7. ufw enabler

如果你想额外的添加一些 IP 的话,那就依样画葫芦:

  1. 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 都不通的哦。

  1. vim /etc/defaut/ufw
  2. DEFAULT_FORWARD_POLICY="ACCEPT"
  3.  
  4. # 或者下面这个也可
  5. ufw default allow routed
  6. # 也要记得放行哦
  7. ufw allow from 192.168.6.0/24 to 192.168.6.1

如果你的容器需要访问宿主机,还要记得这样加一行

  1. ufw allow from 172.16.0.0/12 to 172.17.0.1

禁用 ufw

如果此时你是用 ufw disable 来禁用 ufw,会发现容器无法在外部访问。此时需要这么做:

  1. sudo iptables -I DOCKER-USER 1 -j RETURN

这样可以暂时 bypass 掉 ufw 规则了。

使用如下命令查看规则

  1. sudo iptables -n -L DOCKER-USER

使用这个命令删除规则,不过这个时候就记得要ufw enable

  1. 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

 


文章版权归原作者所有丨本站默认采用 CC-BY-NC-SA 4.0 协议进行授权 |
转载必须包含本声明,并以超链接形式注明原作者和本文原始地址:
https://dmesg.app/ufw-vs-docker.html
喜欢 (32)
分享:-)
关于作者:
If you have any further questions, feel free to contact me in English or Chinese.
发表我的评论
取消评论

                     

去你妹的实名制!

  • 昵称 (必填)
  • 邮箱 (必填,不要邮件提醒可以随便写)
  • 网址 (选填)
(5) 个小伙伴在吐槽
  1. 自己研究了很久,最后选择了宿主机 nginx 做 proxy,应该算是最简单的方案了
    Ling2024-04-18 05:27 回复
  2. 用上 Cloudflare Argo Tunnel 之后,我直接在厂商防火墙把整个 HTTP/HTTPS 入站规则禁了😂
    Heyz2021-04-18 14:54 回复
    • 嗯嗯 这样也可以哒
      -- 本评论由 Telegram Bot 回复~❤️
      Benny 小土豆 2021-04-18 14:55 回复
  3. 虽然看不懂,但是不妨碍我 respect!
    激励鸭 2021-04-11 18:32 回复
    • 点赞就对了 23333
      Benny 小土豆 2021-04-12 17:24 回复
您直接访问了本站! 莫非您记住了我的域名. 厉害~ 我倍感荣幸啊 嘿嘿