Рейтинг:0

Iptables и docker — отключите удаленный доступ к контейнеру, сохранив связь между хостом и контейнерами через прокси.

флаг us

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

Сейчас я пытаюсь защитить веб-сервер с помощью iptables, как делал это много лет назад. я необходимо удовлетворить следующие условия:

  1. обычный брандмауэр для служб, не связанных с докером, должен по-прежнему работать (политики по умолчанию отбрасываются как для ввода, так и для вывода, доступны только явно названные порты)
  2. контейнеры (за одним исключением - см. ниже) не должны быть доступны из мира
  3. контейнеры должны иметь доступ к хосту с помощью «docker.host.internal» для доступа к серверам баз данных.
  4. контейнеры должны иметь доступ к службам других контейнеров, используя их общедоступные (проксированные) доменные имена.
  5. один контейнер должен иметь 22 порта, доступных непосредственно из мира (git)

Пока мне удалось удовлетворить только первые три требования. Моя простая настройка iptables, которую я перенял из предыдущая среда без докера выглядит примерно так:

# политики по умолчанию для удаления

-P ВХОД ПАДЕНИЕ
-P ПАДЕНИЕ ВПЕРЕД
-P ПАДЕНИЕ НА ВЫХОДЕ

# разрешить просмотр в Интернете, NTP, DNS
-A ВЫВОД -p tcp -m tcp --dport 80 -j ПРИНЯТЬ
-A ВЫВОД -p tcp -m tcp --dport 443 -j ПРИНЯТЬ
-A INPUT -p tcp -m tcp --sport 80 -m state --state СВЯЗАННО, УСТАНОВЛЕНО -j ПРИНЯТЬ
-A INPUT -p tcp -m tcp --sport 443 -m state --state СВЯЗАННО, УСТАНОВЛЕНО -j ПРИНЯТЬ
-A ВВОД -p udp -m udp --sport 53 -j ПРИНЯТЬ
-A ВВОД -p tcp -m tcp --sport 53 -j ПРИНЯТЬ
-A ВЫВОД -p udp -m udp --dport 53 -j ПРИНЯТЬ
-A ВЫВОД -p tcp -m tcp --dport 53 -j ПРИНЯТЬ
-A ВВОД -p udp -m udp --dport 123 -j ПРИНЯТЬ
-A ВЫВОД -p udp -m udp --sport 123 -j ПРИНЯТЬ

# разрешить сервисы - веб-сервер, SSH-сервер на порту 16 (знаю, что это нестандартно, но я к этому привык) и SSH на порту 22 для доступа к git
-A INPUT -p tcp -m tcp --dport 22 -j ПРИНЯТЬ
-A INPUT -p tcp -m tcp --dport 16 -j ПРИНЯТЬ
-A ВХОД -p tcp -m tcp --dport 80 -j ПРИНЯТЬ
-A INPUT -p tcp -m tcp --dport 443 -j ПРИНЯТЬ
-A ВЫВОД -p tcp -m tcp --sport 22 -m состояние --state СВЯЗАННО, УСТАНОВЛЕНО -j ПРИНЯТЬ
-A ВЫВОД -p tcp -m tcp --sport 16 -m состояние --state СВЯЗАННО, УСТАНОВЛЕНО -j ПРИНЯТЬ
-A ВЫВОД -p tcp -m tcp --sport 80 -m состояние --state СВЯЗАННО, УСТАНОВЛЕНО -j ПРИНЯТЬ
-A ВЫВОД -p tcp -m tcp --sport 443 -m состояние --state СВЯЗАННО, УСТАНОВЛЕНО -j ПРИНЯТЬ

# разрешить все локальные коммуникации
-A ВВОД -i lo -j ПРИНЯТЬ
-A ВЫВОД -o lo -j ПРИНЯТЬ

# разрешить пинг
-A ВПЕРЕД -p icmp -j ПРИНЯТЬ
-A ВВОД -p icmp -j ПРИНЯТЬ
-A ВЫВОД -p icmp -j ПРИНЯТЬ

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

Теперь о том, что я плохо понимаю. Если я применю это к своей новой конфигурации докера, он прервет доступ к хосту с использованием псевдонима «docker.host.internal». Чтобы позволить это, Я использовал:

iptables -A ВВОД -i br-+ -j ПРИНЯТЬ
iptables -A ВЫВОД -o br-+ -j ПРИНЯТЬ

Я видел -я докер0 используется, но у меня это не сработало, потому что у каждого докер-контейнера есть свой бр-* интерфейс в моей настройке, который он использует для доступа к хосту. Это сработало, Все идет нормально.

Теперь я заметил, что все порты, опубликованные докером (я использую 80XX для веб-сервисов, а затем проксирую правильные домены на этот порт через прокси-сервер nginx), видны из мира. Я, конечно, не хочу этого, поэтому после некоторого поиска я добавил это:

iptables -I DOCKER-USER -i venet0 -j DROP

(венет0 имя моего сетевого интерфейса, а не eth0)

Это сработало хорошо, но нарушило условия 4 и 5 — контейнеры больше не могут получить доступ друг к другу по адресу своего общедоступного домена, и я не могу подключиться к серверу git через порт 22. (чего я и ожидал).

Говоря об общедоступном адресе, я имею в виду следующее:

У меня есть дрон-сервер CI, работающий в одном контейнере, выставляющий порт 8001 хозяину. У меня есть прокси-сервер nginx, передающий адрес https://ci.example.com в этот порт. То же самое относится и к моему контейнеру Gitea, открывающему порт 8002 и доступно на https://git.example.com.

Drone необходимо пройти аутентификацию через OAuth на сервере Gitea. Для этого он должен иметь доступ к нему через https://git.example.com:443 адрес, а не внутренний докер http://gitea_container_name:8002. У меня есть несколько других вариантов использования, где это должно быть возможно, но этот объясняет это лучше всего.

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

# попытался включить определенный порт для контейнера gitea, безрезультатно
iptables -I DOCKER-USER -i venet0 -p tcp --sport 8002 -j ПРИНЯТЬ

# попробовал это предложение, безрезультатно
iptables -I DOCKER-USER -i venet0 -p tcp -m conntrack --ctorigdstport 443 --ctdir ОРИГИНАЛ -j ПРИНЯТЬ

# никакого эффекта, но я подозревал, что это не сработает, так как я не думаю, что 443 входит в эту цепочку
iptables -I DOCKER-USER -i venet0 -p tcp --dport 443 -j ПРИНЯТЬ

# нет эффекта
iptables -A ВВОД -i docker0 -j ПРИНЯТЬ

Возможна ли такая установка? Может ли кто-нибудь, более разбирающийся в мире докеров и брандмауэров, указать мне правильное направление?

Моя установка:

  • Дебиан 11
  • iptables v1.8.7 (nf_tables)
  • Докер 20.10.5

Большое спасибо заранее!

Ответить или комментировать

Большинство людей не понимают, что склонность к познанию нового открывает путь к обучению и улучшает межличностные связи. В исследованиях Элисон, например, хотя люди могли точно вспомнить, сколько вопросов было задано в их разговорах, они не чувствовали интуитивно связи между вопросами и симпатиями. В четырех исследованиях, в которых участники сами участвовали в разговорах или читали стенограммы чужих разговоров, люди, как правило, не осознавали, что задаваемый вопрос повлияет — или повлиял — на уровень дружбы между собеседниками.