Kroki ograniczania połączeń zewnętrznych do kontenera dokowanego za pomocą iptables?

20

Moim celem jest ograniczenie dostępu do kontenerów dokerów tylko do kilku publicznych adresów IP. Czy istnieje prosty, powtarzalny proces do osiągnięcia mojego celu? Zrozumienie tylko podstaw iptables podczas korzystania z domyślnych opcji Dockera jest dla mnie bardzo trudne.

Chciałbym uruchomić kontener, uczynić go widocznym dla publicznego Internetu, ale zezwalać tylko na połączenia z wybranych hostów. Spodziewałbym się ustawić domyślną zasadę WEJŚCIA REJECT, a następnie zezwolić tylko na połączenia z moich hostów. Ale reguły NAT i łańcuchy Dockera przeszkadzają, a moje reguły WEJŚCIE są ignorowane.

Czy ktoś może podać przykład realizacji mojego celu, biorąc pod uwagę następujące założenia?

  • Hostuj publiczny adres IP 80.80.80.80 na eth0
  • Hostuj prywatny adres IP 192.168.1.10 na eth1
  • docker run -d -p 3306:3306 mysql
  • Zablokuj wszystkie połączenia z hostem / kontenerem 3306 oprócz hostów 4.4.4.4 i 8.8.8.8

Z przyjemnością powiążę kontener tylko z lokalnym adresem IP, ale potrzebuję instrukcji, jak poprawnie skonfigurować reguły przekazywania iptables, które przetrwają proces dokowania i host uruchomi się ponownie.

Dzięki!

GGGforce
źródło

Odpowiedzi:

15

Podczas pracy z regułami zapory dokującej należy pamiętać o dwóch kwestiach:

  1. Aby uniknąć blokowania reguł przez okno dokowane, użyj DOCKER-USERłańcucha
  2. Docker wykonuje mapowanie portów w PREROUTINGłańcuchu nattabeli. Dzieje się przed filterzasadami, tak --desti --dportbędzie zobaczyć wewnętrzną IP i port pojemnika. Aby uzyskać dostęp do pierwotnego miejsca docelowego, możesz użyć -m conntrack --ctorigdstport.

Na przykład:

iptables -A DOCKER-USER -i eth0 -s 8.8.8.8 -p tcp -m conntrack --ctorigdstport 3306 --ctdir ORIGINAL -j ACCEPT
iptables -A DOCKER-USER -i eth0 -s 4.4.4.4 -p tcp -m conntrack --ctorigdstport 3306 --ctdir ORIGINAL -j ACCEPT
iptables -A DOCKER-USER -i eth0 -p tcp -m conntrack --ctorigdstport 3306 --ctdir ORIGINAL -j DROP

UWAGA: Bez --ctdir ORIGINALtego byłoby to również zgodne z pakietami odpowiedzi wracającymi w celu połączenia z kontenera do portu 3306 na innym serwerze, co prawie na pewno nie jest tym, czego chcesz! Nie potrzebujesz tego absolutnie, jeśli tak jak ja, twoją pierwszą zasadą jest -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT, ponieważ poradzi sobie ze wszystkimi pakietami odpowiedzi, ale i tak bezpieczniej byłoby z niej korzystać --ctdir ORIGINAL.

SystemParadox
źródło
Czy należy to zmienić, aby uwzględnić --ctdir ? Używam-m conntrack --ctstate NEW --ctorigdstport 3306 --ctdir ORIGINAL
lonix,
@Ionix, tak, powinno, chociaż dopiero co zrozumiałem, dlaczego mnie to myli. Dodałem trochę wyjaśnienia.
SystemParadox
1
Zwróć uwagę, że domyślna DOCKER-USERtabela zawiera wpis: -A DOCKER-USER -j RETURNktóry będzie działał przed powyższym, jeśli go użyjesz -A. Rozwiązaniem jest wstawienie reguł w nagłówku w odwrotnej kolejności za pomocą -I.
BMitch
@BMitch Lub jeszcze lepiej , dodaj wszystkie reguły w nowym FILTERSłańcuchu i -Iwstaw nowe reguły (jak powiedziałeś), aby przejść do tego: -I INPUT -j FILTERSi-I DOCKER-USER -i eth0 -j FILTERS
lonix,
@BMitch Jednak właśnie sprawdziłem serwer i nie ma tam reguły zwrotu, być może najnowsza wersja dokera już go nie wstawia? Warto jednak użyć -I, żeby być bezpiecznym.
lonix,
8

W Docker v.17.06 wprowadzono nowy łańcuch iptables o nazwie DOCKER-USER. Ten jest dla twoich niestandardowych reguł: https://docs.docker.com/network/iptables/

W przeciwieństwie do łańcucha DOCKER nie jest resetowany przy budowaniu / uruchamianiu kontenerów. Abyś mógł dodać te wiersze do skryptu config / skryptu iptables w celu obsługi serwera, nawet przed zainstalowaniem dokera i uruchomieniem kontenerów:

-N DOCKER
-N DOCKER-ISOLATION
-N DOCKER-USER
-A DOCKER-ISOLATION -j RETURN
-A DOCKER-USER -i eth0 -p tcp -m tcp --dport 3306 -j DROP
-A DOCKER-USER -j RETURN

Teraz port dla MySQL jest zablokowany przed dostępem zewnętrznym (eth0), nawet jeśli myśl, że doker otwiera port dla świata. (Zasady te zakładają, że twój interfejs zewnętrzny to eth0.)

W końcu będziesz musiał najpierw wyczyścić iptables, zrestartuj usługę dokera, jeśli zbyt wiele pomieszałeś, próbując zablokować port, tak jak ja.

ck1
źródło
Tęsknię za tym, dlaczego ta tabela DOCKER-USER jest inna niż jakakolwiek inna tabela dodana przez użytkownika. Nie ma w niej wcześniej zastosowanego filtra, więc nadal musisz sam określać nazwy interfejsów. Jeśli utworzysz „MY-CHAIN” i wstawisz go do łańcucha FORWARD, będzie on miał ten sam wynik, nie?
ColinM,
Tak, to robi różnicę, ponieważ Docker wstawia łańcuch DOCKER-USER do łańcucha FORWARD: -A FORWARD -j DOCKER-USER -A FORWARD -j DOCKER-ISOLATION dlatego niestandardowe instrukcje są wykonywane przed łańcuchem DOCKER.
ck1
Zauważ, że jeśli używasz --dportwewnątrz DOCKER-USER, musi to odpowiadać wewnętrznemu adresowi IP usługi kontenera, a nie odsłoniętemu portowi. Często się ze sobą zgadzają, ale nie zawsze, a to z łatwością może kolidować z innymi usługami, dlatego nadal twierdzę, że to rozwiązanie DOCKER-USER jest na wpół wypalone.
ColinM,
4

AKTUALIZACJA : Choć jest ważne w 2015 roku, to rozwiązanie nie jest już właściwym sposobem na zrobienie tego.

Odpowiedź wydaje się znajdować w dokumentacji Dockera na https://docs.docker.com/articles/networking/#the-world

Reguły przekazywania Dockera domyślnie zezwalają na wszystkie zewnętrzne źródła IP. Aby umożliwić dostęp tylko do określonego adresu IP lub sieci do kontenerów, wstaw zanegowaną regułę u góry łańcucha filtrów DOCKER. Na przykład, aby ograniczyć dostęp zewnętrzny, tak aby tylko źródłowy adres IP 8.8.8.8 mógł uzyskać dostęp do kontenerów, można dodać następującą regułę:iptables -I DOCKER -i ext_if ! -s 8.8.8.8 -j DROP

Skończyło się na tym, że:

iptables -I DOCKER -i eth0 -s 8.8.8.8 -p tcp --dport 3306 -j ACCEPT
iptables -I DOCKER -i eth0 -s 4.4.4.4 -p tcp --dport 3306 -j ACCEPT
iptables -I DOCKER 3 -i eth0 -p tcp --dport 3306 -j DROP

Nie dotknąłem opcji --iptableslub --icc.

GGGforce
źródło
1
Jeśli to zrobisz iptables -vnL DOCKER, wszystkie porty docelowe to porty w kontenerze. Jeśli dobrze to zrozumiem, oznacza to, że powyższe reguły wpłynęłyby tylko na port 3306w kontenerze - to znaczy, gdybyś był w -p 12345:3306swoim kontenerze, twoja reguła byłaby nadal wymagana do zablokowania dostępu (tj. --dport 12345Nie działałaby) , ponieważ reguły ACCEPT łańcucha DOCKER są po NAT.
sunside
Zgadza się, zasady muszą odnosić się do portów w kontenerach.
GGGforce
1
Hum, to trochę brzydkie, jeśli zdarza się, że uruchamiasz wiele kontenerów, które używają, powiedzmy, wewnętrznego NGINX do odwrotnego proxy (np. Zabbix, niestandardowy moduł równoważenia obciążenia itp.), Ponieważ wymagałoby to wcześniejszej znajomości adresu IP kontenera. Wciąż szukam rozwiązania tego problemu, który nie wymaga --iptables=false, ponieważ wydaje się, że jest to najgorszy z nich wszystkich.
sunside
Dziękuję Ci! Rozwiązałeś mój problem po wielu godzinach poszukiwań. Teraz jestem w stanie uwięzić MySQL tylko na mój domowy adres IP bez narażania miękkiego podbrzusza na cały świat.
Matt Cavanagh,
1
Łańcuch DOCKER nie powinien być bezpośrednio manipulowany przez użytkownika! W tym celu użyj łańcucha DOCKER-USER. Sprawdź zaakceptowaną odpowiedź.
Paul-Sebastian Manole
3

AKTUALIZACJA: Chociaż ta odpowiedź jest nadal aktualna, odpowiedź @SystemParadox DOCKER-USERw połączeniu z --ctorigdstportjest lepsza.

Oto rozwiązanie, które utrzymuje się między ponownymi uruchomieniami i pozwala wpływać na port odsłonięty, a nie port wewnętrzny .

iptables -t mangle -N DOCKER-mysql iptables -t mangle -A DOCKER-mysql -s 22.33.44.144/32 -j RETURN iptables -t mangle -A DOCKER-mysql -s 22.33.44.233/32 -j RETURN iptables -t mangle -A DOCKER-mysql -j DROP iptables -t mangle -A PREROUTING -i eth0 -p tcp -m tcp --dport 3306 -j DOCKER-mysql

Zbudowałem obraz Dockera, który używa tej metody do automatycznego zarządzania iptables dla ciebie, używając zmiennych środowiskowych lub dynamicznie z etcd (lub z obydwoma):

https://hub.docker.com/r/colinmollenhour/confd-firewall/

ColinM
źródło