недавно я начал переносить веб-сервер с несколькими приложениями на новый сервер и связывать каждое
приложение в докер-контейнере. Моя текущая настройка состоит из nginx для обратного прокси-сервера и серверов баз данных.
работающие на самом сервере, и все веб-приложения, работающие в своих собственных контейнерах докеров.
Сейчас я пытаюсь защитить веб-сервер с помощью iptables, как делал это много лет назад. я
необходимо удовлетворить следующие условия:
- обычный брандмауэр для служб, не связанных с докером, должен по-прежнему работать (политики по умолчанию отбрасываются как для ввода, так и для вывода, доступны только явно названные порты)
- контейнеры (за одним исключением - см. ниже) не должны быть доступны из мира
- контейнеры должны иметь доступ к хосту с помощью «docker.host.internal» для доступа к серверам баз данных.
- контейнеры должны иметь доступ к службам других контейнеров, используя их общедоступные (проксированные) доменные имена.
- один контейнер должен иметь 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
Большое спасибо заранее!