Iptables
如何使用 IPtables 阻止(幾乎)所有非 VPN 流量到 Docker 服務?
我有一台伺服器,我希望只能通過 SSH 在公共 Internet 上訪問它,但我希望它能夠連接到 VPN,並將服務綁定到它的 VPN 介面。
我使用 Linode 來託管 Ubuntu 16.04.2 LTS 虛擬機,ZeroTier 用於我的 VPN 層,Docker 以 Swarm 模式(只有一個節點)用於服務。
在 VM 上執行
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,但不是來自 VM 本身的本地主機。
我在這裡做錯了什麼?我如何獲得 iptables 規則來阻止來自公共 Internet 的流量,但只允許來自 ZeroTier VPN 的流量?
經過更多研究,我發現了我的問題。
它的要點是 Docker 使用自己的鏈來轉發數據包。它會在 Docker 守護程序啟動和部署新容器時修改
DOCKER
和DOCKER-INGRESS
鏈,但不會修改DOCKER-USER
用於管理員定義的我想要應用的類型的防火牆規則的鏈。有了這些知識,我能夠通過以下 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 數據包。