Назад | Перейти на главную страницу

Набор правил Iptables, чтобы контейнер докеров мог получить доступ к службе на IP-адресе хоста

У меня проблемы с доступом к частному интерфейсу хоста (ip) из контейнера докеров. Я почти уверен, что это связано с моими правилами Iptables (или, возможно, с маршрутизацией). Когда я добавляю --net=host флаг docker run, все работает как положено. Точно так же, когда я указываю, что политика INPUT следует либеральной -P INPUT ACCEPT, все работает так, как я ожидал. Однако это нежелательные и небезопасные варианты, которых я бы хотел избежать.

Поскольку это не относится к моим службам (DNS), я исключил это из проблемы, поскольку поиск этого в сочетании с докером приводит к другой (популярной) проблемной области, добавляя шум к результатам поиска.

Также связывание контейнеров Docker не является жизнеспособным вариантом, потому что некоторые контейнеры необходимо запускать с параметром --net = host, предотвращающим связывание, и я хочу создать согласованную ситуацию, где это возможно.

У меня следующие правила Iptables. Я полагаю, это комбинация CoreOS, Digital Ocean и Docker.

-P INPUT DROP
-P FORWARD ACCEPT
-P OUTPUT ACCEPT
-N DOCKER
-A INPUT -i lo -j ACCEPT
-A INPUT -i eth1 -j ACCEPT
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type 0 -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type 3 -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type 11 -j ACCEPT
-A FORWARD -o docker0 -j DOCKER
-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -i docker0 ! -o docker0 -j ACCEPT
-A FORWARD -i docker0 -o docker0 -j ACCEPT

Мои (соответствующие) хост-интерфейсы:

3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    inet 10.129.112.210/16 brd 10.129.255.255 scope global eth1
       valid_lft forever preferred_lft forever
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    inet 172.17.42.1/16 scope global docker0
       valid_lft forever preferred_lft forever

И я запускаю док-контейнер:

$ docker run --rm -it --dns=10.129.112.210 debian:jessie # Specifying the DNS is so that the public DNS servers aren't used.

На этом этапе я хочу иметь возможность использовать локальную службу, привязанную к 10.129.112.210:53. Так что следующий ответ должен дать:

$ ping google.com
^C
$ ping user.skydns.local
^C

Когда я запускаю ту же команду со своего хоста:

$ ping photo.skydns.localPING photo.skydns.local (10.129.112.206) 56(84) bytes of data.
64 bytes from 10.129.112.206: icmp_seq=1 ttl=64 time=0.790 ms
^C

Мой файл resolv.conf

$ cat /etc/resolv.conf
nameserver 10.129.112.210
nameserver 127.0.0.1
nameserver 8.8.8.8
nameserver 8.8.4.4

Дело здесь не в доступе к публичным хостам, а скорее к внутренним, используя локальную службу DNS, доступную на хосте (через другой экземпляр докера).

Чтобы проиллюстрировать это еще больше (мои навыки художественного дизайна ascii превосходят мои iptables fu, так что на данный момент этого должно быть достаточно):

 ______________________________________________
|  __________________________           Host   |
| |   Docker DNS container   |                 |
|  ``````````````````````|```                  |
|                        |                     |
|     ,----------,---( private n. interface )  |
|     |          |                             |
|     |          |   ( public  n. interface )---
|     |          |                             |
|     |          |   ( loopbck n. interface )  |
|     |          |                             |
|     |          |                             |
|     |        __|_______________________      |
|     |       | Docker service container |     |
|     |        ``````````````````````````      |
|     |                                        |
|     |                                        |
| [ Local host service using DNS. ]            |
|                                              |
|______________________________________________|

  private (host) network interface: eth1 (10.129.0.0/16)
  Docker network interface: docker0 (172.17.0.0/16)

Я искал, читал и применял различные примеры конфигураций Iptables, но я слишком мало знаю о более "продвинутых" правилах Iptables, чтобы понять, что происходит, и таким образом получить желаемый результат.

Выход iptables -t nat -nL:

Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
DOCKER     all  --  0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL

Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
DOCKER     all  --  0.0.0.0/0           !127.0.0.0/8          ADDRTYPE match dst-type LOCAL

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination
MASQUERADE  all  --  172.17.0.0/16        0.0.0.0/0

Chain DOCKER (2 references)
target     prot opt source               destination

Выход cat /proc/sys/net/ipv4/ip_forward:

1

Контейнер взаимодействует с хостом, используя docker0 интерфейс. Чтобы разрешить трафик из контейнера, добавьте:

-A INPUT -i docker0 -j ACCEPT

Я столкнулся с очень похожей ситуацией, но добавил -A INPUT -i docker0 -j ACCEPT откроет все доступы через мой интерфейс eth0 хоста docker к контейнерам, что совершенно не то, что я планировал.

И поскольку я заметил, что мой контейнер просто имеет ограниченный доступ (скажем, только порт 22) к интерфейсу хоста, а не полностью отключен из сети хоста, я просмотрел свои правила iptables и нашел правило в цепочке IN_public_allow, которое должно отвечать за это. Правило -A IN_public_allow -p tcp -m tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT. Поэтому я добавил аналогичные правила, чтобы позволить моему контейнеру получать доступ к другим желаемым портам хоста, что, как я думаю, могло бы быть немного более точным способом открытия сетевого доступа хоста к контейнерам.