Eu tenho um VPS executando vários contêineres docker. Eu tenho uma instância nextcloud, que obtém a terminação SSL/TLS por um proxy nginx (certificados de Let'sEncrypt). E eu tenho um contêiner openvpn. Em sua rede docker também hospedo outros serviços (próprio servidor dns de ligação e um servidor git), que posso acessar através da VPN.
Agora também quero acessar minha instância nextcloud por meio da VPN. Originalmente eu pensei que isso não seria um problema, já que a instância nextcloud pode ser acessada pela internet e a VPN também dá conexão à internet. Mas infelizmente não consigo alcançá-lo. Se eu enrolar meu servidor (http ou https) através da VPN, recebo "port 80/443: No route to host". Sem conectar a VPN a conexão funciona corretamente.
Se eu usar o traceroute, posso ver que ele chega corretamente ao IP público do meu VPS. Então eu concluo, que é um problema com o roteamento. O tráfego direcionado para a porta 80/443 no IP público do meu VPS não é encaminhado/roteado para o contêiner do proxy nginx (que expôs as portas mencionadas).
Pelo que entendi, o docker usa firewalld/iptables para rotear o tráfego entre e para contêineres. Assim, outras regras são aplicadas ao tráfego VPN, além do tráfego proveniente da internet. O que preciso configurar como, para que o tráfego VPN (servidor interno) para meu endereço IP público seja encaminhado corretamente para o container correspondente? Gostaria de manter a conectividade constante/inalterada entre os estados VPN e No-VPN, para que meu aplicativo Nextcloud não fique confuso.
O que eu tentei: Eu tentei as possibilidades de uma solução alternativa. Eu poderia adicionar uma própria entrada DNS para minha instância nextcloud no meu servidor DNS VPN, que aponta para o IP do contêiner do aplicativo nextcloud (onde eu perderia a terminação SSL/TLS) ou do proxy nginx. No último caso, o proxy nginx não encaminha o tráfego para o contêiner nextcloud, pois usa um nome de host diferente. Eu quero deixar a configuração do proxy inalterada, se possível, pois ela é preenchida automaticamente na inicialização do contêiner/do contêiner complementar letsencrypt. Além disso, os certificados não corresponderiam ao FQDN usado. Se eu tentar adicionar uma zona mestre com meu nome DNS real/público (para poder usar o mesmo FQDN de fora), todos os outros domínios desse TLD não serão mais encaminhados (Existe a possibilidade de configurar o bind por isso?).
TL;DR: o tráfego de um contêiner docker para o endereço IP do VPS público não é encaminhado para o contêiner docker correto, como o tráfego de fora.
Se você também precisar de mais informações sobre os contêineres usados, adicionarei links e meus arquivos docker-compose.
EDITAR:
[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
O Docker, por padrão, não permite tráfego entre dois de seus contêineres conectados a pontes diferentes. E também não permite o tráfego de um contêiner para um porto que foi mapeado para o exterior pelo próprio docker. Isso tudo é implementado com iptables .
Primeiramente, o mapeamento de uma porta para o exterior também acontece com o iptables . Ele usa uma
DNAT
regra na tabela nat . Para essas regras, o Docker cria umaDOCKER
cadeia separada, para que as mesmas regras sejam aplicadasPREROUTING
naOUTPUT
tabela nat . AsDNAT
regras são precedidas porRETURN
saltos que filtram todo o tráfego proveniente de uma ponte Docker. Então esse é o primeiro obstáculo.Parece um pouco com isso:
A
DNAT
regra também pode ter um-d address
se você expôs a porta apenas para esse endereço local. Nenhum tráfego de qualquer ponte do Docker pode atingir aDNAT
(s) regra(s) por causa dasRETURN
regras anteriores. E ainda por cima, aDNAT
regra não permiteDNAT
voltar pela mesma ponte de onde veio o tráfego. O que não seria necessário de qualquer maneira, porque da mesma ponte você já pode chegar ao PORTÃO INTERNO .A restrição de tráfego entre containers em diferentes bridges é implementada na tabela de filtros do iptables . Duas cadeias personalizadas estão no início da
FORWARD
cadeia e a política padrão dessa cadeia éDROP
. Um é para contêineres com pontes definidas pelo usuário, o outro para contêineres com pontes Docker:DOCKER-ISOLATION-STAGE-1
. Essa cadeia novamente usaDOCKER-ISOLATION-STAGE-2
. A combinação de ambos diz basicamente que, se o tráfego sai de uma ponte Docker e depois entra em outra ponte Docker, entãoDROP
(sem sinalização ICMP, então a conexão simplesmente trava .....)Se parece com isso:
Portanto, se você deseja que o tráfego da ponte um atinja uma
DNAT
porta exposta do lado de fora por um contêiner na ponte dois e deseja que o tráfego retorne para uma conexão completa, é necessário fazer algumas coisas:Elimine as
RETURN
regras que interrompem o tráfego daDNAT
cadeiaDOCKER
na tabela nat . Você TEM que remover oRETURN
para a ponte de origem. Você PODE deixar oRETURN
para a ponte de destino, se não quiser permitir que um contêiner dessa ponte acesse umaDNAT
porta exposta.iptables -t nat -D DOCKER -i br-one -j RETURN
iptables -t nat -D DOCKER -i br-two -j RETURN
#Opcional se br-um -> br-doisRemova as
DROP
regras para ambas as pontes daDOCKER-ISOLATION-STAGE-2
cadeia na tabela de filtros .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
Agora as linhas estão abertas.
O Docker não costuma atualizar suas regras (pelo menos não na versão 19.03 com a qual testei). Parece que só reconstrói os conjuntos de regras quando o daemon do docker é reiniciado, não quando você para, inicia ou cria um contêiner. Você pode tentar corrigir quaisquer alterações na reinicialização do serviço para mantê-las persistentes.