Набор правил Iptables, позволяющий контейнеру док-станции получать доступ к службе по IP-адресу хоста.

18

У меня проблемы с доступом к частному интерфейсу хоста (ip) из контейнера Docker. Я совершенно уверен, что это связано с моими правилами 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 art design превосходят мои 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
Dynom
источник
Можете ли вы опубликовать вывод iptables -t nat -nL? Делали ли вы какой-либо анализ пакетов, скажем, делаете ping из исходного контейнера и используете tcpdump для захвата пакетов на хосте.
Даниил Т.
Конечно, спасибо за помощь до сих пор: pastebin.com/TAaT73nk (не подходит для комментария ..) - edit -> Обновлена ​​ссылка на pastebin, срок действия которого не истек.
Dynom
Возможно, я не правильно понял вашу проблему, но я не вижу ни одного правила, разрешающего DNS-запросы на хосте. Также включен ли ip_forward?
Laurentiu Roescu
Привет @LaurentiuRoescu. $ cat /proc/sys/net/ipv4/ip_forward -> 1и -A INPUT -i eth1 -j ACCEPTпринимает все соединения на частном интерфейсе. Какие правила вам не хватает?
Dynom
2
Я думаю, что пакеты из контейнера приходят от интерфейса docker0, а не eth1. попробуйте-A INPUT -i docker0 -j ACCEPT
Laurentiu Roescu

Ответы:

14

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

-A INPUT -i docker0 -j ACCEPT
Laurentiu Roescu
источник
2
Dynom, урок, который вы можете извлечь из этого, состоит в том, что регистрация всех ваших отказов полезна, например, с помощью iptables -A INPUT -j LOG. Марка IN=docker0была бы очень полезна для выяснения того, какая настройка правила была необходима. Не отнимать у Лоренциу работу, которая была превосходной - +1 от меня!
MadHatter поддерживает Монику
5
Для людей, которые используют UFW, вот что я сделал, чтобы разрешить всю связь с контейнерами Docker для хостинга: ufw разрешил вход на docker0
Али Ок
0

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

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

L3w1s
источник
-i docker0следует убедиться, что это не повлияет на трафик, который не поступает через сеть docker0. Ваша грамматика неясна, хотя. Возможно, вы говорили, что исходящий доступ с узлов докера через eth0 был включен, что может быть правдой. Я согласен, что более целенаправленные правила возможны, открывая только столько, сколько вам нужно.
mc0e