土豆不好吃

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

文章目录[显示]
这篇文章在 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起的,比如说

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。

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

  1. 在厂商防火墙做配置
  2. nginx配置allow deny规则
  3. 宿主机nginx做proxy,将docker 的端口绑定到127.0.0.1
  4. 让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.rules 最后加上如下规则

# 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

 


文章版权归原作者所有丨本站默认采用CC-BY-NC-SA 4.0协议进行授权|
转载必须包含本声明,并以超链接形式注明原作者和本文原始地址:
https://dmesg.app/ufw-vs-docker.html
退出移动版