Oi, gente!
Iniciando 2019. Esse ano promete mudanças em todos os aspectos e espero que seja de grandes realizações para todos. Vamos lá!
Se você já trabalha com Docker, já deve ter passado por alguma situação onde as regras de iptables não são obedecidas (sim, isso acontece).
Se você ainda não mexeu com Docker, recomendo este artigo, que explica um pouco sobre o que é o Docker, como ele funciona, e te prepara, pois o que mostraremos no artigo de hoje será bastante útil pra vocês.
O problema
Por padrão, o Docker manipula regras no iptables, mas por que ele faz isso? Porque quando você cria um container, o Docker precisa criar algumas regras para encaminhamento de tráfego, isolamento, etc. É possível desabilitar esse comportamento. Porém, você precisará garantir isso manualmente.
Digamos que você tem seu iptables bonito. Nele, você libera apenas a porta 80 e 22, e bloqueia todo o resto. Para isso, é provável que você utilize as seguintes regras:
iptables -P INPUT DROP
iptables -A INPUT --dport 80 -j ACCEPT
iptables -A INPUT --dport 22 -j ACCEPT
Neste servidor você tem Docker rodando, e criou um container na porta 80, obviamente para responder as requisições que são feitas.
Ok, agora você teve a necessidade de criar outro container – desta vez, em outra porta – e neste container você terá o ambiente de homologação, como você já tem um container executando e utilizando a porta 80, você precisará criar na porta 8080.
Ok, sem problemas. Agora posso ir no meu iptables e liberar a porta 8080 apenas para o meu ip. Óbvio que funciona:
iptables -A INPUT -s 10.1.1.2 --dport 8080 -j ACCEPT
Não, não funciona. E a explicação para esse comportamento encontramos em um dos assuntos primordiais da existência de um Sysadmin Linux: o comportamento do iptables.
Para recapitularmos, o iptables é basicamente é um interpretador de regras, gerando ações baseadas nessas regras. Dentro do iptables temos três tabelas. São elas: Filter, Nat e Mangle. A tabela mais utiliza de todas é a Filter – é nessa tabela que criamos nossas regras para bloqueio de portas/ips. Dentro dela encontramos outras três chains:
- Input: consultado para pacotes que chegam na própria máquina
- Forward: consultado para pacotes que são redirecionados para outra interface de rede ou outra estação – utilizada em mascaramento
- Output: consultado para pacotes que saem da própria máquina
Agora advinhe em qual chain são criadas as regras do Docker? Sim, em uma chain de forward. Isso porque o pacote não é destinado ao host, e sim para a interface que o Docker cria. É importante saber disso, pois o fluxo dentro do iptables muda se o destino do pacote é local ou não.
Para ficar mais claro, dê uma olhada nessa imagem:
Isso quer dizer que aquele seu container que foi criado na porta 8080 ficará exposto, pois a regra de forward será executada antes daquela sua regra de input que bloqueia tudo. A chain utilizada, neste caso, chama-se Docker, e você pode visualizar as regras criadas utilizando a linha abaixo:
iptables -L -nv
- “Oh, e agora, quem poderá nos defender?”
A solução
Até pouco tempo atrás havia basicamente uma solução – nem um pouco “elegante” – de se resolver isso. Desabilitar no daemon do Docker para ele não manipular de forma automática essas regras. Com isso, você precisaria criar manualmente as regras.
Existiam outras formas? Sim. Algumas mais complexas, e outras mais baixo nível. Mas de qualquer forma, nada tão simples e muito menos fácil de se administrar.
Então, depois de vários pedidos e sugestões de solução no GitHub do Docker, isso foi repensado e resolvido de uma forma mais inteligente.
Foi adicionada uma nova Chain, chamada DOCKER-USER. Essa chain, apesar de ser forward também, precede as chains utilizadas pelo Docker na criação de containers. Dessa forma, você pode utiliza-la para adicionar as suas regras personalizadas e garantir o bloqueio ou acesso aos containers.
Te lembra daquelas regra que não funcionava antes? Então: neste novo cenário ficará dessa forma:
iptables -I DOCKER-USER -s 10.1.1.2 --dport 8080 -j ACCEPT
iptables -A DOCKER-USER --dport 8080 -j DROP
Bem mais simples do que manipular todas as regras do Docker manualmente, certo? Mas lembre-se: isso serve apenas para controlar o tráfego externo aos containers – isso não é aplicado no contexto de Input, então tome cuidado achando que é nessa chain que devem ir todas as suas regras.
Outro ponto positivo para essa abordagem é a simplicidade para automatizar, pois basta integrar essa mesma lógica em sua pipeline e você estará protegendo seus containers.
É possível ainda manter aquele seu script maroto de firewall. Basta adicionar mais algumas regras liberando ou não o acesso a determinada porta (na qual quem responderá será um container).
Então era isso. Se quiser saber mais, tirar alguma dúvida ou ainda ajudar, deixe nos comentários ou entre em contato por e-mail.
Grande abraço e boa semana!