如何防止(几乎)所有使用IPtables的非VPN通信到Docker服务?

我有一台服务器,我希望只能通过SSH在公共Internet上访问,但是我希望它能够连接到VPN,并将服务绑定到其VPN接口。

我正在使用Linode为我的VPN层托pipeUbuntu 16.04.2 LTS VM,ZeroTier和Swarm模式的Docker(只有一个节点)。

在虚拟机上运行ifconfig -a返回这个(为了保密而编辑的公共IP):

 docker0 Link encap:Ethernet HWaddr 02:42:f7:e7:e6:1e inet addr:172.17.0.1 Bcast:0.0.0.0 Mask:255.255.0.0 UP BROADCAST MULTICAST MTU:1500 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) docker_gwbridge Link encap:Ethernet HWaddr 02:42:ad:82:56:0f inet addr:172.18.0.1 Bcast:0.0.0.0 Mask:255.255.0.0 inet6 addr: fe80::42:adff:fe82:560f/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:6083 errors:0 dropped:0 overruns:0 frame:0 TX packets:16927 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:6364682 (6.3 MB) TX bytes:49725061 (49.7 MB) eth0 Link encap:Ethernet HWaddr f2:3c:91:a7:c3:ac inet addr:<REDACTED> Bcast:<REDACTED> Mask:255.255.255.0 inet6 addr: <REDACTED>/64 Scope:Global inet6 addr: <REDACTED>/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:94675 errors:0 dropped:0 overruns:0 frame:0 TX packets:47778 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:59390529 (59.3 MB) TX bytes:14765979 (14.7 MB) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:54171 errors:0 dropped:0 overruns:0 frame:0 TX packets:54171 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1 RX bytes:6243573 (6.2 MB) TX bytes:6243573 (6.2 MB) veth8323309 Link encap:Ethernet HWaddr 82:b3:ec:d8:8c:9b inet6 addr: fe80::80b3:ecff:fed8:8c9b/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:15 errors:0 dropped:0 overruns:0 frame:0 TX packets:538 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:1248 (1.2 KB) TX bytes:22932 (22.9 KB) veth602daf7 Link encap:Ethernet HWaddr 82:7e:cf:b3:cb:a4 inet6 addr: fe80::807e:cfff:feb3:cba4/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:3065 errors:0 dropped:0 overruns:0 frame:0 TX packets:11154 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:6187902 (6.1 MB) TX bytes:794193 (794.1 KB) zt0 Link encap:Ethernet HWaddr 42:08:c2:e8:fc:93 inet addr:192.168.196.106 Bcast:192.168.196.255 Mask:255.255.255.0 inet6 addr: fe80::4008:c2ff:fee8:fc93/64 Scope:Link inet6 addr: fc7b:7a95:194:6f84:bf9a::1/40 Scope:Global UP BROADCAST RUNNING MULTICAST MTU:2800 Metric:1 RX packets:442 errors:0 dropped:0 overruns:0 frame:0 TX packets:279 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:52165 (52.1 KB) TX bytes:46603 (46.6 KB) 

我的Docker撰写文件如下所示:

 version: "3" services: jenkins: image: jenkins/jenkins:lts ports: - "80:8080" volumes: - jenkins_home:/var/jenkins_home deploy: replicas: 1 volumes: jenkins_home: 

但是,我似乎无法得到正确的iptables规则。 我本来希望能够做到这一点:

 # Allow established connections, including outbound connections this server initiated, to continue to receive replies iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT # Allow SSH on eth0 iptables -A INPUT -i eth0 -p tcp --dport 22 -j ACCEPT # Allow UDP 9993 on eth0 (required for ZeroTier) iptables -A INPUT -i eth0 -p udp --dport 9993 -j ACCEPT iptables -A OUTPUT -p udp --dport 9993 -j ACCEPT # Reject everything that hasn't already been accepted (rules are applied in order) iptables -A INPUT -i eth0 -j REJECT 

但是,当我部署和启动Docker Swarm服务时,我最终可以通过eth0上的地址,从不在VPN上的远程主机上,通过zt0上的VPN地址,从远程主机上访问该服务VPN,但不是来自虚拟机本身的本地主机。

我在这里做错了什么? 我怎样才能得到iptables的规则,将阻止来自公共互联网的stream量,但只允许从零级VPN?

经过更多的研究,我发现我的问题。

它的要点是,Docker使用自己的链路来转发数据包。 它将在Docker守护进程启动时以及部署新容器时修改DOCKERDOCKER-INGRESS链,但它不会修改一个名为DOCKER-USER的链,这个链旨在用于我想要应用的types的pipe理员定义的防火墙规则。

有了这些知识,我可以用下面的iptables规则得到我想要的东西:

 #!/bin/sh set -e set -u # REMINDER: DROP vs REJECT: # a DROP operation blackholes the packet, likely causing client to wait dozens of seconds for a timeout # a REJECT operation sends an ICMP response letting the client know the packet was rejected # REMINDER: IPv4 vs IPv6 # `iptables` only applies IPv4 rules; `ip6tables` only lapplies IPv6 rules. ## System firewall rules # Allow any established sessions from our container host - including replies to outbound queries - to receive traffic iptables --append INPUT -m conntrack --ctstate ESTABLISHED,RELATED --jump ACCEPT ip6tables --append INPUT -m conntrack --ctstate ESTABLISHED,RELATED --jump ACCEPT # Allow SSH on eth0 iptables --append INPUT --in-interface eth0 --protocol tcp --dport 22 --jump ACCEPT ip6tables --append INPUT --in-interface eth0 --protocol tcp --dport 22 --jump ACCEPT # Allow UDP 9993 on eth0 (required for ZeroTier) iptables --append INPUT --in-interface eth0 --protocol udp --dport 9993 --jump ACCEPT ip6tables --append INPUT --in-interface eth0 --protocol udp --dport 9993 --jump ACCEPT iptables --append OUTPUT --protocol udp --dport 9993 --jump ACCEPT ip6tables --append OUTPUT --protocol udp --dport 9993 --jump ACCEPT # Reject everything to eth0 that hasn't been accepted by a previous rule iptables --append INPUT --in-interface eth0 --jump REJECT ip6tables --append INPUT --in-interface eth0 --jump REJECT ## Docker firewall rules # NOTE: Docker does not enable IPv6 networking by default, so there is no need for `ip6tables` here # <https://docs.docker.com/engine/userguide/networking/default_network/ipv6/> # NOTE: Docker promises not to modify the DOCKER-USER chain so that we can do this type of filtering # Create the chain we need if Docker hasn't done so already iptables --new DOCKER-USER 2>/dev/null || true # Allow any established sessions from our containers - including replies to outbound queries - to receive traffic iptables --append DOCKER-USER --match conntrack --ctstate ESTABLISHED,RELATED --jump ACCEPT # Reject packets that would be forwarded to Docker when they come in over eth0 (the public interface) iptables --append DOCKER-USER --in-interface eth0 ---jump REJECT 

请注意,其中一些规则并不特定于Docker,但存在拒绝数据包给我的Docker服务器 – 我只想接受服务器本身的VPN和SSH数据包。