Etapas para limitar conexões externas ao container docker com iptables?

20

Meu objetivo é limitar o acesso aos contêineres do docker a apenas alguns endereços IP públicos. Existe um processo simples e repetível para alcançar meu objetivo? Compreendendo apenas o básico do iptables ao usar as opções padrão do Docker, estou achando muito difícil.

Eu gostaria de executar um contêiner, torná-lo visível para a Internet pública, mas apenas permitir conexões de hosts selecionados. Eu esperaria definir uma política de entrada padrão de REJECT e permitir apenas conexões de meus hosts. Mas as regras e cadeias NAT do Docker atrapalham e minhas regras INPUT são ignoradas.

Alguém pode fornecer um exemplo de como realizar meu objetivo, considerando as seguintes premissas?

  • IP público do host 80.80.80.80 em eth0
  • Hospedar IP privado 192.168.1.10 no eth1
  • docker run -d -p 3306:3306 mysql
  • Bloquear toda a conexão com o host / contêiner 3306, exceto os hosts 4.4.4.4 e 8.8.8.8

Estou feliz em vincular o contêiner apenas ao endereço IP local, mas precisaria de instruções sobre como configurar as regras de encaminhamento do iptables corretamente, que sobrevivam ao processo do docker e reinicia o host.

Obrigado!

GGGforce
fonte

Respostas:

15

Duas coisas a ter em mente ao trabalhar com as regras de firewall do docker:

  1. Para evitar que suas regras sejam derrotadas pela janela de encaixe, use a DOCKER-USERcadeia
  2. O Docker faz o mapeamento de portas na PREROUTINGcadeia da nattabela. Isso acontece antes das filterregras, por isso --deste --dportvai ver o IP interno e porta do recipiente. Para acessar o destino original, você pode usar -m conntrack --ctorigdstport.

Por exemplo:

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

NOTA: Sem --ctdir ORIGINALisso, isso também corresponderia aos pacotes de resposta que retornam para uma conexão do contêiner à porta 3306 em algum outro servidor, o que quase certamente não é o que você deseja! Você não precisa disso estritamente se, como eu, sua primeira regra for -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT, pois ela lidará com todos os pacotes de resposta, mas seria mais seguro ainda usá-la de --ctdir ORIGINALqualquer maneira.

SystemParadox
fonte
Isso deve ser editado para incluir o --ctdir ? Eu uso-m conntrack --ctstate NEW --ctorigdstport 3306 --ctdir ORIGINAL
lonix 15/09
@ Ionix, sim, deveria, apesar de eu ter acabado de descobrir por que isso está me confundindo. Eu adicionei um pouco de explicação.
SystemParadox 16/09
1
Observe que a DOCKER-USERtabela padrão contém a entrada: -A DOCKER-USER -j RETURNque será executada antes do acima, se você usar -A. Uma solução é inserir regras na cabeça na ordem inversa com -I.
BMitch 16/09
@BMitch Ou melhor ainda , adicione todas as regras em uma nova FILTERScadeia e -Iinsira novas regras (como você disse), para pular para ela: -I INPUT -j FILTERSe-I DOCKER-USER -i eth0 -j FILTERS
lonix 17/09/09
@BMitch No entanto, acabei de verificar meu servidor e a regra de retorno não existe, talvez a versão mais recente do Docker não o insira mais? Boa idéia para usar -I, porém, apenas por segurança.
lonix 17/09
8

Com o Docker v.17.06, há uma nova cadeia de iptables chamada DOCKER-USER. Este é para suas regras personalizadas: https://docs.docker.com/network/iptables/

Ao contrário da corrente DOCKER, ela não é redefinida na construção / partida de contêineres. Portanto, você pode adicionar essas linhas ao seu iptables config / script para provisionar o servidor antes de instalar o docker e iniciar os contêineres:

-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

Agora, a porta do MySQL está bloqueada para acesso externo (eth0), mesmo que o docker abra a porta para o mundo. (Essas regras assumem que sua interface externa é eth0.)

Eventualmente, você terá que limpar o iptables e reiniciar o serviço docker primeiro, se você estragou demais tentando bloquear a porta como eu fiz.

ck1
fonte
Sinto falta do motivo pelo qual esta tabela DOCKER-USER é diferente de qualquer outra tabela adicionada pelo usuário. Ela não possui um filtro pré-aplicado, portanto você ainda precisa especificar os nomes da interface. Se você criar um "MY-CHAIN" e inseri-lo na cadeia FORWARD, terá o mesmo resultado, não?
ColinM 04/04
Sim, faz a diferença, porque o Docker insere a cadeia DOCKER-USER na cadeia FORWARD: -A FORWARD -j DOCKER-USER -A FORWARD -j DOCKER-ISOLATION É por isso que as instruções personalizadas são executadas antes da cadeia DOCKER.
Ck1
Observe que se você usar --dportdentro do DOCKER-USER, isso deve corresponder ao IP interno do serviço de contêiner, não à porta exposta. Eles costumam corresponder, mas nem sempre, e isso pode facilmente entrar em conflito com outros serviços, por isso ainda argumento que esta solução DOCKER-USER é incompleta.
ColinM
4

ATUALIZAÇÃO : Embora válida em 2015, esta solução não é mais a maneira certa de fazê-lo.

A resposta parece estar na documentação do Docker em https://docs.docker.com/articles/networking/#the-world

As regras de encaminhamento do Docker permitem todos os IPs de origem externa por padrão. Para permitir que apenas um IP ou rede específico acesse os contêineres, insira uma regra negada na parte superior da cadeia de filtros DOCKER. Por exemplo, para restringir o acesso externo, de modo que apenas o IP 8.8.8.8 de origem possa acessar os contêineres, a seguinte regra pode ser adicionada:iptables -I DOCKER -i ext_if ! -s 8.8.8.8 -j DROP

O que acabei fazendo foi:

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

Não toquei nas opções --iptablesou --icc.

GGGforce
fonte
1
Se o fizer iptables -vnL DOCKER, as portas de destino são todas as portas dentro do contêiner. Se eu entendi direito, isso significa que as regras acima afetariam apenas a porta 3306dentro do contêiner - ou seja, se você estivesse no -p 12345:3306seu contêiner, sua regra ainda seria a necessária para bloquear o acesso (ou seja --dport 12345, não funcionaria) , porque as regras ACCEPT da cadeia DOCKER são pós-NAT.
sunside
É isso mesmo, as regras precisam estar relacionadas às portas nos contêineres.
GGGforce #
1
Hum, isso é meio feio se você executar vários contêineres que usam, por exemplo, um NGINX interno para fazer proxy reverso (por exemplo, Zabbix, um balanceador de carga personalizado etc.) porque exigiria que você soubesse o IP do contêiner com antecedência. Ainda estou procurando uma solução para esse problema que não exige --iptables=false, porque essa parece ser a pior escolha de todas.
sunside
Obrigado! Você resolveu meu problema após muitas horas de pesquisa. Agora, finalmente, sou capaz de prender o MySQL apenas no meu endereço IP residencial, sem expor o ponto fraco ao mundo inteiro.
275166 MattelananJul
1
A cadeia DOCKER não deve ser manipulada diretamente pelo usuário! Use a cadeia DOCKER-USER para isso. Verifique a resposta aceita.
Paul-Sebastian Manole
3

ATUALIZAÇÃO: Embora essa resposta ainda seja válida, a resposta do @SystemParadox usando DOCKER-USERem combinação com --ctorigdstporté melhor.

Aqui está uma solução que persiste bem entre as reinicializações e permite afetar a porta exposta e não a porta interna .

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

Criei uma imagem do Docker que usa esse método para gerenciar automaticamente as tabelas de ip para você, usando variáveis ​​de ambiente ou dinamicamente com o etcd (ou ambos):

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

ColinM
fonte