Zestaw reguł Iptables, dzięki czemu kontener dokujący może uzyskać dostęp do usługi na adresie IP hosta

18

Mam problemy z dostępem do prywatnego interfejsu hosta (ip) z kontenera dokowanego. Jestem całkiem pewien, że jest to związane z moimi regułami Iptables (a może routingiem). Po dodaniu --net=hostflagi docker runwszystko działa zgodnie z oczekiwaniami. Podobnie, gdy sprecyzuję, że polityka INPUT podąża za liberałem -P INPUT ACCEPT, rzeczy również działają tak, jakbym się spodziewał. Są to jednak niepożądane i niebezpieczne opcje, których chciałbym uniknąć.

Ponieważ nie jest to specyficzne dla moich usług (DNS), wykluczyłem to z problemu, ponieważ wyszukiwanie tego w połączeniu z dokerem daje w innym (popularnym) obszarze problemu, powodując hałas w wynikach wyszukiwania.

Również łączenie kontenerów Docker nie jest realną opcją, ponieważ niektóre kontenery muszą być uruchamiane z opcją --net = host, co zapobiega łączeniu, a ja chcę stworzyć spójną sytuację tam, gdzie to możliwe.

Mam następujące zasady Iptables. Zakładam, że połączenie CoreOS, Digital Ocean i 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

Moje (odpowiednie) interfejsy hosta:

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

I prowadzę kontener dokerów:

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

W tym momencie chcę móc korzystać z usługi lokalnej, związanej z 10.129.112.210:53. Tak więc poniższe powinny dać odpowiedź:

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

Po uruchomieniu tego samego polecenia z mojego hosta:

$ 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

Mój 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

Nie chodzi o to, aby uzyskać dostęp do hostów publicznych, ale wewnętrznych, za pomocą lokalnej usługi DNS dostępnej na hoście (za pośrednictwem innej instancji dokera).

Aby zilustrować to jeszcze bardziej (moje umiejętności projektowania ascii przewyższają moje iptables fu, więc w tym momencie powinno to powiedzieć dość):

 ______________________________________________
|  __________________________           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)

Przeszukałem, przeczytałem i zastosowałem różne przykładowe konfiguracje Iptables, ale zbyt mało znam bardziej „zaawansowanych” reguł Iptables, aby zrozumieć, co się dzieje, a tym samym uzyskać pożądany rezultat.

Wyjście 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

Wyjście cat /proc/sys/net/ipv4/ip_forward:

1
Dynom
źródło
Czy możesz opublikować wynik iptables -t nat -nL? Czy zrobiłeś jakąkolwiek analizę pakietu, powiedz, że wykonaj ping z kontenera źródłowego i użyj tcpdump, aby przechwycić pakiety na hoście.
Daniel t.
Z pewnością dziękuję za pomoc do tej pory: pastebin.com/TAaT73nk (Nie pasuje do komentarza ..) - edytuj -> Zaktualizowałem link do pastebin, który nie wygasa.
Dynom
Być może nie zrozumiałem poprawnie twojego problemu, ale nie widzę żadnej reguły zezwalającej na zapytania DNS na hoście. Czy funkcja ip_forward jest włączona?
Laurentiu Roescu
Cześć @LaurentiuRoescu. $ cat /proc/sys/net/ipv4/ip_forward -> 1i -A INPUT -i eth1 -j ACCEPTakceptuje wszystkie połączenia w interfejsie prywatnym . Jakich zasad brakuje?
Dynom
2
Myślę, że pakiety z kontenera pochodzą z interfejsu docker0, a nie z eth1. spróbuj-A INPUT -i docker0 -j ACCEPT
Laurentiu Roescu,

Odpowiedzi:

14

Kontener komunikuje się z hostem za pomocą docker0interfejsu. Aby zezwolić na ruch z kontenera, dodaj:

-A INPUT -i docker0 -j ACCEPT
Laurentiu Roescu
źródło
2
Dynom, lekcją, którą możesz chcieć od tego wyciągnąć, jest to, że rejestrowanie wszystkich odmów jest przydatne, np iptables -A INPUT -j LOG. Znaczek IN=docker0byłby bardzo przydatny w ustaleniu, jakie ulepszenie reguły jest potrzebne. Nie odbierać pracy Laurentiu, która była doskonała - +1 ode mnie!
MadHatter obsługuje Monikę
5
Dla osób korzystających z UFW, oto co zrobiłem, aby umożliwić całą komunikację z kontenerów Docker do hostowania: ufw zezwól na docker0
Ali Ok
0

Zetknąłem się z bardzo podobną sytuacją, ale dodanie -A INPUT -i docker0 -j ACCEPTspowoduje otwarcie wszystkich dostępów przez mój interfejs eth0 hosta dokującego do kontenerów, co absolutnie nie jest moim zamierzeniem.

A ponieważ zauważyłem, że mój kontener ma po prostu ograniczony dostęp (powiedzmy tylko port 22) do interfejsu hosta zamiast całkowicie zamknąć sieć hosta, przejrzałem moje reguły iptables i znalazłem regułę w łańcuchu IN_public_allow, która powinna być za to odpowiedzialna. Zasada jest taka -A IN_public_allow -p tcp -m tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT. Dodałem więc podobne reguły, aby mój kontener mógł uzyskać dostęp do innych pożądanych portów hosta, co moim zdaniem może być nieco bardziej precyzyjnym sposobem otwarcia dostępu do kontenerów przez sieć hosta.

L3w1s
źródło
-i docker0powinien upewnić się, że nie wpłynie to na ruch, który nie dociera przez sieć docker0. Twoja gramatyka jest jednak niejasna. Być może mówiłeś, że dostęp wychodzący z hostów dokerów przez eth0 jest włączony, co może być prawdą. Zgadzam się, że bardziej ukierunkowane zasady można otwierać tylko tyle, ile potrzebujesz.
mc0e