Шаги для ограничения внешних подключений к Docker-контейнеру с помощью iptables?


20

Моя цель - ограничить доступ к док-контейнерам всего несколькими публичными IP-адресами. Есть ли простой, повторяемый процесс для достижения моей цели? Понимать только основы iptables при использовании параметров Docker по умолчанию я нахожу очень сложным.

Я хотел бы запустить контейнер, сделать его видимым для общедоступного Интернета, но разрешить соединения только с выбранных хостов. Я ожидал бы установить политику INPUT по умолчанию REJECT, а затем разрешить только подключения от моих хостов. Но мешают правила и цепочки докеров NAT, а мои правила INPUT игнорируются.

Может ли кто-нибудь привести пример того, как достичь моей цели, учитывая следующие предположения?

  • Хост публичный IP 80.80.80.80 на eth0
  • Хост частный IP 192.168.1.10 на eth1
  • docker run -d -p 3306:3306 mysql
  • Заблокируйте все соединения с хостом / контейнером 3306, кроме хостов 4.4.4.4 и 8.8.8.8

Я рад привязать контейнер только к локальному IP-адресу, но мне нужны инструкции о том, как правильно настроить правила пересылки iptables, которые выживают в процессе докера и перезапускают хост.

Благодарность!

Ответы:


15

При работе с правилами брандмауэра докера нужно учитывать две вещи:

  1. Чтобы избежать засорения ваших правил докером, используйте DOCKER-USERцепочку
  2. Docker выполняет отображение портов в PREROUTINGцепочке natтаблицы. Это происходит раньше filterправил, поэтому --destи --dportувидим внутренний IP и порт контейнера. Чтобы получить доступ к исходному месту назначения, вы можете использовать -m conntrack --ctorigdstport.

Например:

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

ПРИМЕЧАНИЕ: без --ctdir ORIGINALэтого это также будет соответствовать ответным пакетам, возвращающимся для соединения от контейнера к порту 3306 на каком-либо другом сервере, что почти наверняка не то, что вы хотите! Вам не нужно это строго, если, как и я, ваше первое правило таково -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT, поскольку оно будет работать со всеми ответными пакетами, но в --ctdir ORIGINALлюбом случае было бы безопаснее его использовать .


Должно ли это быть отредактировано, чтобы включить --ctdir ? Я использую-m conntrack --ctstate NEW --ctorigdstport 3306 --ctdir ORIGINAL
Lonix

@ Ионикс, да, так и должно быть, хотя я только понял, почему это меня смущает. Я добавил немного объяснений.
SystemParadox

1
Обратите внимание, что в DOCKER-USERтаблице по умолчанию есть запись: -A DOCKER-USER -j RETURNкоторая будет работать перед тем, как указано выше, если вы используете -A. Решение состоит в том, чтобы вставить правила в начале в обратном порядке с -I.
BMitch

@BMitch Или, еще лучше , добавьте все правила в новую FILTERSцепочку и -Iвставьте новые правила (как вы сказали), чтобы перейти к ней: -I INPUT -j FILTERSи-I DOCKER-USER -i eth0 -j FILTERS
lonix

@BMitch Однако, я только что проверил свой сервер, и правила возврата там нет, может быть, последняя версия докера больше не вставляет его? Хорошая идея, чтобы использовать -Iхотя, просто чтобы быть в безопасности.
Ионикс

8

В Docker v.17.06 появилась новая цепочка iptables под названием DOCKER-USER. Это для ваших пользовательских правил: https://docs.docker.com/network/iptables/

В отличие от цепочки DOCKER она не сбрасывается при сборке / запуске контейнеров. Таким образом, вы можете добавить эти строки в конфигурационный / сценарий iptables для подготовки сервера еще до установки docker и запуска контейнеров:

-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

Теперь порт для MySQL заблокирован от внешнего доступа (eth0), даже если докер открывает порт для всего мира. (Эти правила предполагают, что ваш внешний интерфейс - eth0.)

В конце концов, вам придется очистить iptables, сначала перезапустить службу докера, если вы слишком много испортили, пытаясь заблокировать порт, как я.


Я скучаю, почему эта таблица DOCKER-USER отличается от любой другой таблицы, добавленной пользователем. В ней нет предварительно примененного фильтра, поэтому вам все равно придется указывать имена интерфейсов самостоятельно. Если вы создадите «MY-CHAIN» и вставите его в цепочку FORWARD, результат будет тот же, нет?
ColinM

Да, это имеет значение, потому что Docker вставляет цепочку DOCKER-USER в цепочку FORWARD: -A FORWARD -j DOCKER-USER -A FORWARD -j DOCKER-ISOLATION поэтому пользовательские инструкции выполняются до цепочки DOCKER.
ck1

Обратите внимание, что если вы используете --dportвнутри DOCKER-USER, это должно совпадать с внутренним IP-адресом службы контейнера, а не с открытым портом. Они часто совпадают, но не всегда, и это может легко вступить в конфликт с другими сервисами, поэтому я до сих пор утверждаю, что это решение DOCKER-USER наполовину испечено.
ColinM

4

ОБНОВЛЕНИЕ : Действуя в 2015 году, это решение больше не является правильным способом сделать это.

Ответ, кажется, находится в документации Докера по адресу https://docs.docker.com/articles/networking/#the-world.

Правила пересылки Docker разрешают все внешние IP-адреса по умолчанию. Чтобы разрешить доступ к контейнерам только для определенного IP-адреса или сети, вставьте отрицательное правило вверху цепочки фильтров DOCKER. Например, чтобы ограничить внешний доступ, чтобы только контейнеры с IP-адресами 8.8.8.8 могли иметь доступ к контейнерам, можно добавить следующее правило:iptables -I DOCKER -i ext_if ! -s 8.8.8.8 -j DROP

То, что я закончил, было:

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

Я не трогал --iptablesили --iccварианты.


1
Если вы это сделаете iptables -vnL DOCKER, все порты назначения являются портами в контейнере. Если я правильно понял, это означает, что вышеприведенные правила будут влиять только на порт 3306внутри контейнера - то есть, если бы вы были в -p 12345:3306вашем контейнере, ваше правило все равно было бы тем, которое требуется для блокировки доступа (т.е. --dport 12345не будет работать) потому что правила ACCEPT цепочки DOCKER являются пост-NAT.
Солнце

Правильно, правила должны относиться к портам внутри контейнеров.
GGGforce

1
Хм, это довольно уродливо, если вы запускаете несколько контейнеров, которые используют, скажем, внутренний NGINX для обратного проксирования (например, Zabbix, пользовательский балансировщик нагрузки и т. Д.), Потому что для этого потребуется заранее знать IP-адрес контейнера. Я все еще ищу решение этой проблемы, которое не требует --iptables=false, потому что это, кажется, худший выбор из всех.
Солнце

Спасибо! Вы решили мою проблему после многих часов поиска. Теперь я наконец могу заключить MySQL в тюрьму только на свой домашний IP-адрес, не подвергая уязвимое место живота всему миру.
Мэтт Кавана,

1
Цепочка DOCKER не должна напрямую управляться пользователем! Для этого используйте цепочку DOCKER-USER. Проверьте принятый ответ.
Пол-Себастьян Маноле

3

ОБНОВЛЕНИЕ: Хотя этот ответ остается в силе, ответ @SystemParadox лучше использовать DOCKER-USERв сочетании с --ctorigdstport.

Вот решение, которое хорошо сохраняется между перезапусками и позволяет воздействовать на открытый порт, а не на внутренний порт.

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

Я создал образ Docker, который использует этот метод для автоматического управления iptables для вас, используя либо переменные окружения, либо динамически с etcd (или обоими):

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.