Iptables

如何使用 IPtables 阻止(幾乎)所有非 VPN 流量到 Docker 服務?

  • September 21, 2017

我有一台伺服器,我希望只能通過 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 守護程序啟動和部署新容器時修改DOCKERDOCKER-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&gt;/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 數據包。

引用自:https://serverfault.com/questions/874859