У меня есть VPS с несколькими контейнерами докеров. У меня есть экземпляр nextcloud, который получает завершение SSL / TLS прокси-сервером nginx (сертификаты от Let'sEncrypt). И у меня есть контейнер openvpn. В этой сети докеров я также размещаю дополнительные службы (собственный DNS-сервер привязки и сервер git), к которым я могу подключиться через VPN.
Теперь я также хочу подключиться к своему экземпляру nextcloud через VPN. Первоначально я думал, что это не будет проблемой, поскольку к экземпляру nextcloud можно получить доступ через Интернет, а VPN также обеспечивает подключение к Интернету. Но, к сожалению, я не могу этого достичь. Если я скручиваю свой сервер (http или https) через VPN, я получаю «порт 80/443: нет маршрута к хосту». Без подключения VPN соединение работает правильно.
Если я использую traceroute, я вижу, что он правильно достигает общедоступного IP-адреса моего VPS. Итак, я прихожу к выводу, что это проблема с маршрутизацией. Трафик, нацеленный на порт 80/443 на общедоступном IP-адресе моего VPS, не перенаправляется / маршрутизируется в прокси-контейнер nginx (который предоставляет указанные порты).
Как я понял, докер использует firewalld / iptables для маршрутизации трафика между контейнерами и в них. Таким образом, к трафику VPN применяются другие правила, чем трафик, исходящий из Интернета. Что мне нужно настроить, чтобы трафик VPN (внутренний сервер) на мой общедоступный IP-адрес правильно перенаправлялся в соответствующий контейнер? Я хотел бы поддерживать постоянное / неизменное соединение между состояниями VPN и No-VPN, чтобы мое приложение Nextcloud не запуталось.
Что я пробовал: Я опробовал возможности обходного пути. Я мог бы добавить собственную DNS-запись для моего экземпляра nextcloud на моем DNS-сервере VPN, которая указывает на IP-адрес контейнера приложения nextcloud (где я потеряю завершение SSL / TLS) или прокси-сервера nginx. В последнем случае прокси-сервер nginx не перенаправляет трафик в контейнер nextcloud, так как он использует другое имя хоста. Я хочу по возможности оставить конфигурацию прокси без изменений, поскольку она автоматически заполняется при запуске контейнера / из сопутствующего контейнера letsencrypt. Кроме того, сертификаты не будут соответствовать используемому полному доменному имени. Если я попытаюсь добавить главную зону с моим настоящим / общедоступным DNS-именем (чтобы я мог использовать то же полное доменное имя, что и извне), все другие домены из этого TLD больше не будут перенаправляться (есть ли возможность настроить привязку для этого?).
TL; DR: Трафик из контейнера докеров на общедоступный IP-адрес VPS не перенаправляется в правильный контейнер докеров, в отличие от внешнего трафика.
Если вам также понадобится дополнительная информация об используемых контейнерах, я добавлю ссылки и свои файлы docker-compose.
РЕДАКТИРОВАТЬ:
[root@XXXXXXXX ~]# iptables -S FORWARD
-P FORWARD ACCEPT
-A FORWARD -j DOCKER-USER
-A FORWARD -j DOCKER-ISOLATION-STAGE-1
-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -o docker0 -j DOCKER
-A FORWARD -i docker0 ! -o docker0 -j ACCEPT
-A FORWARD -i docker0 -o docker0 -j ACCEPT
-A FORWARD -o br-7e5cecc96f4a -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -o br-7e5cecc96f4a -j DOCKER
-A FORWARD -i br-7e5cecc96f4a ! -o br-7e5cecc96f4a -j ACCEPT
-A FORWARD -i br-7e5cecc96f4a -o br-7e5cecc96f4a -j ACCEPT
-A FORWARD -o br-fd56ce52983e -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -o br-fd56ce52983e -j DOCKER
-A FORWARD -i br-fd56ce52983e ! -o br-fd56ce52983e -j ACCEPT
-A FORWARD -i br-fd56ce52983e -o br-fd56ce52983e -j ACCEPT
-A FORWARD -o br-f1ef60d84b48 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -o br-f1ef60d84b48 -j DOCKER
-A FORWARD -i br-f1ef60d84b48 ! -o br-f1ef60d84b48 -j ACCEPT
-A FORWARD -i br-f1ef60d84b48 -o br-f1ef60d84b48 -j ACCEPT
-A FORWARD -o br-b396aa5a2d35 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -o br-b396aa5a2d35 -j DOCKER
-A FORWARD -i br-b396aa5a2d35 ! -o br-b396aa5a2d35 -j ACCEPT
-A FORWARD -i br-b396aa5a2d35 -o br-b396aa5a2d35 -j ACCEPT
-A FORWARD -o br-83ac9a15401e -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -o br-83ac9a15401e -j DOCKER
-A FORWARD -i br-83ac9a15401e ! -o br-83ac9a15401e -j ACCEPT
-A FORWARD -i br-83ac9a15401e -o br-83ac9a15401e -j ACCEPT
-A FORWARD -d 192.168.122.0/24 -o virbr0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -s 192.168.122.0/24 -i virbr0 -j ACCEPT
-A FORWARD -i virbr0 -o virbr0 -j ACCEPT
-A FORWARD -o virbr0 -j REJECT --reject-with icmp-port-unreachable
-A FORWARD -i virbr0 -j REJECT --reject-with icmp-port-unreachable
-A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -i lo -j ACCEPT
-A FORWARD -j FORWARD_direct
-A FORWARD -j FORWARD_IN_ZONES_SOURCE
-A FORWARD -j FORWARD_IN_ZONES
-A FORWARD -j FORWARD_OUT_ZONES_SOURCE
-A FORWARD -j FORWARD_OUT_ZONES
-A FORWARD -m conntrack --ctstate INVALID -j DROP
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
Docker по умолчанию не разрешает трафик между любыми двумя своими контейнерами, подключенными к разным мостам. А также он не разрешает трафик из контейнера в порт, который был отображен снаружи самим докером. Все это реализовано с помощью iptables.
Во-первых, отображение порта вовне также происходит с iptables. Он использует DNAT
править в нац стол. Для этих правил Docker создает отдельный DOCKER
цепи, так что те же правила применяются от PREROUTING
или OUTPUT
в нац стол. В DNAT
правилам предшествуют RETURN
прыжки, которые отфильтровывают весь трафик, исходящий от моста Docker. Так что это первое препятствие.
Это выглядит примерно так:
-A DOCKER -i br-one -j RETURN
-A DOCKER -i br-two -j RETURN
-A DOCKER ! -i br-one -p tcp -m tcp --dport EXPOSEDPORT -j DNAT --to-destination 172.17.0.2:INTERNALPORT
В DNAT
правило также может иметь -d address
если вы открыли порт только для этого локального адреса. Трафик с любого моста Docker не может попасть в DNAT
правила из-за RETURN
правила до этого. И, кроме того, DNAT
правило не позволяет DNAT
обратно через тот же мост, откуда пришел трафик. В этом все равно не было бы необходимости, потому что с того же моста можно просто добраться до ВНУТРЕННИЙ ПОРТ уже.
Ограничение движения между контейнерами на разных мостах реализовано в фильтр таблица iptables. Две пользовательские цепочки находятся в начале FORWARD
цепочка, и политика по умолчанию для этой цепочки DROP
. Один предназначен для контейнеров с определяемыми пользователем мостами, другой - для контейнеров с мостами Docker: DOCKER-ISOLATION-STAGE-1
. Эта цепочка снова использует DOCKER-ISOLATION-STAGE-2
. Сочетание того и другого в основном говорит о том, что если трафик листья мост Docker, а затем входит еще один докерский мост, затем DROP
это (без сигнализации ICMP, поэтому соединение просто зависает .....)
Выглядит это так:
-A FORWARD -j DOCKER-ISOLATION-STAGE-1
-A DOCKER-ISOLATION-STAGE-1 -i br-one ! -o br-one -j DOCKER-ISOLATION-STAGE-2
-A DOCKER-ISOLATION-STAGE-1 -i br-two ! -o br-two -j DOCKER-ISOLATION-STAGE-2
-A DOCKER-ISOLATION-STAGE-2 -o br-one -j DROP
-A DOCKER-ISOLATION-STAGE-2 -o br-two -j DROP
Итак, если вам нужен трафик с моста один, чтобы ударить DNAT
для порта, открытого снаружи контейнером на мосту два и вы хотите, чтобы трафик вернулся для полноценного соединения, тогда вам нужно сделать пару вещей:
Отбросьте RETURN
правила, которые останавливают движение от DNAT
в DOCKER
цепь в нац стол. Вы ДОЛЖНЫ удалить RETURN
для исходного моста. Вы МОЖЕТЕ покинуть RETURN
для целевого моста, если вы не хотите разрешать контейнеру с этого моста доступ к DNAT
открытый порт.
iptables -t nat -D DOCKER -i br-one -j RETURN
iptables -t nat -D DOCKER -i br-two -j RETURN
# Необязательно, если br-one -> br-twoУдалить DROP
правила для обоих мостов из DOCKER-ISOLATION-STAGE-2
цепь в фильтр стол.
iptables -t filter -D DOCKER-ISOLATION-STAGE-2 -o br-one -j DROP
iptables -t filter -D DOCKER-ISOLATION-STAGE-2 -o br-two -j DROP
Сейчас линии открыты.
Docker не часто обновляет свои правила (по крайней мере, в версии 19.03, с которой я тестировал). Кажется, он восстанавливает наборы правил только при перезапуске демона докера, а не при остановке, запуске или создании контейнера. Вы можете попытаться внести любые изменения в перезапуск службы, чтобы они оставались постоянными.