Moby: Rede Docker ignora Firewall, sem opção para desativar

Criado em 14 abr. 2016  ·  114Comentários  ·  Fonte: moby/moby

Resultado de docker version :

Client:
 Version:      1.10.3
 API version:  1.22
 Go version:   go1.5.3
 Git commit:   20f81dd
 Built:        Thu Mar 10 15:54:52 2016
 OS/Arch:      linux/amd64

Server:
 Version:      1.10.3
 API version:  1.22
 Go version:   go1.5.3
 Git commit:   20f81dd
 Built:        Thu Mar 10 15:54:52 2016
 OS/Arch:      linux/amd64

Resultado de docker info :

Containers: 14
 Running: 5
 Paused: 0
 Stopped: 9
Images: 152
Server Version: 1.10.3
Storage Driver: aufs
 Root Dir: /var/lib/docker/aufs
 Backing Filesystem: extfs
 Dirs: 204
 Dirperm1 Supported: false
Execution Driver: native-0.2
Logging Driver: json-file
Plugins: 
 Volume: local
 Network: bridge null host
Kernel Version: 3.13.0-58-generic
Operating System: Ubuntu 14.04.4 LTS
OSType: linux
Architecture: x86_64
CPUs: 8
Total Memory: 7.793 GiB
Name: brm-pheonix-dev
ID: Y6Z4:6D53:RFOL:Z3CM:P7ZK:H6HL:RLV5:JT73:LZMC:DTBD:7ILK:2RS5
Username: benjamenmeyer
Registry: https://index.docker.io/v1/

Detalhes adicionais do ambiente (AWS, VirtualBox, físico, etc.):
Rackspace Cloud Server, Ubuntu 14.04, mas isso não deveria importar

Etapas para reproduzir o problema:

  1. Configure o sistema com um firewall bloqueado
  2. Crie um conjunto de contêineres docker com portas expostas
  3. Verifique o firewall; docker usará "qualquer lugar" como fonte, portanto, todos os contêineres serão expostos ao público.

Descreva os resultados que você recebeu:
root @ brm-pheonix-dev : ~ / rse # iptables --list DOCKER
Cadeia DOCKER (1 referências)
destino prot opt ​​origem destino
ACEITAR tcp - em qualquer lugar 172.17.0.2 tcp dpt: 6379

Descreva os resultados que você esperava:
root @ brm-pheonix-dev : ~ / rse # iptables --list DOCKER
Cadeia DOCKER (1 referências)
destino prot opt ​​origem destino
ACEITAR tcp - 127.0.0.0/24 172.17.0.2 tcp dpt: 6379
ACEITAR tcp - 172.16.0.0/16 172.17.0.2 tcp dpt: 6379

Informações adicionais que você considera importantes (por exemplo, o problema acontece apenas ocasionalmente):

Por padrão, o docker está bloqueando o firewall de uma forma que quebra a segurança - permite que todo o tráfego de todos os dispositivos de rede acesse as portas expostas nos contêineres. Considere um site que tem 2 contêineres: o contêiner A expõe 443 executando Nginx e o contêiner B executa uma API na porta 8000. É desejável abrir o contêiner A para uso público, mas ocultar o contêiner B inteiramente para que ele possa se comunicar apenas com o localhost (para teste pelo usuário) e a rede docker (para falar com o Container A). Também pode ser desejável para fins de teste que o Container C seja um banco de dados usado pelo Container B com o mesmo tipo de restrições.

Descobri isso por causa dos logs de monitoramento em um serviço que _pensava_ não ser aberto ao público. Depois de encontrar entradas de log de fontes que tentavam invadir, verifiquei as regras de firewall e descobri que não havia limite de endereços de fonte ou interfaces. Eu uso o UFW e só permito o SSH nesta caixa em particular e prefiro mantê-lo assim. Isso pode impactar drasticamente o uso de contêineres Docker para implantar serviços e levar a possíveis problemas de segurança se as pessoas não tomarem cuidado.

A melhor prática de segurança seria, por padrão, limitar a rede para funcionar como o exemplo de efeito desejado acima e, em seguida, permitir que o usuário adicione o firewall apropriado, regras etc. para substituir esse comportamento, ou ter a opção de reverter para o comportamento atual. Eu sei que por razões de legado isso não é provável, já que quebraria muitas coisas na atualização; então, pelo menos, ter uma opção para habilitar o acima que pode ser ativado agora seria um bom primeiro passo, e talvez mais tarde, após muitos avisos, torne-o o comportamento padrão. Assumindo que o comportamento padrão é seguro, ter funcionalidade para gerenciar isso (firewall-> habilitar porta pública, ip) no docker-compose yml seria uma ótima maneira de tornar visível o que está acontecendo.

Eu encontrei a opção --iptables = false, no entanto, não quero ter que definir todas as regras sozinho. A única coisa que estou contestando é a configuração da fonte para as regras.

Embora eu não tenha verificado, suspeito que todos os firewalls suportados pelo docker terão o mesmo problema.

arenetworking versio1.10

Comentários muito úteis

O problema que Ben está trazendo à luz é real e surpreendente (uma combinação ruim). Muitos administradores, como eu, estão usando o firewall ufw testado e comprovado. O Docker está executando e rodando o ufw e alterando as regras do iptables de tal forma que 1) faz com que o ufw informe incorretamente o status atual das regras de filtragem de pacotes e 2) expõe serviços aparentemente privados à rede pública. Para que docker permaneça nas boas graças da comunidade de administradores de sistemas, outra abordagem deve ser planejada. No momento, existem muitos administradores por aí, que, como Ben e eu, inadvertidamente abriram portas para a Internet mais ampla. Ao contrário de Ben e eu, eles ainda não descobriram.

Todos 114 comentários

Nota: notei em https://github.com/docker/docker/blob/master/vendor/src/github.com/docker/libnetwork/iptables/iptables.go que não há nem mesmo uma opção existente para definir a fonte , então é apenas usar os padrões de iptables para o ip / dispositivo de origem.

Não é bem o # 14041, pois esse problema se refere a portas expostas. A exposição de portas tem o objetivo de torná-las acessíveis ao público, pois é assim que você expõe os serviços para o mundo exterior. Se estiver trabalhando em um ambiente de desenvolvimento, você pode desabilitar o acesso às portas de fora do seu computador com um firewall host ou simplesmente não expor as portas e acessar os serviços diretamente ou de outros contêineres na mesma rede.

Eu recomendo que você use os recursos de rede do docker mais recentes para configurar redes privadas para serviços que você não deseja que sejam expostos, consulte https://docs.docker.com/engine/userguide/networking/

Foi nisso que pensei primeiro; mas estava um pouco confuso, porque _expor_ uma porta ( EXPOSE ), na verdade não faz nada, mas _publicar_ uma porta ( -p / -P ) na verdade a expõe no hospedeiro.

Se você está realmente falando sobre _publicação_, então foi projetado;

Em seu exemplo, o contêiner B e C não devem publicar suas portas, e o contêiner A pode se comunicar com eles por meio da Rede Docker, por exemplo.

docker network create mynet

docker run -d --net=mynet --name=api api-image
docker run -d --net=mynet --name=db database-image
docker run -d --net=mynet --name=web -p 443:443 nginx

Isso publica apenas o contêiner "web" para o host. O contêiner web pode acessar os contêineres "API" e "banco de dados" por meio de seus nomes (por exemplo, http: // api : 80 / e db: 3306 (assumindo MySQL))

@justincormack, então não acho que usar uma rede privada resolve o problema. No meu caso, estou usando uma rede privada entre os contêineres e eles ainda estão expostos publicamente porque o firewall do host não está configurado para limitar a exposição à rede privada.

@thaJeztah o problema ainda se resume ao suporte de firewall - não há suporte de firewall no docker para limitá-lo a uma rede específica. Você provavelmente ainda pode acessar esses contêineres de outro sistema, pois o firewall não impedirá que outros sistemas acessem a porta no host.

Agora estou executando isso via docker-compose; no entanto, não é inteiramente um problema docker-compose, uma vez que a funcionalidade libnetwork não tem _nenhuma_ capacidade de limitar a rede nas regras de firewall - as regras de iptables não têm especificação de origem, portanto, independentemente de como se configura a rede, desde que se dependa do docker para criar o regras de firewall (o que deve ser feito porque é mais provável que sejam acertadas), então isso se torna um problema. Considere o seguinte em um arquivo docker-compose.yml:

nginx:
    build: ./docker/nginx/.
    ports:
        - "127.0.0.1:8080:80"
        - "127.0.0.1:443:443"
    environment:
        DESTINATION_HOST: repose
    links:
        - repose
repose:
    build: ./docker/repose/.
    ports:
        - "127.0.0.1:80:8080"
    environment:
        DESTINATION_HOST: phoenix
        DESTINATION_PORT: 8888
    links:
        - phoenix
curryproxy:
    build: ./docker/curryproxy/.
    ports:
        - "127.0.0.1:8081:8081"
    external_links:
        - rse_rse_1
        - rse_rse_2
        - rse_rse_3
phoenix:
    build: .
    ports:
        - '127.0.0.1:88:8888'
    links:
        - curryproxy:curry
    external_links:
        - rse_rse_1:rse
        - rse_rse_2
        - rse_rse_3
        - rse_cache_1:cache
    volumes:
        - .:/home/phoenix

O texto acima é um trecho de um de meus projetos. Embora eu queira poder testar todos eles localmente em meu host, não quero que ninguém mais possa acessar nada além da instância do nginx.

Não tenho certeza de como isso se traduz em sua nomenclatura ... pode ser que isso seja parte do aspecto de "publicação" e a capacidade de publicação precise ser expandida para fazer o que estou dizendo.

Se isso for intencional, então é um modelo de segurança pobre, já que agora você expõe todos os desenvolvedores a riscos extremos quando em redes desconhecidas (por exemplo, em viagens).

Como eu disse, não espero que o padrão mude imediatamente, mas ter a opção seria um bom primeiro passo.

Estou um pouco confuso, então, você pode dar alguns exemplos externamente do que você pode se conectar? Os serviços de back-end estarão (por padrão) na rede 172.17.0.0/16 , que você não poderá acessar externamente, eu não pensaria primeiro porque você não terá uma rota para aquela definida de um host externo.

Há um possível problema se o seu IP externo também for um IP privado, de modo que o tráfego roteado para as redes internas não será descartado (embora deva ser de público para privado) - esse é o problema?

@justincormack, portanto, estou configurando principalmente o proxy adequado para que alguns serviços só possam ser

firewall:
    external:
        ports:
            - 80
            - 443

Agora, a situação pode ser atenuada um pouco limitando o mapeamento de rede no _host_ a 127.0.0.1 em vez do mapa padrão 0.0.0.0. Observe que isso é o que realmente o atenua, caso contrário, a ponte encaminhará a porta do host para a rede docker.

E sim, eu verifiquei que essa limitação funciona; no entanto, ainda deixa vulnerabilidades potenciais no local e as regras de firewall não correspondem ao que realmente está sendo feito.

Como outro exemplo, havia uma vulnerabilidade do kernel do Linux há pouco tempo (tendo problemas para encontrá-la no momento) que estava relacionada a portas que foram marcadas em IPtables como sendo abertas para uso por aplicativos, mas não estavam realmente conectadas a um aplicativo - por exemplo, estar em uma porta de host local, mas não em uma porta IP pública. Isso potencialmente configura isso, e seria uma prática melhor limitar as regras de IPtables às redes esperadas em vez de deixá-las abertas para se conectar de qualquer lugar. Como eu disse, no mínimo tenho a opção de especificar. Eles provavelmente consertaram esse problema específico, mas por que deixar a possibilidade em aberto?

IOW, é tudo uma questão de segurança.

@BenjamenMeyer se você não quer que os outros serviços sejam acessíveis, por que você publica suas portas? ou seja, "127.0.0.1:8081:8081" não é necessário se for acessado apenas pela rede do docker (outros serviços se conectam diretamente pela rede do docker)

Um problema que tenho relacionado a este é que gostaria de publicar portas, mas apenas permitir que determinados endereços IP as acessem.

Por exemplo, eu executo um ambiente Jenkins em alguns contêineres. O nó mestre é "publicado", mas eu tenho que fazer algumas regras de iptables bastante complicadas para bloqueá-lo de forma que apenas os 2 escritórios que temos possam acessá-lo.

Existe uma maneira de contornar isso embutido no Docker? Ou pelo menos uma prática recomendada? Eu vi na documentação como você pode restringir o acesso a 1 endereço IP; mas não vários. O outro problema com isso é que se você tiver um servidor que já tem uma configuração de iptables, você pode redefinir todas as regras antes de aplicar suas regras (daí as regras complicadas que tive que configurar).

Eu tenho um problema semelhante ao declarado por @SeerUK. Há uma violação chocante de expectativa quando regras de firewall preexistentes não se aplicam às portas de contêiner publicadas. O comportamento desejado é o seguinte (pelo menos para mim)

  1. A tentativa de conexão do usuário é filtrada com base nas configurações de INPUT, etc.
  2. O encaminhamento de tráfego ocorre normalmente com base nas regras FORWARD adicionadas ao docker

Existe uma maneira sucinta de fazer isso no iptables ou não permite tal construção facilmente. Estou particularmente limitado em meu conhecimento de iptables, então tenha paciência comigo. Recentemente, adquiri conhecimento sobre ele ao tentar entender as interações do docker com ele.

O que eu realmente usei por enquanto, já que estou executando esses contêineres em um servidor dedicado muito poderoso, configurei uma VM KVM executando o Docker e, em seguida, usando algumas regras de iptables padrão para restringir o acesso do host . A VM tem sua própria interface de rede que só pode ser acessada a partir do servidor, então tenho que adicionar regras para permitir explicitamente o acesso às portas em iptables no host. Perdi um pouco de desempenho, mas não muito.

@thaJeztah Eu quero ser capaz de acessá-lo do sistema local e testá-lo facilmente. Por exemplo, configurar uma API HTTP RESTful que tem um end-point Health e ser capaz de executar curl forma confiável usando localhost (eu tenho que documentar isso para outros e ter endereços IP que mudam não são confiáveis ) Na maioria dos casos, para meu ambiente de desenvolvimento, quero apenas que os contêineres se comuniquem, mas também quero acessá-los do host.

Para o caso de @SeerUK , ser capaz de definir um bloco de IP (5.5.0.0/16 - um parâmetro válido para um endereço de origem nas regras de iptables) seria uma coisa muito boa. O IPtables já tem a capacidade de fazer a limitação, mas o docker não está tirando proveito disso.

@thaJeztah eu defino "127.0.0.1:8081:8081" explicitamente para mantê-lo fora da rede externa; Eu tinha encontrado logs em meus contêineres docker de pessoas que tentavam invadir os contêineres através das portas expostas.

Meu trabalho agora é desligar os contêineres do docker antes de sair do trabalho porque não posso garantir que o ambiente que desejo que seja externo realmente _é_ externo, ou que o ambiente seja devidamente limitado para fins de segurança.

@BenjamenMeyer uma maneira de fazer isso é executando esses testes em um contêiner, por exemplo

docker run --net -it --rm --net=mynetwork healthchecker 

O problema que Ben está trazendo à luz é real e surpreendente (uma combinação ruim). Muitos administradores, como eu, estão usando o firewall ufw testado e comprovado. O Docker está executando e rodando o ufw e alterando as regras do iptables de tal forma que 1) faz com que o ufw informe incorretamente o status atual das regras de filtragem de pacotes e 2) expõe serviços aparentemente privados à rede pública. Para que docker permaneça nas boas graças da comunidade de administradores de sistemas, outra abordagem deve ser planejada. No momento, existem muitos administradores por aí, que, como Ben e eu, inadvertidamente abriram portas para a Internet mais ampla. Ao contrário de Ben e eu, eles ainda não descobriram.

@thaJeztah que assume que estou fazendo isso por meio da linha de comando e não usando outra ferramenta onde só preciso definir um endereço IP.

Por exemplo, estou trabalhando em uma API. Tenho uma ferramenta que posso usar para trabalhar com essa API em produção para dar suporte a ela; para o desenvolvimento da ferramenta e da API, quero apenas apontar a ferramenta para a API encaixada. A ferramenta não sabe nada sobre o docker, nem deveria. E não quero necessariamente colocar a ferramenta no docker apenas para usá-la - apontar para uma porta exposta apenas ao host local deve ser suficiente.

@jcheroske Eu concordo, mas não sei se existe uma boa solução para _tesse_ aspecto. Para isso, ufw provavelmente precisa ser mais inteligente para ser capaz de pesquisar e relatar as regras que não estava envolvido na criação. Existem muitos softwares que podem ajustar as regras de iptables maneiras que ufw (ou AFAIK firewalld, etc) desconhece. Também não há uma solução simples para consertar isso.

Dito isso, seria bom se o Docker pudesse se integrar com eles para despejar os arquivos de configuração apropriados para habilitá-los / desabilitá-los, ou se integrar com essas ferramentas de forma que fiquem conectados e despejem as informações de forma adequada, no entanto, dado existem soluções melhores que eu não acho que _tesse_ aspecto será realmente resolvido. Aqui, trata-se mais de apenas limitar o escopo das regras iptables que estão sendo geradas para, pelo menos, minimizar os impactos potenciais, permitindo a especificação da fonte (lo, eth0, 127.0.0.0/24, etc).

Se você estiver disposto a fazer isso, usar o iptables torna isso totalmente possível.

Este é um exemplo resumido de como você pode usá-lo: https://gist.github.com/SeerUK/b583cc6f048270e0ddc0105e4b36e480

Você pode ver que bem na parte inferior, 1.2.3.4 recebe explicitamente acesso à porta 8000 (que é exposta pelo Docker) e, em seguida, qualquer outra coisa nessa porta é descartada. A cadeia PRE_DOCKER é inserida para estar antes da cadeia DOCKER para que seja atingida primeiro, o que significa que o DROP impede que as solicitações bloqueadas cheguem à cadeia DOCKER.

É um pouco irritante que o Docker não tenha essa funcionalidade incorporada, mas é possível contornar isso agora.

Outra alternativa seria usar um firewall externo. Alguns lugares como AWS e Scaleway oferecem coisas como grupos de segurança onde você pode gerenciar o acesso às suas caixas de fora, de lá, cada porta se comporta da mesma maneira.

Na verdade, nunca consegui descobrir como fazer isso funcionar com o UFW. Porém, por enquanto, estou feliz em usar o iptables como uma solução. Parece estar funcionando muito bem para mim até agora.

Obviamente, essa não é uma boa solução se você já construiu um conjunto razoavelmente complicado de regras de firewall em torno do UFW. Porém, ele torna o uso de coisas como iptables-persistent bastante fácil. Você também pode usar formas alternativas de permitir o acesso a esta forma que parece mais "normal" no iptables.

@BenjamenMeyer , você pensou em usar um docker network definido pelo usuário com uma opção de sub-rede e intervalo de ip e atribuir um endereço IP estático para contêineres e usá-los para desenvolvimento local para que você não precise depender de um ip estático virtual como 127.0.0.1? Isso evitará a necessidade de mapear todas as portas para os contêineres privados de um host.

docker network create --subnet=30.1.0.0/16 --ip-range=30.1.0.0/24 mynetwork
docker run --net=mynetwork --ip=30.1.1.1 --name=myservice1 xxxx
docker run --net=mynetwork --ip=30.1.1.2 --name=myservice2 yyyy

Com esta configuração, myservice2 pode alcançar myservice1 pelo nome myservice1 e não há necessidade nem mesmo de depender do ip estático. Além disso, o host pode alcançar o ip estático livremente sem a necessidade de mapeamento de porta.

Também com o Compose 1.7, você pode especificar o endereço IP estático para contêineres e especificar sub-redes e intervalos de rede.

Eu descobri uma solução simples.

1) Edite / etc / default / docker: DOCKER_OPTS="--iptables=false"

2) Adicionar regra ufw: ufw allow to <private_ip> port <port>

Tão simples que realmente me pergunto por que a opção --iptables=false não é a padrão. Por que criar tal situação quando tudo o que o docker precisa fazer é dizer: "Ei, se você estiver executando um firewall, terá que abrir um buraco nele!" o que estou perdendo?

https://fralef.me/docker-and-iptables.html
http://blog.viktorpetersson.com/post/101707677489/the-dangers-of-ufw-docker

Não consigo fazer o docker parar de modificar o iptables para salvar minha vida. Tentei atualizar / etc / default / docker sem sucesso no Ubuntu 16.04

@enzeart Experimente /lib/systemd/system/docker.service .

@SeerUK Abençoe sua alma

@enzeart para configurar um daemon em execução em um host que usa o systemd, é melhor não editar o arquivo docker.unit em si, mas usar um arquivo "drop in". Dessa forma, você não terá problemas ao atualizar o docker (no caso de haver um arquivo docker.unit mais recente). Consulte https://docs.docker.com/engine/admin/systemd/#custom -docker-daemon-options para obter mais informações.

Você também pode usar um arquivo de configuração daemon.json, consulte https://docs.docker.com/engine/reference/commandline/daemon/#daemon -configuration-file

@mavenugo Já existe uma rede docker em funcionamento.

@jcheroske isso funciona, mas como eu observei, isso significaria que o _end-user_ (eu) teria que se certificar de que todas as regras iptables estavam corretas, o que não é ideal e não é tão provável de acontecer quanto tendo docker fazendo isso automaticamente, daí este problema.

Oi, por favor. Eu acho que é o problema também. A cadeia de contêineres em Iptables precisa estar após as regras principais e não ser exposta ao mundo por padrão.

Eu realmente gostaria de ver o Docker (e docker-compose) tendo a capacidade de colocar na lista branca ou negra os IPs que podem acessar essa porta.

Considere por exemplo:

nginx:
    ports:
      - "8000:8000"
    whitelist:
      - 10.6.20.2

Isso implicaria que apenas um IP de origem de 10.6.20.2 poderia acessar a porta 8000 neste host.

@StefanPanait Gosto muito dessa ideia. Ele também pode funcionar com uma sintaxe semelhante para volumes e listas de acesso / negação, algo como:

nginx:
  access:
  - "10.0.1.6:allow"
  - "deny"

É claro que ainda teria que permitir coisas como comunicação entre contêineres.

A comunicação entre contêineres do

Embora eu ache que a maneira correta de fazer isso seria separar as redes do docker ... ainda assim, acho que ser capaz de fazer algo como:

nginx: access: - "10.0.1.6:allow" - "webapi:allow" - "database:deny" - "deny"
Poderia ser útil ... a questão é, é útil o suficiente para justificar a implementação nesse grau? Eu não sei.

No momento, gostaria de ver o problema original resolvido; em seguida, recursos como este podem ser adicionados se não fizerem sentido projetar na resolução para começar (talvez).

por que você não deveria proibir um contêiner de falar com ele? Isso pode ser extremamente útil para depuração

É para isso que docker network disconnect destina? Você pode desconectar um contêiner da rede para depuração e reconectá-lo com docker network attach

Para aqueles que acabaram de descobrir que uma tonelada de portas estavam abertas em seus servidores expostos à Internet, depois de utilizar o UFW, eu cavei e cavei e descobri o seguinte:

Ubuntu 16.04 com UFW e Docker apresenta novos desafios. Fiz todas as etapas conforme mostrado aqui: https://svenv.nl/unixandlinux/dockerufw MAS NÃO consegui fazer o docker plus UFW funcionar em 16.04. Em outras palavras, não importa o que eu fizesse, todas as portas do docker ficaram globalmente expostas à Internet. Até eu encontrar isso: http://blog.samcater.com/how-to-set-docker-1-12-to-not-interfere-with-iptables-firewalld/
Tive que criar o arquivo: /etc/docker/daemon.json e colocar o seguinte em:

{
"iptables": falso
}

Em seguida, emiti sudo service docker stop e sudo service docker start FINALMENTE o docker está simplesmente seguindo as regras apropriadas no UFW.

Dados adicionais: https://chjdev.com/2016/06/08/docker-ufw/

@thaJeztah

por que você não deveria proibir um contêiner de falar com ele? Isso pode ser extremamente útil para depuração
É para isso que serve a desconexão de rede do docker? Você pode desconectar um contêiner da rede para depuração e reconectá-lo com a conexão de rede do docker

E se fosse desejado ainda ter conectividade de rede? Exemplo: Teste de falha de verificação de integridade do servidor no contêiner B para o contêiner A, embora ainda tenha serviços fornecidos pelos contêineres C, D e E. Mais fácil simplesmente impedir que o contêiner B leve para o contêiner A do que fechar toda a rede - os contêineres C também podem dependem do acesso ao Container B para que sua verificação de saúde seja aprovada.

Ainda assim, isso não resiste ao "vamos corrigir o problema original".

@ gts24 achado interessante.

IMHO, todo o problema é que o Docker, como literalmente todos os outros programas, não deve tocar no firewall (iptables ou outro) de forma alguma. Quando eu instalar (por exemplo) apache e dizer-lhe para escutar 0.0.0.0:80 é a minha decisão de abrir a porta 80 no firewall ou não, onde posso especificar quaisquer regras para isso que desejo.

Em vez de reinventar regras de firewall nos arquivos de configuração do docker (e / ou composição), todo o recurso PUBLISH deve ser descontinuado e um novo recurso LISTEN criado para funcionar como todos os outros programas. Na melhor das hipóteses, o docker pode criar serviços firewalld desabilitados por padrão para cada porta / contêiner, nos sistemas que os usam.

@gcscaglia defina --iptables=false no daemon e você deve obter isso

@thaJeztah Esse recurso é inútil porque o Docker não oferece alternativa para obter as regras de firewall necessárias (ou ganchos de script de gatilho mais específicos com os parâmetros relevantes) do daemon. Ele mal pode ser usado se você tiver contêineres estáticos e nunca mudar nada sobre eles (por exemplo, portas publicadas), mas para todos os outros casos, você pode esquecê-lo.

@thaJeztah exatamente o que taladar disse.

--iptables=false deve ser renomeado para --networking=false já que nem mesmo a rede interna de contêiner a contêiner funciona com ele desabilitado. Uma opção de ter um contêiner escutando em alguma combinação de porta / interface sem perfurar as regras de firewall de entrada (ou seja, LISTEN ) resolveria tudo isso, seria compatível com versões anteriores e permitiria que --iptables=true fosse usado .

Com esse modo de escuta, aqueles que desejam "apenas funcionar" podem continuar usando PUBLISH , e aqueles que desejam controlar podem usar LISTEN .

@gcscaglia ok, então se você quiser que o docker configure as regras básicas e lide com a rede container-container, mas não "publique". Você pode manter --iptables habilitado, mas _não_ use -p / --publish . Você deve ser capaz de definir manualmente as regras do IPTables para encaminhar portas para contêineres. O contêiner já escuta em seu próprio endereço IP privado e nas portas em que o serviço em seu contêiner escuta.

@thaJeztah Não, você não pode. Porque você não tem ideia de que existe um contêiner que acabou de iniciar e precisa de regras de firewall. Você também não tem como saber quem usou a API para iniciá-la em qual porta do host ela está escutando.

Exemplo simples. Usamos o contêiner Docker para executar jobs do Jenkins. Eles expõem sua porta SSH para que o Docker possa contatá-los. Eles são destruídos assim que o trabalho é concluído. O Docker não oferece nenhuma maneira de fazer isso funcionar com --iptables = false porque você não tem como informar ao Jenkins (usando a API Docker para iniciar o contêiner) a porta do host e não tem como nem mesmo acionar um script para configurar as regras de firewall necessárias.

Sua ideia só funciona para o caso de uso ridiculamente simples de ter contêineres permanentes, nunca mudando, iniciados manualmente. Mesmo um simples --restart = sempre no contêiner será interrompido nesta configuração, a menos que o contêiner tenha um IP estático.

@taladar Estou respondendo ao caso de uso de @gcscaglia , que solicitou um recurso em que o docker _não_ gerencia IPTables para abrir portas e onde eles têm controle sobre IPTables; ou seja, nenhum contêiner é exposto, a menos que seja feito manualmente. Além disso, em minha resposta eu expliquei para _não_ usar --iptables=false , mas para mantê-lo habilitado, mas apenas não use o recurso -p / --publish (que diz ao docker para fazer essas portas acessíveis através do IPTables).

@thaJeztah Embora você tenha

Atualmente, a única solução para o meu caso é ter outro firewall (no qual o docker não pode tocar) entre o host do docker e todas as redes externas. Mas se o Docker simplesmente fizesse tudo que já faz, exceto abrir portas para o mundo, eu poderia acabar totalmente com o segundo firewall.

Acho que você não pode ter tudo, mas com certeza é frustrante. Você acha que vale a pena abrir uma solicitação de recurso sobre minha ideia LISTEN ou a equipe do Docker não teria interesse em tal recurso?

O que LISTEN faria? Há EXPOSE , que permite anotar em quais portas o contêiner escuta. Para gatilhos, você pode ouvir o docker events .

Não estou dizendo que não há espaço para melhorias aqui (eu sei que está sendo investigado), apenas me perguntando o que você esperaria

Atualmente, como você disse, todos os serviços em execução em um contêiner ligam-se ao IP privado do contêiner (se você não usar --net=host ou similar). Isso é bom e desejável, pois esse isolamento entre o host e os contêineres é exatamente o ponto de venda do Docker.

Mas, atualmente, se eu quiser que um aplicativo em execução fora de qualquer contêiner (seja no host ou em outro lugar na rede) tenha acesso a um serviço em execução dentro de um contêiner, preciso de meios para fazer esse serviço escutar em uma das interfaces de rede do host. Para resolver esse problema sem expor nenhuma interface do host ao contêiner, o Docker criou o recurso -p / --publish , que:

  1. Cria regras de iptables para encaminhar uma porta escolhida na interface de um host escolhido para uma porta escolhida no IP privado do contêiner (o que todos esperamos, pois é o que pedimos)
  2. Diz ao iptables para permitir que qualquer pessoa de qualquer lugar do mundo acesse essa porta na interface do host escolhido (o que é desnecessário para o encaminhamento funcionar e, portanto, pega muitos de nós de surpresa)

O que estou propondo é um recurso (denominado LISTEN ou outro) que faz apenas "1" e permite "2" ao critério do usuário, como todos os outros serviços / programas costumam fazer.

Quanto a EXPOSE , AFAIK são apenas metadados em imagens Docker, então o daemon sabe o que fazer quando o usuário especifica -P (publicar tudo). Talvez eu esteja errado sobre isso, e as portas "expostas" podem ser encaminhadas de uma maneira resistente a reinicialização (sem acesso mundial)?

--fora do assunto--

IMHO, o OP desta edição está perguntando exatamente por que -p faz "2" e como impedir o Docker de fazê-lo. Embora uma configuração de nível de daemon (diferente de desativar a rede) possa resolver isso, para manter as coisas compatíveis com versões anteriores, o melhor seria um novo recurso (ou seja, LISTEN ou algum outro nome).

Enquanto muitos usuários gostam deste efeito "funciona fora da caixa", nenhum administrador de sistema espera que outros programas além de seu iptables / firewalld abram portas no firewall, ainda menos de uma forma que seu software de gerenciamento de firewall não faça relatórios.

Vejo que há três problemas principais:

  1. A maneira do Docker de publicar / expor portas contorna as regras de firewall comuns, como o UFW.
  2. O fato acima não parece estar bem documentado (é completamente inesperado, uma vez que nenhum outro serviço Linux que eu conheço contorna as regras de firewall).
  3. Não há uma maneira simples de adaptar o iptables à maneira do Docker de publicar / expor portas ou às formas que as pessoas conhecem não são fáceis de automatizar e nenhuma está documentada.

Talvez não seja tecnicamente viável implementar a exposição de porta na tabela FILTER e, portanto, fixar 1 não é possível. No mínimo, isso deve receber um grande aviso em algum lugar (correção 2), mas o ideal é que 3 possam ser resolvidos com novas opções, como as pessoas aqui sugeriram, como allow ou deny que adicionaria regras de firewall adicionais automaticamente para permitir ou negar IPs específicos para as portas expostas / publicadas.

Por exemplo, allow: "tcp:{your_trusted_ip}" para um contêiner chamado porta de publicação "elasticsearch" 9200 pode fazer algo como:

iptables -t mangle -N DOCKER-elasticsearch
iptables -t mangle -A DOCKER-elasticsearch -s {your_trusted_ip} -j RETURN
iptables -t mangle -A DOCKER-elasticsearch -j DROP
iptables -t mangle -I PREROUTING -p tcp --dport 9200 -j DOCKER-elasticsearch

Achei muito útil nos documentos do Docker: https://docs.docker.com/engine/userguide/networking/default_network/container-communication/#communicating -to-the-outside-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ífica acesse os contêineres, insira uma regra negada no topo da cadeia de filtros DOCKER. Por exemplo, para restringir o acesso externo de forma que apenas o IP de origem 8.8.8.8 possa acessar os contêineres, a seguinte regra pode ser adicionada:

$ iptables -I DOCKER -i ext_if! -s 8.8.8.8 -j DROP

@jmimico sim, já passei por isso antes. Como você restringiria o acesso de 2 ou mais IPs?

O que é tão difícil sobre o Docket apenas adicionar uma opção para executar um script de shell em qualquer lugar onde agora ele cria regras de iptables com todas as informações internas do Docker passadas para o script como parâmetros? Isso permitiria que todos criassem exatamente as regras de que precisam. Adicione alguma forma de disparar uma reexecução dos scripts para os containers ativos que as pessoas podem chamar depois de fazerem um iptables-restore ou liberar as cadeias por alguns outros motivos e pronto.

O Docker não precisa recriar todos os tipos de cenários de firewall pré-construídos, como algumas pessoas aqui estão sugerindo, que poderiam ser construídos em cima de um sistema como aquele por conjuntos de distribuição de scripts de gancho. No máximo, algo como expor apenas no host local e expor globalmente (como o Docker faz agora) pode fazer sentido. O IPTables tem muita flexibilidade para o Docker esperar modelar todos os cenários diretamente nas configurações.

Este tíquete existe aparentemente há uma eternidade, o comportamento atual torna o Docker inutilizável (parece ser a forma padrão de implementar recursos neste projeto, por exemplo, falta de GC integrado adequado ou apenas um armazenamento performante usável, sem bugs no kernel backend, ...) e há uma solução fácil para permitir que as pessoas implementem suas próprias soluções que se adaptam aos seus ambientes.

@StefanPanait Boa pergunta. Minha aposta é que você precisaria alavancar o uso de grupos de objetos. Preencha os grupos de objetos com IPs listados em branco e, em seguida, use esse grupo de objetos na primeira linha da cadeia DOCKER.

Exemplo:
iptables -N docker-allow
iptables -A docker-allow -s 1.1.1.1 -j ACEITAR
iptables -A docker-allow -s 2.2.2.2 -j ACEITAR
iptables -A docker-allow -s 3.3.3.3 -j ACEITAR
iptables -A docker-allow -j DROP

iptables -I DOCKER -i ext_if -j docker-allow

As regras adicionadas à cadeia DOCKER não são prejudicadas por coisas como a reinicialização do daemon? Eu acho que o problema com as soluções manuais é que elas são difíceis de fazer direito e, portanto, há um caso forte para melhor apoiar os casos comuns (bloquear / permitir IPs individuais) mais diretamente.

Talvez um plug-in seja o local apropriado para oferecer suporte a isso? Por exemplo, talvez um plugin "ufw" pudesse adicionar regras de uma forma compatível com ufw para que o usuário pudesse gerenciar o firewall de forma eficaz com sua cadeia de ferramentas de firewall regular e os serviços docker se comportariam mais como serviços de host normais.

As regras adicionadas à cadeia DOCKER não são prejudicadas por coisas como a reinicialização do daemon? Eu acho que o problema com as soluções manuais é que elas são difíceis de fazer direito e, portanto, há um caso forte para melhor apoiar os casos comuns (bloquear / permitir IPs individuais) mais diretamente.

Adicionar a regra de descarte à cadeia DOCKER-USER parece funcionar melhor para torná-la persistente nas reinicializações do docker.

Com o Docker v.17.06, há uma nova cadeia de iptables chamada DOCKER-USER. Este é para suas regras personalizadas, veja minha resposta em serverfault: https://serverfault.com/questions/704643/steps-for-limiting-outside-connections-to-docker-container-with-iptables/886257#886257

Como comentei no SF, não consigo entender por que essa cadeia DOCKER-USER é diferente de qualquer outra cadeia adicionada pelo usuário. Ela não tem nenhum filtro pré-aplicado a ela e filtra todo o tráfego e não apenas o tráfego destinado ao docker-container. você ainda precisa especificar os nomes das interfaces e ainda está sujeito a erros graves de especialistas não-iptables.

Por outro lado, ele ainda segue a mentalidade "Docker é o único usuário de iptables", que é péssima para as pessoas que querem usar iptables para mais do que apenas Docker. Portanto, é ruim para toda a gama de usuários em potencial, além das pessoas que dedicam hosts inteiros a nada além do Docker.

OK, então, usar DOCKER-USER resolve o problema da ordem das inserções, garantindo que sempre venha na cadeia antes das outras regras relacionadas ao Docker. No entanto, isso não torna o tráfego na lista de permissões para um contêiner por número de porta muito mais fácil, pois neste ponto --dport é a porta do serviço dentro do contêiner docker, não a porta exposta. Exemplo:

Publique a porta 9900 para expor um serviço docker escutando internamente no 9000.

$ sudo iptables -A DOCKER-USER -m limit --limit 20/min -j LOG --log-prefix "IPTables: "
$ docker run --rm -it -p '192.168.56.101:9900:9000' alpine nc -l 9000

De outra máquina na rede:

$ telnet 192.168.56.101 9900

O que é registrado:

IPTables: IN=enp0s8 OUT=docker0 MAC=08:00:27:b6:8d:d6:0a:00:27:00:00:04:08:00 SRC=192.168.56.1 DST=172.17.0.2 LEN=52 TOS=0x00 PREC=0x00 TTL=127 ID=14127 DF PROTO=TCP SPT=51208 DPT=9000 WINDOW=64240 RES=0x00 SYN URGP=0
IPTables: IN=docker0 OUT=enp0s8 PHYSIN=veth05ba007 MAC=02:42:0f:f9:76:4c:02:42:ac:11:00:02:08:00 SRC=172.17.0.2 DST=192.168.56.1 LEN=40 TOS=0x00 PREC=0x00 TTL=63 ID=23041 DF PROTO=TCP SPT=9000 DPT=51208 WINDOW=0 RES=0x00 ACK RST URGP=0

Como você pode ver, não há oportunidade neste ponto de filtrar o tráfego para a porta 9900. Claro, eu poderia filtrar o tráfego para a porta 9000, mas isso é um problema porque a porta interna pode se sobrepor acidentalmente entre vários contêineres ou até mesmo serviços em execução no host. Este é um dos grandes pontos de venda do Docker, pois você pode executar vários serviços em um host e não se preocupar com conflitos de porta. Portanto, muitos contêineres são projetados para apenas escutar em uma porta e o usuário pode usar a opção --publish para alterar qual porta é exposta em qual interface:

$ docker run -d -p 7777:6379 --name data1 redis
$ docker run -d -p 8888:6379 --name data2 redis

No entanto, não posso usar DOCKER-USER (corrija-me se estiver errado) para afetar o tráfego de dados1 sem também afetar o tráfego de dados2, a menos que eu use algum tipo de introspecção para descobrir o IP do contêiner de destino, que é temporário e o leva de volta estaca zero para encontrar uma maneira simples e confiável de serviços publicados de firewall sem scripts e introspecção.

Para ser claro, isso NÃO funciona:

$ sudo iptables -A DOCKER-USER -p tcp -m tcp -s 192.168.56.0/24 --dport 7777 -j RETURN
$ sudo iptables -A DOCKER-USER -p tcp -m tcp -s 10.0.24.0/24 --dport 8888 -j RETURN
$ sudo iptables -A DOCKER-USER -p tcp -m tcp --dport 7777 -j DROP
$ sudo iptables -A DOCKER-USER -p tcp -m tcp --dport 8888 -j DROP

Isso funciona, mas o resultado é que ambos os serviços são expostos aos dois CIDRs permitidos:

$ sudo iptables -A DOCKER-USER -p tcp -m tcp -s 192.168.56.0/24 --dport 6379 -j RETURN
$ sudo iptables -A DOCKER-USER -p tcp -m tcp -s 10.0.24.0/24 --dport 6379 -j RETURN
$ sudo iptables -A DOCKER-USER -p tcp -m tcp --dport 6379 -j DROP

Portanto, parece que DOCKER-USER só é útil para expor todas as portas a IPs específicos, mas não expor portas específicas a IPs específicos, a menos que você não se importe de escrever suas regras de iptables para fazer referência a números de portas internas e não ter vários contêineres usando os mesmos números de porta interna. Todos parecem estar perdendo esses pontos e executando com DOCKER-USER como uma solução e eu acho que isso merece uma solução nova e melhor.

@SeerUK
A solução postada em https://gist.github.com/SeerUK/b583cc6f048270e0ddc0105e4b36e480 não funcionou para mim. Poderia ajudar por favor?

o erro do docker é usar iptables (firewall do sistema) para fazer um roteamento de aplicativo. Isso criará problemas e caos para sempre. Alterar o iptables é muito perigoso. Para adicionar esta função embutida no docker, não é tão caro. Além disso, ele também pode ter um status inconsistente simultâneo se você executar o docker de maneira simultânea.

Parece que o iptables tem comportamentos estranhos se você alterar o iptables quando o docker está em execução. Se você parar o docker, defina o iptables e reinicie o docker, todos funcionando conforme previsto. Minha preocupação é ... se eu tiver que mudar o iptables na produção?

Parece que o iptables tem comportamentos estranhos se você alterar o iptables quando o docker está em execução.

iptables é iptables, não importa se o docker está rodando ou não ..

É melhor se você fizer muitos testes antes ... dizer ao iptables que o iptables é apenas uma tautologia. É minha sugestão :)

Acho que o que ele quis dizer foi que o iptables não se comporta de maneira diferente quando o Docker está sendo executado como seu comentário sugeriu. Este é claramente um problema do Docker com a maneira como eles usam o iptables como se ninguém mais precisasse dele no mesmo sistema.

Não vejo por que DOCKER-USER não resolve isso.
Você usa iptables para filtrar como quiser: porta de origem, porta de destino, endereço de origem, tráfego não local, etc.

O ponto principal de DOCKER-USER é que ele executa todas as regras que o usuário deseja executar antes que qualquer regra do docker seja executada. Isso permite que você faça o que quiser com o tráfego antes que ele atinja o docker.

@ cpuguy83 o problema principal é que o docker está abrindo portas para a rede pública - fora do sistema - sem aviso prévio porque não vincula as portas expostas a uma interface de rede (como eth0 ou lo) ou um IP específico (por exemplo, 127.0. 0,1, 172,16.1.1); nem as regras introduzidas pelo Docker aparecem nas ferramentas de gerenciamento de iptables como UFW - assim, os usuários podem não estar cientes de que os vários contêineres do docker podem ser acessados ​​de qualquer sistema na rede, não apenas de seu host local.

O DOCKER-USER não pode resolver isso porque também não vincula as redes do docker a uma interface de rede específica ou a um IP específico (por exemplo, 127.0.0.1).

Por minha solicitação original:
1. O Docker deve se vincular a um IP específico - com base na configuração da Rede Docker - e então permitir que o usuário adicione regras para expor o contêiner fora do sistema (usando as ferramentas de sua escolha); por padrão, o contêiner não deve ser exposto fora do sistema.

  1. Isso não pode ser feito sem um período de transição, pois pelo menos algumas pessoas contam com o comportamento histórico.

IMHO, todo o problema é que o Docker, como literalmente todos os outros programas, não deve tocar no firewall (iptables ou outro) de forma alguma. Quando eu instalo (por exemplo) o apache e digo para escutar em 0.0.0.0:80, é minha decisão abrir a porta 80 no firewall ou não, onde posso especificar as regras que eu desejar.
Em vez de reinventar as regras de firewall nos arquivos de configuração do docker (e / ou compose), todo o recurso PUBLISH deve ser descontinuado e um novo recurso LISTEN criado para funcionar como todos os outros programas. Na melhor das hipóteses, o docker poderia criar serviços firewalld desabilitados por padrão para cada porta / contêiner, nos sistemas que os usam.

Bem dito @gcscaglia ! Ainda mais desconcertante, há pouca ou nenhuma menção a tudo isso em https://docs.docker.com/engine/reference/run/#expose -incoming-ports, que eu acho que é onde a maioria daqueles que se importam em procurar um pouco de documentação para complementar o aprendizado por exemplo. Deve haver uma caixa vermelha brilhante em algum lugar explicando os riscos de o Docker substituir as regras de iptables pré-existentes.

Se você não quiser que o docker gerencie iptables, defina --iptables-=false
Se você deseja apenas que o docker abra portas em uma interface específica, você também pode definir isso na configuração do daemon.

@BenjamenMeyer
Você pode bloquear todo o tráfego em DOCKER-USER e apenas deixar passar o que quiser.

Você pode bloquear todo o tráfego em DOCKER-USER e só deixar passar o que você quiser.

@ cpuguy83 Por favor, diga-me se alguma coisa que eu disser aqui estiver incorreta:

$ docker run -d -p 7777:6379 --name data1 redis
$ docker run -d -p 8888:6379 --name data2 redis

No entanto, não posso usar DOCKER-USER (corrija-me se estiver errado) para afetar o tráfego de dados1 sem também afetar o tráfego de dados2, a menos que eu use algum tipo de introspecção para descobrir o IP do contêiner de destino, que é temporário e o leva de volta estaca zero para encontrar uma maneira simples e confiável de serviços publicados de firewall sem scripts e introspecção.

Para ser claro, isso NÃO funciona:

$ sudo iptables -A DOCKER-USER -p tcp -m tcp -s 192.168.56.0/24 --dport 7777 -j RETURN
$ sudo iptables -A DOCKER-USER -p tcp -m tcp -s 10.0.24.0/24 --dport 8888 -j RETURN
$ sudo iptables -A DOCKER-USER -p tcp -m tcp --dport 7777 -j DROP
$ sudo iptables -A DOCKER-USER -p tcp -m tcp --dport 8888 -j DROP

Isso funciona, mas o resultado é que ambos os serviços são expostos aos dois CIDRs permitidos:

$ sudo iptables -A DOCKER-USER -p tcp -m tcp -s 192.168.56.0/24 --dport 6379 -j RETURN
$ sudo iptables -A DOCKER-USER -p tcp -m tcp -s 10.0.24.0/24 --dport 6379 -j RETURN
$ sudo iptables -A DOCKER-USER -p tcp -m tcp --dport 6379 -j DROP

Portanto, não há como controlar o tráfego de / para o contêiner data1 independentemente do contêiner data2 usando DOCKER-USER . Para mim, isso faz com que DOCKER-USER não seja uma solução.

@colinmollenhour

Se você tiver uma regra de remoção genérica em DOCKER-USER , como isso é diferente se docker anexou vs anexou sua regra de salto principal?

@ cpuguy83 meu ponto não é contra a substituição em si. O que estou dizendo é que fazer com que o Docker substitua as regras de iptables pré-existentes deve ser definitivamente um recurso opcional, não um recurso opt-out.

E mesmo como um opt-out - o que não deveria ser - deve ser extremamente bem documentado, pois é completamente inesperado e bastante contra-intuitivo para depurar. Especialmente considerando quantos usam ufw . Não conheço nenhum outro software fazendo algo assim sem avisos muito explícitos e, pessoalmente, ficaria longe desse tipo de software.

Além disso, isso é agravado pelo fato de que a maioria dos usuários do Docker - pelo menos na minha experiência - começa a usá-lo em ambientes onde a infraestrutura de rede adicional mascara o problema, reforçando a suposição de que a máquina está configurada como eles acreditam que está.

Espero que o Docker mude para uma abordagem somente LISTEN, como exemplificado por @gcscaglia .

Se você tiver uma regra de descartar genérica em DOCKER-USER, como isso difere se docker anexou vs prefixou sua regra de salto principal?

Acho que não entendi sua pergunta ... Por favor, ignore meu comentário de 22 de abril de 2017, pois é apenas em relação à persistência que o DOCKER-USER realmente resolve. O problema que estou apontando não é sobre a persistência.

@taladar O que você está

@jacoscaz

Primeiro, acho que todos os mantenedores concordam que o comportamento existente não é o ideal. Infelizmente, o comportamento existe desde sempre. Alterar um padrão para algo que é usado por milhões de pessoas não é realmente algo que possamos fazer aqui. Esta é uma das razões pelas quais adicionamos DOCKER-USER para que pelo menos as pessoas possam injetar as regras de que precisam.

No entanto, não podemos evitar a necessidade de usar iptables (ou ebpf, ou alguma outra solução de gerenciamento) E fornecer a funcionalidade que -p fornece ... outro lado, se você quiser evitar que as pessoas façam furos no firewall, desautorize-os de usar -p .

Para recapitular, você pode:

  1. Diga ao docker para (por padrão) vincular-se a um endereço específico (o endereço padrão é 0.0.0.0 , ou todas as interfaces) ... no entanto, isso não impedirá que alguém especifique manualmente um endereço em -p spec (por exemplo, -p 1.2.3.4:80:80 )
  2. Injetar regras personalizadas em DOCKER-USER , incluindo negar todos
  3. Desative o gerenciamento de iptables com --iptables=false

Há algo mais que você possa sugerir que não inclua interromper os usuários existentes?

@ cpuguy83 Eu entendo isso. Não estou defendendo que tal mudança aconteça drasticamente de uma versão para outra. Eu acho que seria uma boa mudança planejar ao longo de várias versões. Também não acho que o Docker deva parar de usar iptables inteiramente. Encaminhar é diferente de permitir e, pelo que posso entender, o Docker deve ser capaz de encaminhar por padrão, sem permitir o acesso a ninguém em qualquer lugar por padrão.

Quanto às coisas que podem ser feitas agora, a documentação adicional sobre isso deve ser a primeira e mais importante. Qual seria o canal mais adequado para levantar isso com os mantenedores do docker.com?

Na ausência de interesse da parte deles, manter essa conversa viva é provavelmente a melhor maneira de maximizar as chances de esse comportamento se tornar mais conhecido.

Concordo com a chamada para adicionar documentação adicional para esta funcionalidade. Pelo que eu posso dizer, ele só é mencionado em https://docs.docker.com/network/iptables/. Eu só descobri esse problema depois de tentar descobrir por que um serviço que eu havia restringido para estar disponível a um IP específico por meio do firewall estava disponível ao público. Esse comportamento foi completamente inesperado para mim, pois é contrário a todos os outros serviços de rede com os quais já trabalhei.

Eu entendo que existem soluções alternativas, mas acredito que isso deve ser analisado para uma mudança de longo prazo para o funcionamento do Docker. Eu gosto da ideia de defini-lo como LISTEN em uma porta, e permitir que regras definidas pelo usuário sejam construídas em cima disso.

Como sugerido, adicionei uma regra DROP ao DOCKER-USER para evitar que os contêineres sejam expostos ao exterior acidentalmente (o que, de forma alarmante, aconteceu).

No entanto, agora tenho um serviço que desejo expor. Mas como @colinmollenhour explicou, como o NAT acontece antes da filtragem, só posso filtrar no ip do docker (que não é fixo) e no número da porta interna (que pode ser o mesmo para vários contêineres).

Então, como posso expor esse serviço?

@SystemParadox é uma das muitas razões pelas quais DOCKER-USER não é uma solução real para o problema.

@ cpuguy83
Eu gosto da solução LISTEN proposta e nunca defendi uma mudança significativa de uma versão para outra; mas, em vez disso, defendia fazer a mudança em uma série de versões com avisos apropriados saindo, b / c eu percebo que muitas pessoas usam o Docker e ter uma mudança brusca de uma versão para outra seria prejudicial para todos.

Também concordo que atualizar a documentação do Docker com relação a -p e EXPOSE, etc. deve ser uma prioridade feita imediatamente para, pelo menos, chamar a atenção para o problema. Na minha experiência, a maioria das pessoas que usam o Docker não são especialistas em firewall, portanto, estão confiando que o Docker fará o que esperam, o que não está no design atual.

Além disso, as soluções de recapitulação em https://github.com/moby/moby/issues/22054#issuecomment -425580301 também não funcionam. Porque? Eu não executo o Docker diretamente, eu executo o Docker Compose - baseado no YAML; Os endereços IP são dinâmicos (controlados pelo Docker) e geralmente implanto vários serviços na mesma rede Docker que precisam interagir uns com os outros. Portanto, tanto o uso de -p quanto o uso de ligação de endereço (opção 1 na recapitulação) não são soluções. O DOCKER-USER não resolve nada como outros apontaram (opção 2 na recapitulação) e desabilitar as tabelas de IP totalmente (opção 3 na recapitulação) também não ajuda em nada b / c agora tudo está quebrado (IPs são dinâmicos, então difícil fazer o script de uma solução; a rede entre contêineres está quebrada b / c Docker depende do IPTables para mover entre os contêineres; etc).

Novamente, não há nenhuma chamada neste segmento para uma alteração significativa entre duas versões; mas uma chamada para uma abordagem planejada em fases que permita que as pessoas migrem de forma adequada.

Como alternativa, você pode acessar a porta de destino original usando -m conntrack --ctorigdstport .

Portanto, no meu caso, tenho o seguinte:

-A DOCKER-USER -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# Allow docker out
-A DOCKER-USER -s 172.17.0.0/16 -j ACCEPT
# Allow access to docker service mapped to host 8702 (the service is actually listening on port 8088 in the container)
-A DOCKER-USER -p tcp -m conntrack --ctorigdstport 8702 -j ACCEPT
# Prevent access to docker from outside
-A DOCKER-USER -j DROP

@SystemParadox

Viva a solução correta de iptables! 🍻

Eu nunca tinha ouvido falar de --ctorigdstport mas acho que é porque eu não li e tentei todas as extensões iptables possíveis e você é o primeiro a mencioná-lo neste contexto que eu saiba.

Eu testei e isso realmente funciona:

$ docker run -d -p 7777:6379 --name data1 redis
$ docker run -d -p 8888:6379 --name data2 redis
$ sudo iptables -N DOCKER-USER-redis1
$ sudo iptables -A DOCKER-USER-redis1 -s 192.168.56.0/24 -p tcp -m tcp -j RETURN
$ sudo iptables -A DOCKER-USER-redis1 -j REJECT --reject-with icmp-port-unreachable
$ sudo iptables -N DOCKER-USER-redis2
$ sudo iptables -A DOCKER-USER-redis2 -s 10.0.24.0/24 -p tcp -m tcp -j RETURN
$ sudo iptables -A DOCKER-USER-redis2 -j REJECT --reject-with icmp-port-unreachable
$ sudo iptables -A DOCKER-USER -i eth0 -p tcp -m conntrack --ctorigdstport 7777 -j DOCKER-USER-redis1
$ sudo iptables -A DOCKER-USER -i eth0 -p tcp -m conntrack --ctorigdstport 8888 -j DOCKER-USER-redis2

Acho que um exemplo como este pertence aos documentos, pois provavelmente cobre o que 99% dos usuários estão procurando: a capacidade de expor portas usando -p mas ainda ser capaz de controlar o tráfego para elas usando filtros comuns como -s .

Eu criei uma solicitação para atualizar a documentação do Docker sobre iptables.

https://github.com/docker/docker.github.io/issues/8087

A solução listada lá em https://unrouted.io/2017/08/15/docker-firewall/
parece ser algo semelhante, criando uma cadeia de iptables adicional chamada FILTERS
para onde as cadeias INPUT e DOCKER-USER vão.

@SystemParadox @colinmollenhour Depois de testar --ctorigdstport posso confirmar que funciona, mas com uma pequena advertência.

No meu caso, tenho um aplicativo PHP encaixado no Apache ouvindo na porta 80. Minhas regras que permitem apenas 1.2.3.4 são as seguintes:

-A DOCKER-USER -s 1.2.3.4/32 -i eth0 -p tcp -m conntrack --ctorigdstport 80 -j ACCEPT
-A DOCKER-USER -i eth0 -p tcp -m conntrack --ctorigdstport 80 -j DROP

Portanto, minha regra de descarte é um pouco mais específica do que a sua, descartando apenas os pacotes que atingem meu servidor web - ou assim pensei. Na verdade, ele estava descartando pacotes direcionados ao meu servidor web, bem como pacotes retornando com respostas de solicitações feitas por aplicativos PHP para servidores de terceiros.

Isso se deve ao fato de que --ctorigdstport não corresponde à porta de destino no pacote que está sendo filtrado, mas no pacote que iniciou a conexão. Portanto, as respostas às solicitações que saem do Docker para outros servidores terão SPT=80 e também corresponderão a --ctorigdstport 80 .

Se alguém quiser ter um controle mais rígido das regras DROP, --ctdir também deve ser adicionado:

-A DOCKER-USER -i eth0 -p tcp -m conntrack --ctorigdstport 80 --ctdir ORIGINAL -j DROP

Na verdade, todas as regras que permitem a conexão também devem ter --ctdir adicionado para expressar exatamente seu significado:

-A DOCKER-USER -s 1.2.3.4/32 -i eth0 -p tcp -m conntrack --ctorigdstport 80 --ctdir ORIGINAL -j ACCEPT

@jest uau, isso é realmente importante saber! Eu não tinha percebido que poderia combinar pacotes na outra direção. Faz sentido quando você pensa sobre isso, já que corresponde ao estado de toda a conexão, mas é fácil passar despercebido ao ler os documentos.

@SystemParadox sim, não tive a chance de me informar por meio dos documentos e fui pego de surpresa por solicitações do Docker que aguardavam respostas. :)

Eu continuo andando em círculos com os motivos para precisar de --ctdir ORIGINAL . Por um lado, a explicação de @jest faz todo o sentido e, por outro lado, normalmente nunca preciso lidar com pacotes de resposta, então por que deveria ser diferente aqui?

Eu acho que a diferença é que eu tenho -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT como a primeira regra, então o resto das minhas regras nunca vê nenhum pacote de resposta. Nesse caso, acho que --ctdir ORIGINAL não é estritamente necessário, embora provavelmente seja mais seguro incluí-lo de qualquer maneira.

@jest , você concorda com isso? Presumivelmente, você não tem uma regra ESTABLISHED,RELATED -j ACCEPT , e é por isso que isso faz diferença para você?

@jest Sua postagem foi uma grande ajuda, obrigado.

A sua abordagem é necessária apenas para coisas gerenciadas pelo docker ou para tudo? Por exemplo, minha porta ssh (22) não tem nada a ver com docker. Preciso usar -m tcp --dport 22 normalmente ou devo usar -m conntrack --ctorigdstport 22 --ctdir ORIGINAL ?

Presumo que sua abordagem seja necessária apenas para o tráfego gerenciado pelo docker, pois esses pacotes sofrem mutilação / alteração antes de chegarem a mim na tabela de filtros. MAS, eu sou novo no iptables, então quero ter a certeza de alguém que sabe mais do que eu!

@ lonix1 As regras são adicionadas pelo docker à cadeia DOCKER apenas se você expor uma porta e, provavelmente, apenas quando o contêiner estiver em execução. Se em nenhum dos seus contêineres você expõe a porta 22, você deve ter suas regras de firewall funcionando sem modificações.

@SystemParadox Já faz algum tempo e não tenho acesso a esse servidor para verificar, mas se bem me lembro, havia regras ESTABLISHED,RELATED (gerenciadas pelo UFW, na cadeia ufw-before-input ). No entanto, no meu caso, eles não corresponderiam ao primeiro pacote (SYN) de conexões feitas do docker para hosts da Internet na porta 80. E seriam descartados pela regra apropriada em DOCKER-USER quando não havia --ctdir .

@ Ionix1 , os pacotes para serviços no host passam apenas por INPUT, enquanto os pacotes para serviços docker passam apenas por FORWARD e DOCKER-USER.

Por exemplo, dado um IP externo de 10.0.0.1 e dois contêineres com -p 4000:80 e -p 4001:80 , você veria pacotes com as seguintes propriedades:

INPUT:
dst 10.0.0.1 dport 80 ctorigdst 10.0.0.1 ctorigdstport 80
FORWARD/DOCKER-USER:
dst 172.17.0.5 dport 80 ctorigdst 10.0.0.1 ctorigdstport 4000
dst 172.17.0.6 dport 80 ctorigdst 10.0.0.1 ctorigdstport 4001

Portanto, você pode usar --dport 80 com segurança para as regras INPUT, já que elas estão em cadeias totalmente separadas. Como você pode ver, --ctorigdstport 80 ainda corresponderia, mas a menos que você também esteja alterando a entrada por algum motivo, eu provavelmente não faria isso.

Você também pode notar que pode realmente usar --dport 80 com --dst 172.17.0.5 para filtrar pacotes para um contêiner docker específico, mas esse IP não é previsível, é por isso que estamos usando --ctorigdstport .

Em última análise, você precisa estar ciente de quais pacotes qualquer regra pode corresponder, dependendo da cadeia em que você está, qual é o destino e se há alguma mutilação acontecendo.

@muito obrigado, isso parece confirmar meu pensamento.

Então, estou precisando de um pouco de orientação ... para mudar de UFW para iptables ...
- eu simplesmente desligo o UFW via "desativar ufw"?
- eu crio meu próprio arquivo .sh ou existe um (sou Ubuntu no DigitalOcean)?
-i só preciso dizer ao Docker "--iptables = false"? (é isso para o Docker?)
-DOCKER-USER já foi criado e encadeado pelo Docker?

@fredjohnston você pode continuar usando o UFW se quiser. Os perfis são armazenados em /etc/ufw . O problema aqui é que o Docker não aparece b / c não há nenhum perfil de aplicativo listado em /etc/ufw/applications.d (o mesmo para qualquer outra ferramenta de firewall e sua configuração).

Desativar IPTables no Docker significa que você não terá muito de qualquer rede no Docker, apenas endereços IP e contêineres não serão capazes de se comunicar. DOCKER_USER é um hack para lhe dar algum controle, mas realmente não resolve o problema - que na verdade é sobre não tornar os contêineres Docker públicos na rede por padrão, mas bloqueado para o endereço IP do contêiner.

No momento, eu recomendo que você continue usando qualquer ferramenta de Firewall com a qual você se sinta mais confortável (ufw, etc), mas esteja ciente de que os Docker Containers serão públicos em sua rede.

Já que estou aqui de qualquer maneira - estou realmente tendo um problema relacionado agora, onde este é um problema em outras plataformas além do Linux. Considere o seguinte:

  • O Projeto A tem dois contêineres, um para um banco de dados e outro para um aplicativo.
  • O Projeto B tem dois contêineres, um para um banco de dados e outro para um aplicativo.
  • Ambos os projetos são isolados um do outro (repositórios de origem separados, configurações, etc)
  • Ambos os projetos são gerenciados no Docker Compose
  • Ambos os projetos expõem suas portas de banco de dados para fins de desenvolvimento local
  • Ambos os projetos usam o mesmo servidor de banco de dados (postgres, mysql, etc)

Agora, suponha que você queira executar os dois projetos localmente - por exemplo, para trabalhar em uma biblioteca compartilhada que os dois projetos usam para que você possa colocá-la facilmente em seu código para teste.

Sob o atual design de interação do firewall - e o que em parte leva aos problemas acima sobre a exposição do contêiner à rede pública sem o conhecimento do usuário - os dockers de banco de dados de ambos os projetos não podem ser executados ao mesmo tempo, uma vez que estarão na disputa pelo porta exposta para o banco de dados. Mesmo se você quisesse expor a porta do aplicativo e ambos usassem a mesma porta para o servidor do aplicativo - comum, pois APIs e aplicativos baseados em HTTP são extremamente comuns agora, especialmente em aplicativos orientados à nuvem.

Claro, você pode hackear sua maneira de configurar ambos em um contêiner de banco de dados; mas você não os está isolando de acordo com o design do seu projeto e precisa ser ainda mais cuidadoso com as configurações, etc.

Em uma solução adequada, aqui está duas vezes:

  1. Os contêineres seriam vinculados apenas a seus IPs e suas portas expostas seriam vinculadas apenas a seus IPs em suas respectivas redes Docker, não correspondendo no sistema a todos os IPs ( 0.0.0.0 , :: ).
  2. O Docker também não exporia publicamente a rota para as Redes Docker fora do sistema por padrão. O Docker Networking pode ser utilizado para estabelecer conexões entre redes (rede docker para rede docker) conforme projetado atualmente e, em seguida, também permitir conexões de host locais por padrão.
  3. Os usuários estariam então no gancho para adicionar as regras de firewall apropriadas para expor o contêiner ao mundo externo quando e se desejado - por exemplo, encaminhando a porta 443 para a porta 443 de seu contêiner de escolha.

Novamente, isso pode ser feito gradualmente:

  • Versão 1: Implementar as etapas 1 e 2; mas adicione o roteamento não-localhost também (temporariamente) com um aviso. O comportamento atual de ordem de chegada para obter uma porta é mantido; e um aviso sobre o desaparecimento desse comportamento é emitido.
  • Versão 1 + N: elimine o aviso e elimine o roteamento de host não local. Exija a Etapa 3 para usuários que desejam que o Docker exponha as portas do sistema e certifique-se de que isso esteja bem documentado.

DOCKER_USER é um hack para lhe dar algum controle, mas realmente não resolve o problema - que na verdade não é tornar os contêineres Docker públicos na rede por padrão, mas bloqueado para o endereço IP do contêiner.

No momento, eu recomendo que você continue usando qualquer ferramenta de Firewall com a qual você se sinta mais confortável (ufw, etc), mas esteja ciente de que os Docker Containers serão públicos em sua rede.

Há uma solicitação de atualização de documento aqui https://github.com/docker/docker.github.io/pull/8357 para criar uma configuração estática iptables bolt-on, mas não tenho certeza do status disso.

Edit: Percebi que você entendeu mal o significado de DOCKER-USER. Não é usado para "tornar os containers bloqueados para o endereço IP do container", mas para filtrar o acesso ao container com regras de iptables.

@ aki-k por DOCKER_USER Estou ciente de que DOCKER_USER não bloqueia o contêiner contra o endereço IP e nunca alegou que o fez. DOCKER_USER simplesmente passa o problema de segurança para o usuário gerenciar - o que significa que o usuário deve saber como manipular seu firewall para garantir que ele realmente tenha um ambiente seguro. Torná-lo um problema do usuário é igualmente inaceitável porque a maioria dos usuários não sabe como gerenciar seu firewall - as regras de firewall são difíceis, e mesmo aqueles de nós que sabem um pouco sobre como escrever regras de firewall ainda podem errar.

Minha solicitação - e toda a questão - no problema é que o Docker precisa ser seguro por padrão e não expor os contêineres para o mundo externo sem o conhecimento do usuário ou intervenção explícita para fazê-lo. E, de acordo com meu último comentário (https://github.com/moby/moby/issues/22054#issuecomment-552951146), isso também trará alguns outros benefícios técnicos.

Minha solicitação - e toda a questão - no problema é que o Docker precisa ser seguro por padrão e não expor os contêineres para o mundo externo sem o conhecimento do usuário ou intervenção explícita para fazê-lo.

O primeiro relatório de problema que encontrei sobre isso é de 18 de março de 2014:

https://github.com/moby/moby/issues/4737

Provavelmente é uma decisão de design que eles não querem mudar e tentar consertar com a cadeia iptables DOCKER-USER. Você pode usar a opção -p para docker run para publicar a porta apenas para o host docker (-p 127.0.0.1:port:port), mas isso também não está resolvendo o problema.

Vamos ser claros, o Docker é seguro por padrão. Você tem que dizer ao Docker para fazer o encaminhamento de porta.

Quanto aos serviços que precisam se comunicar entre si, você deve usar redes ( docker network ) para limitar o acesso, não o encaminhamento de porta.

Vamos ser claros, o Docker _Is_ é seguro por padrão. Você tem que dizer ao Docker para fazer o encaminhamento de porta.

Não descarte um problema real com o docker executando sobre a proteção iptables estabelecida. Tenho certeza de que você já viu os incontáveis ​​relatórios de problemas discutindo isso.

Vamos ser claros, o Docker _Is_ é seguro por padrão. Você tem que dizer ao Docker para fazer o encaminhamento de porta.

Quanto aos serviços que precisam se comunicar entre si, você deve usar redes ( docker network ) para limitar o acesso, não o encaminhamento de porta.

Vamos ser claros - o Docker é seguro até que você queira acessá-lo fora da rede do docker. Uma vez que você deseja acessá-lo até mesmo de um host local (127.0.0.1), o Docker não é seguro por padrão, pois se liga a 0.0.0.0 e expõe os contêineres fora do sistema - ignorando qualquer coisa que o usuário faça para proteger seu sistema, sem aparecer em ferramentas que não sejam o uso direto de iptables . DOCKER_USER não é e nunca será uma solução adequada porque requer que o usuário saiba muito sobre o sistema de firewall subjacente (iptables no Linux, seja o que for que o Mac use e o Firewall do Windows no Windows). Isso ainda precisa ser seguro por padrão. Há uma maneira muito fácil de fazer isso, conforme descrevi anteriormente em https://github.com/moby/moby/issues/22054#issuecomment -552951146 e chamei várias vezes ao longo destes problemas (embora talvez não tão claramente como naquele Comente).

Observe também que a documentação para expor uma porta também não menciona esse problema de segurança, sem fazer referência a como o Docker interage com o firewall do sistema ao expor uma porta - reduzindo assim a segurança em sistemas que foram projetados para serem protegidos até o Docker ser usado .

Vamos ser claros, o Docker _Is_ é seguro por padrão. Você tem que dizer ao Docker para fazer o encaminhamento de porta.
Quanto aos serviços que precisam se comunicar entre si, você deve usar redes ( docker network ) para limitar o acesso, não o encaminhamento de porta.

Vamos ser claros - o Docker é seguro até que você queira acessá-lo fora da rede do docker. Uma vez que você deseja acessá-lo até mesmo de um host local (127.0.0.1), o Docker _não_ é seguro por padrão, pois se liga a 0.0.0.0 [...]

Para ser preciso, o Docker não se vincula ao 0.0.0.0. Essa é a expectativa (razoável) do usuário: especificar --publish na CLI, portanto operando no espaço do usuário, inicia algum tipo de daemon de proxy que escuta na porta especificada, aceita conexões de entrada e empurra para frente e para trás todos os pacotes entre o Docker e o mundo exterior.

Mas, em vez disso, o Docker injeta regras DNAT / masquarading mágicas no firewall para reescrever os endereços no pacote, quebrando aleatoriamente qualquer sistema de regras pré-instalado.

Na minha opinião, bagunçar níveis de abstrações aqui é a maior chatice e confunde o usuário. Não sei quais cenários foram considerados pela equipe do Docker ao projetar o maquinário --publish que vemos aqui, não vejo nenhum que justifique a decisão (talvez além dos motivos de desempenho).

Vamos ser claros, o Docker é seguro por padrão. Você tem que dizer ao Docker para fazer o encaminhamento de porta.

... que passa a ser um dos recursos mais usados ​​do Docker. Efetivamente, você acabou de declarar que uma substituição não documentada de regras de firewall pré-existentes em um dos recursos mais usados ​​do Docker torna o Docker _seguro por padrão_.

Bem, o que funciona para você, eu acho. Como não funciona para mim, começarei a procurar plataformas de contêiner alternativas. Os problemas de segurança podem ser corrigidos, mas a desconsideração flagrante das expectativas de segurança razoáveis ​​é uma besta diferente.

Vamos avaliar a situação como ela é hoje:

Por padrão, nenhuma porta é exposta. Você tem que dizer ao Docker para expor uma porta.
No passado, o Docker configurava iptables de forma que qualquer coisa que soubesse como rotear para a rede de ponte pudesse acessar os IPs do contêiner (definindo a política de encaminhamento para "aceitar"), mas isso não é mais verdade.

Tenho visto algumas pessoas dizendo que estão usando -p para expor serviços uns aos outros, o que não deveria ser obrigatório.
Na rede padrão, você pode usar --link para conectar os serviços, o contêiner está disponível no DNS.
Em redes não padrão (redes definidas pelo usuário), os contêineres também podem acessar uns aos outros por DNS, incluindo a configuração de aliases por meio de --link ou até mesmo a uma rede com um alias especificado.

Parece que em muitos casos você realmente deseja apenas se conectar ao serviço do cliente, caso em que é recomendável usar outro contêiner com acesso ao serviço ao qual deseja se conectar, em vez de expor uma porta.

-p é projetado especificamente para entrada, como em permitir que coisas externas acessem este serviço.
O padrão para -p é permitir tráfego de qualquer lugar. Você pode alterar isso especificando manualmente o endereço a ser permitido por -p ou como uma configuração ampla do daemon.
Como -p usa iptables, a cadeia DOCKER-USER foi criada para que os usuários possam adicionar suas próprias regras de filtro antes de atingir o contêiner.

Uma vez que -p é projetado para entrada, acho que é razoável expor o tráfego como o faz. Eu realmente sinto que é uma pena que as regras do Docker sejam inseridas na parte superior da tabela de filtros, no entanto, mudar isso seria uma mudança significativa para um grande grupo de usuários que desejam esse comportamento.

Existem algumas outras alternativas para -p :

  1. Não use -p , conecte-se ao IP do contêiner diretamente. Isso requer um pouco de trabalho extra, já que você precisa consultar o IP, mas esses dados estão disponíveis na API. Você também precisa se certificar de que a política de encaminhamento no firewall permite isso (supondo que você esteja se conectando de um host diferente, o mesmo host deve servir)
  2. Use a rede macvlan ou ipvlan para serviços que você deseja que sejam acessíveis a partir da rede do host (ou seja, ingresso). Essas opções de rede fornecem ao contêiner um IP diretamente da interface de rede do host (você escolhe a qual interface ele está vinculado).
  3. Use --net=host , isto executa o serviço no namespace da rede host dando ao serviço acesso a infra de rede já existente no host.

Você diz "tornar isso seguro por padrão", mas expor uma porta é, por definição, uma ação potencialmente insegura. Também parece haver alguma ideia de que expor apenas ao host local é seguro, mas não é porque qualquer coisa em execução no host pode acessar o host local (incluindo javascript em um navegador, se for um desktop).

Que caso de uso você está tentando resolver usando -p ?
Você tem alguma opinião sobre a mudança real que gostaria de ver?

Não há problema em alterar algo para tornar isso melhor para o seu fluxo de trabalho, mas há muitos casos de uso diferentes aqui e um tamanho nunca serve para todos (consulte as reclamações sobre -p ).

@ cpuguy83 está tudo bem e elegante, mas não muda nada neste pedido.

As pessoas usam o docker e desejam se conectar a aplicativos em execução no docker de seu sistema local - este é um caso de uso extremamente comum para desenvolvedores, especialmente ao tentar diagnosticar algo ou escrever um serviço que não está no docker, mas precisa se conectar a serviços hospedados no docker (para preencher um ambiente de desenvolvimento). Desenvolver no Docker nem sempre é uma experiência boa, divertida ou útil, dizendo:

é recomendável usar outro contêiner com acesso ao serviço ao qual você deseja se conectar, em vez de expor uma porta.

É simplesmente um impedimento. Nem todas as ferramentas são compatíveis com o Docker, nem deveriam ser. Não é necessário que o usuário esteja totalmente sob o Docker para utilizar os serviços em execução no Docker. Mesmo assim, se o Docker for usado para operar servidores, o administrador não poderá controlá-los facilmente por meio da configuração do firewall e a funcionalidade Expose Port, embora seja orquestrada (linha de comando, Dockerfile, Docker Compose Config) está totalmente corrompida.

Além disso, as pessoas usam o Docker Compose para gerenciar grande parte do ambiente e especificam por meio de docker-compose.yml ou Dockerfile que uma porta precisa ser exposta para que possam acessá-la localmente. Portanto, dizer o parâmetro use the -p é incorreto, pois eles nunca fazem interface com o comando docker diretamente de uma forma que funcione.

Expor uma porta não significa que a segurança deve ser quebrada. Descrevi como você pode expor uma porta ao sistema local sem quebrar a segurança (https://github.com/moby/moby/issues/22054#issuecomment-552951146) e isso colocaria o gerenciamento da exposição externa (desligado sistema) nas mãos do usuário de uma forma que ele possa controlar facilmente com as ferramentas existentes.

Solução:

  • Use Docker Network
  • Exponha as portas na rede Docker sozinhas e no host local
  • Pare de vincular a porta em 0.0.0.0 - ou efetivamente fazendo isso
  • Exigir que os usuários usem suas próprias ferramentas de firewall para expor o sistema de port off (ufw, firewalld, etc)
  • Fornece integrações para firewalls comuns para tornar isso mais fácil

Em outras palavras, em vez de acessar um serviço em um contêiner docker por meio de 127.0.0.1:<port> requeira <docker container ip>:<service port> mesmo do host local. Se as pessoas quiserem expor o serviço fora do sistema, elas podem adicionar uma regra de firewall por meio de suas ferramentas (ufw, etc) para encaminhar a porta de uma determinada porta para <docker container ip>:<service port> .

Como alternativa, siga a abordagem do Kubernetes com seu design de proxy, efetivamente fazendo como @jest sugerido em https://github.com/moby/moby/issues/22054#issuecomment -554665865. Novamente, isso ainda precisaria ser um sistema local apenas até que o usuário o expusesse propositalmente para a rede externa.

O maior problema aqui é que o Docker está comprometendo a integridade do firewall para fornecer seus serviços, fazendo isso sem dizer nada ao usuário e sem se integrar às ferramentas do usuário para que o usuário não saiba nem possa controlá-lo.

Sei que há uma tonelada de interfaces de firewall no mercado - mesmo com iptables, há firewalld, ufw e uma dúzia de outros. Eu realmente não espero que o Docker se integre com eles (embora isso seja bom), mas espero que o Docker funcione de uma maneira que não os ignore ou quebre qualquer segurança que o usuário tenha configurado.

Como um caso de teste básico:

  • Configure um servidor baseado em Debian (Debian, Ubuntu)
  • Instale ufw, OpenSSH Server
  • execute ufw allow OpenSSH
  • execute ufw enable

Neste ponto, você tem um servidor bastante seguro; o único tráfego permitido é (a) relacionado ao tráfego de saída ou (b) tráfego do servidor SSH.

Agora, inicie um contêiner docker com uma porta exposta.

Uma solução aceitável atenderia ao seguinte:

  • A porta não deve ser acessível de outro computador; o firewall deve continuar a bloqueá-lo de sistemas externos.
  • A porta deve estar acessível a partir do computador local (localhost).
  • Vários contêineres docker devem ser capazes de expor a mesma porta e fazer com que tudo funcione; todos os contêineres com uma porta exposta devem ser acessíveis apenas a partir do sistema local.
  • O usuário não deve saber sobre os detalhes de configuração do firewall para que isso aconteça.

Tente o mesmo para um sistema baseado em CentOS / Fedora / RHEL com firewalld .

@ cpuguy83 O pessoal da IMO espera que -p funcione em um nível de aplicativo. Ou seja, se eu -p 80:80 , espero o comportamento como se o aplicativo vinculado à porta 80 no contêiner estivesse em execução no host.

As portas do modelo VirtualBox ou SSH encaminham dessa forma, então as pessoas presumem que o mesmo ocorre no caso do Docker.

Para usar um paralelo de expectativa mais amplo: do ponto de vista do usuário, ele deve funcionar como volumes vinculados ao host. Basta apontar para o diretório do host e, "como mágica", ele fica visível dentro do contêiner; quaisquer modificações feitas no sistema de arquivos do outro lado funcionam da mesma forma, incluindo permissões, cota, etc.

Limitando -p problemas aos casos de firewall: no mundo normal, o usuário espera que um aplicativo vinculado a 0.0.0.0:80 seja visível para o mundo externo. Se o usuário deseja limitar o acesso, ele é instruído em vários guias a usar um firewall e configurar uma regra na cadeia INPUT :

-P INPUT DROP
-A INPUT -s 1.2.3.4/32 -p tcp --dst-port 80 -j ACCEPT

ou use ferramentas como UFW:

ufw enable
ufw allow http

Mas com o docker, o usuário deve ser capaz de construir essas regras do nada:

-A DOCKER-USER -s 1.2.3.4/32 -i eth0 -p tcp -m conntrack --ctorigdstport 80 --ctdir ORIGINAL -j ACCEPT
-A DOCKER-USER -i eth0 -p tcp -m conntrack --ctorigdstport 80 --ctdir ORIGINAL -j DROP

Por favor, mostre-me um guia abrangente para um usuário médio sobre como resolver esses cenários comuns com o Docker.

E para, por exemplo, um desenvolvedor médio - para quem você vende todo o conceito de "containerização de aplicativos" - as consequências de expor uma porta são simplesmente imprevisíveis.

Atualmente, simplesmente não exponho as portas devido à minha falta de conhecimento e uso serviços como o Traefik. Talvez --expose também não deva ser proposto como um propósito geral nos documentos CLI, porque IMO simplesmente não faz nenhum bem para um usuário médio.

Ou forneça a qualquer pessoa sem conhecimento profundo dessas ferramentas um laptop Linux, como Dell ou Lenovo, e testemunhe como seus esforços genuínos para configurar corretamente seus firewalls não fazem absolutamente nenhuma diferença quando alguém consegue acessar seu banco de dados local enquanto tomando um café no Starbucks.

Esse problema exato causou em nós uma vulnerabilidade do sistema que descobri no fim de semana. Por @jacoscaz , consegui acessar o banco de dados local de outro desenvolvedor porque ele tem uma porta publicada. Seria ótimo se vocês pudessem incluir isso na documentação de introdução para que outros não façam o mesmo. Precisamos de um serviço não contêiner para se conectar a um contêiner sem que todos os outros na rede tenham acesso, portanto, as redes Docker estão fora de questão. Parece que a melhor opção por enquanto é se conectar ao IP do contêiner local, a menos que alguém tenha uma ideia melhor.

@dentonmwood Isso soa exatamente como você deve fazer. Como mencionei acima, você pode até configurar o serviço para rodar usando macvlan ou ipvlan que vai dar um IP diretamente na sua rede normal.

@BenjamenMeyer @jest @jacoscaz

Obrigado pelo feedback extra.

Concordo que o conhecimento que estamos pedindo aos usuários que posam a esse respeito não é bom. No momento, colocamos a responsabilidade sobre o usuário (seja um administrador de sistema ou um desenvolvedor) para entender o que -p faz e tomar as precauções adequadas para garantir que o serviço seja exposto apenas a quem eles pensam que está exposto. Levamos isso um passo adiante, mesmo esperando que os desenvolvedores que geralmente não têm nenhuma razão para conhecer o iptables entrem e corrijam em seu próprio ambiente (via DOCKER-USER ).
Estamos basicamente nessa situação devido ao medo de quebrar a compatibilidade.

Tenho algumas ideias que ainda não tive tempo de pensar totalmente, mas basicamente mudariam o comportamento de -p base na versão API do cliente e trataria o ingresso separadamente. Ainda uma mudança significativa que me preocupa, mas pelo menos o comportamento antigo é preservado nas versões mais antigas da API.

Eu pensei que poderíamos fazer um proxy local (ala kubectl proxy ) para o caso de uso de acesso local, no entanto, isso coloca novamente o ônus sobre o desenvolvedor de saber e entender mais do que realmente deveria .

Pensamentos?

Acho que a introdução do proxy é um passo na direção certa.

Eu reconheço que o "encaminhamento de porta" é uma responsabilidade compartilhada entre um contêiner e um orquestrador, semelhante em espírito ao modo Swarm. Mas a integração mínima do host é sempre necessária e ao dar uma opção como -p ao usuário (não especialista), a solução proposta deve ser compreensível para tal pessoa.

Como as -p consequências para o ambiente não são especificadas (pelo menos no nível CLI, eu acho), existe a possibilidade de introduzir uma configuração amigável do modo como funciona.

Por exemplo, no momento há iptables em /etc/docker/daemon.json que determina se deve manipular DOCKER ou não. A configuração poderia ser estendida com outra entrada, de forma que uma combinação como iptables==true && expose_with_network_proxy==true ativasse o comportamento de proxy.

Isso deve ser geralmente seguro para novas instalações, pois era preferível overlay2 vez de aufs algum tempo atrás (se bem me lembro). E facilmente implantado como tal, já que as atualizações não afetam os arquivos de configuração existentes, mas as novas instalações são gratuitas.

Estamos basicamente nessa situação devido ao medo de quebrar a compatibilidade.

Eu realmente gostaria de enfatizar o fato de que a gravidade desse problema, pelo menos aos meus olhos, tem mais a ver com a falta de documentação do que com os fundamentos técnicos atuais de -p . Sim, não acredito que o mecanismo atual possa ser considerado _seguro_, mas as falhas de segurança podem ser corrigidas e não estou em posição de criticar os motivos que levaram ao cenário atual. Afinal, a retrospectiva é sempre 20/20, e admiro o compromisso com a compatibilidade com versões anteriores e a disposição para enfrentar os desafios técnicos que esse compromisso traz.

O que não entendo é por que ainda não há caixas vermelhas grandes e brilhantes na documentação do Docker alertando explicitamente contra os efeitos colaterais do uso de -p . Se eu tivesse me deparado com tal aviso, acho que nem mesmo estaria aqui em primeiro lugar.

Pensamentos?

A opção de proxy parece razoável. Além disso, gosto da ideia de poder expor e cancelar a exposição de portas sempre que necessário, em vez de ser obrigado a fazer isso durante o lançamento de docker run .

@BenjamenMeyer @jest @jacoscaz

Obrigado pelo feedback extra.

Obrigado por finalmente olhar para isso mais de perto.

Concordo que o conhecimento que estamos pedindo aos usuários que posam a esse respeito não é bom. No momento, colocamos a responsabilidade sobre o usuário (seja um administrador de sistema ou um desenvolvedor) para entender o que -p faz e tomar as precauções adequadas para garantir que o serviço seja exposto apenas a quem eles pensam que está exposto. Levamos isso um passo adiante, mesmo esperando que os desenvolvedores que geralmente não têm nenhuma razão para conhecer o iptables entrem e corrijam em seu próprio ambiente (via DOCKER-USER ).
Estamos basicamente nessa situação devido ao medo de quebrar a compatibilidade.

Tenho algumas ideias que ainda não tive tempo de pensar totalmente, mas basicamente mudariam o comportamento de -p base na versão API do cliente e trataria o ingresso separadamente. Ainda uma mudança significativa que me preocupa, mas pelo menos o comportamento antigo é preservado nas versões mais antigas da API.

Pensamentos?

  1. Comece atualizando a documentação para que os usuários estejam cientes do efeito de EXPOSE de todos os métodos (-p, Dockerfile, docker-compose.yml). Isso pode ser feito rapidamente. Também pode ajudar a obter alguma atenção sobre o problema para obter mais pessoas oferecendo conselhos / experiência para encontrar uma boa solução e ajudar a responder se há um desejo da comunidade de compatibilidade com versões anteriores também.
  2. Planeje um método para ir de onde o Docker está agora para onde o Docker precisa estar. Isso pode incluir uma alteração significativa em algum ponto.

Acho que a gravidade da situação certamente permite uma mudança significativa para consertá-la; apenas não faça isso sem informar as pessoas. Estabeleça um cronograma (6 meses? 12 meses?) De onde a mudança seja amplamente discutida, etc. e você prepara a comunidade; então, em uma versão "principal", faça a mudança. Com base em seu esquema de versão, parece que o primeiro conjunto é o ano (19); como estamos no final de 2019, use o restante de 2019 e, em seguida, 2020 para descobrir a solução e anunciá-la; introduza-o como um recurso opcional em 2020 e, em seguida, promova-o para o primeiro lugar / uso padrão em 2021.

A versão do Dockerfile e a versão do esquema do Docker Compose podem ser boas ferramentas também no que diz respeito a definir um comportamento padrão, mas eu não impediria que versões mais antigas tirassem proveito disso e colocaria um forte aviso sobre a necessidade de atualização em tal caso também.

Seja como for, acho que você achará que a comunidade em geral apoiará muito mais essa mudança se entender por que e o que está acontecendo e não sentir que está sendo pego de surpresa por isso.

Eu pensei que poderíamos fazer um proxy local (ala kubectl proxy ) para o caso de uso de acesso local, no entanto, isso coloca novamente o ônus sobre o desenvolvedor de saber e entender mais do que realmente deveria .

Eu gosto da ideia do proxy, e a sugestão de @jest de como habilitá-lo pode muito bem ser um ótimo método de fazer a migração também.

Honestamente, a maioria das pessoas que procuram expô-lo fora do sistema, ou devem estar familiarizados com firewalls até certo ponto, mesmo que seja apenas como configurar UFW ou firewalld para fazer isso. Portanto, se a solução o tornar disponível apenas para o host local, acho que é aceitável que as pessoas aprendam a usar suas ferramentas de firewall para encaminhar uma porta através do firewall.

Eu acho que é importante que essa funcionalidade seja feita por meio das ferramentas de firewall que o usuário decidiu usar, pois isso tornará tudo visível para eles. Essa funcionalidade não deve ignorar suas ferramentas. Também percebo que existem tantas ferramentas de firewall por aí que não é razoável para o Docker integrar-se a todas elas. Portanto, sugiro seguir uma rota de documentação e destacar como fazê-lo com alguns dos mais populares para começar, e deixar a comunidade adicionar mais e atualizá-los.

Eu gosto da ideia do proxy, e a sugestão de @jest de como habilitá-lo pode muito bem ser um ótimo método de fazer a migração também.

Honestamente, a maioria das pessoas que procuram expô-lo fora do sistema, ou devem estar familiarizados com firewalls até certo ponto, mesmo que seja apenas como configurar UFW ou firewalld para fazer isso. Portanto, se a solução o tornar disponível apenas para o host local, acho que é aceitável que as pessoas aprendam a usar suas ferramentas de firewall para encaminhar uma porta através do firewall.

Eu concordo com isso. Com a solução de proxy, acho que seria viável criar algo que permitiria aos usuários vincular o proxy a qualquer combinação de interface (ões) e porta (s) que desejassem. No entanto, se for forçado a fazer concessões em nome da compatibilidade com versões anteriores (que eu apoio!) Ou por qualquer outro motivo, a prioridade deve ser dada para corresponder às expectativas de segurança razoáveis, mesmo ao custo de delegar a exposição fora do sistema aos usuários.

Na sequência do meu comentário, o proxy pode realmente operar de maneiras diferentes, dependendo das ferramentas de firewall disponíveis. Se uma ferramenta de configuração de firewall compatível for detectada, o proxy pode usá-la para definir as regras de encaminhamento apropriadas. A presença de tal ferramenta pode ser adicionada como um requisito para ambientes de produção. Se essa ferramenta não estiver disponível, o proxy terá como padrão gerar um servidor proxy no nível do aplicativo.

O Docker ignora o firewall do macOS da mesma forma que no Linux.

Na minha máquina de desenvolvimento (Docker Desktop para Mac), adicionei "ip": "127.0.0.1" à configuração do daemon do Docker. Desta forma, qualquer banco de dados de desenvolvimento, etc., estará, por padrão, acessível apenas a partir do localhost. Para aqueles raros casos em que preciso tornar um aplicativo visível para outras pessoas, posso publicá-lo explicitamente com -p 0.0.0.0:8080:8080 .

Editar: parece que o Docker Compose ainda se liga a 0.0.0.0 por padrão, independentemente da configuração do daemon do Docker. Portanto, esse truque funciona apenas ao executar o Docker a partir da linha de comando. E de qualquer forma, uma vez que os arquivos do Compose são frequentemente compartilhados com colegas de equipe que podem ter configurações de sistema diferentes, é melhor adicionar 127.0.0.1 explicitamente ao arquivo do Compose.

@luontola Que solução elegante.

Edit: O que eu também estava pensando era talvez uma maneira de ter uma lista de permitir / bloquear de endereços que poderia ser definida, por exemplo, Docker compose que seria então automaticamente adicionada à cadeia de iptables DOCKER-USER.

Uma pequena margarida oopsie que destaca a importância desse problema: https://github.com/docker/for-linux/issues/810 (resumo: DOCKER-USER foi removido, portanto, mesmo se você configurou o iptables para bloquear o acesso externo dos dockers, isso iria ignorar essa configuração extra e expor todos os contêineres)

Comentários @danhallin como os seus podem realmente salvar o dia em algumas situações. Obrigado por compartilhar.

Hmm leitura interessante!

Estou executando o Mac OS X e também tenho um firewall local chamado LittleSnitch. Ele apenas apareceu uma caixa de diálogo perguntando se estava OK para 185.156.177.252 se conectar ao processo com.docker.backend. Eu comecei a negar.

Eu queria saber como isso poderia abrir um encaminhamento de porta no meu roteador, mas acabei de perceber que isso é feito via UPnP! Todos os roteadores suportam isso.

O que estou me perguntando agora é o porquê. Por que algo de 185.156.177.252 está tentando se conectar de fora? Se meu processo Docker local precisar de algo, ele deve chamar de lar de dentro, não abrir uma porta do lado de fora.

@ cpuguy83 está tudo bem e elegante, mas não muda nada neste pedido.

As pessoas usam o docker e desejam se conectar a aplicativos em execução no docker de seu sistema local - este é um caso de uso extremamente comum para desenvolvedores, especialmente ao tentar diagnosticar algo ou escrever um serviço que não está no docker, mas precisa se conectar a serviços hospedados no docker (para preencher um ambiente de desenvolvimento). Desenvolver no Docker nem sempre é uma experiência boa, divertida ou útil, dizendo:

é recomendável usar outro contêiner com acesso ao serviço ao qual você deseja se conectar, em vez de expor uma porta.

É simplesmente um impedimento. Nem todas as ferramentas são compatíveis com o Docker, nem deveriam ser. Não é necessário que o usuário esteja totalmente sob o Docker para utilizar os serviços em execução no Docker. Mesmo assim, se o Docker for usado para operar servidores, o administrador não poderá controlá-los facilmente por meio da configuração do firewall e a funcionalidade Expose Port, embora seja orquestrada (linha de comando, Dockerfile, Docker Compose Config) está totalmente corrompida.

Além disso, as pessoas usam o Docker Compose para gerenciar grande parte do ambiente e especificam por meio de docker-compose.yml ou Dockerfile que uma porta precisa ser exposta para que possam acessá-la localmente. Portanto, dizer o parâmetro use the -p é incorreto, pois eles nunca fazem interface com o comando docker diretamente de uma forma que funcione.

Expor uma porta não significa que a segurança deve ser quebrada. Eu descrevi como você pode expor uma porta ao sistema local sem quebrar a segurança ( # 22054 (comentário) ) e isso colocaria o gerenciamento da exposição externa (fora do sistema) nas mãos do usuário de uma forma que ele possa facilmente controle com suas ferramentas existentes.

Solução:

* Use Docker Network

* Expose the Ports on the Docker Network alone and local host

* Stop binding the port on 0.0.0.0 - or effectively doing so

* Require users to use their own firewall tooling to expose the port off system (ufw, firewalld, etc)

* Provide integrations for common firewalls to make this easy

Em outras palavras, em vez de acessar um serviço em um contêiner docker por meio de 127.0.0.1:<port> solicite <docker container ip>:<service port> mesmo do host local. Se as pessoas quiserem expor o serviço fora do sistema, elas podem adicionar uma regra de firewall por meio de suas ferramentas (ufw, etc) para encaminhar a porta de uma determinada porta para <docker container ip>:<service port> .

Como alternativa, siga a abordagem do Kubernetes com seu design de proxy, efetivamente fazendo como @jest sugerido em # 22054 (comentário) . Novamente, isso ainda precisaria ser um sistema local apenas até que o usuário o expusesse propositalmente para a rede externa.

O maior problema aqui é que o Docker está comprometendo a integridade do firewall para fornecer seus serviços, fazendo isso sem dizer nada ao usuário e sem se integrar às ferramentas do usuário para que o usuário não saiba nem possa controlá-lo.

Sei que há uma tonelada de interfaces de firewall no mercado - mesmo com iptables, há firewalld, ufw e uma dúzia de outros. Eu realmente não espero que o Docker se integre com eles (embora isso seja bom), mas espero que o Docker funcione de uma maneira que não os ignore ou quebre qualquer segurança que o usuário tenha configurado.

Como um caso de teste básico:

* Setup a Debian-based server (Debian, Ubuntu)

* Install ufw, OpenSSH Server

* run `ufw allow OpenSSH`

* run `ufw enable`

Neste ponto, você tem um servidor bastante seguro; o único tráfego permitido é (a) relacionado ao tráfego de saída ou (b) tráfego do servidor SSH.

Agora, inicie um contêiner docker com uma porta exposta.

Uma solução aceitável atenderia ao seguinte:

* The port should not be accessible from another computer; the firewall should continue to block it from outside systems.

* The port should be accessible from the local computer (localhost).

* Multiple docker containers should be able to expose the same port and have everything work; all containers with an exposed port should be accessible from the local system only.

* The user should not have to know about their firewall configuration details to make this happen.

Tente o mesmo para um sistema baseado em CentOS / Fedora / RHEL com firewalld .

Eu me deparei com esse problema enquanto tentava descobrir se há uma maneira de configurar o docker para não contornar minhas regras de entrada do firewall sempre que uma porta é publicada. O comentário acima faz um bom trabalho explicando a coisa toda e também uma abordagem mais preferível, IMO.

Ontem, tropecei neste assunto por acidente. Passei semanas personalizando meticulosamente minhas regras de firewall de iptables para meu VPS. Tentei ser cauteloso e restritivo. A entrada e a saída do filtro são configuradas para cair por padrão. Eu permito explicitamente determinado tráfego e bloqueio todo o resto. O tráfego é registrado e tenho testado e monitorado.

Eu sabia que o Docker fez suas próprias alterações na cadeia Forward e na tabela NAT. Portanto, tenho sido _extremamente_ cuidadoso para respeitar essas alterações em todo o meu processo. Além disso, todos os meus contêineres são anexados a redes definidas pelo usuário. A rede host padrão nunca é usada. A documentação oficial nos diz para não fazer isso.

Minha primeira surpresa foi que meu Nginx Proxy em contêiner estava acessível para toda a Internet. Tenho uma opção em minhas regras de firewall para permitir o tráfego da Web de entrada do mundo. Mas eu não tinha ativado esse recurso ainda. Em nenhum lugar em meu iptables é óbvio que HTTP 80/443 é permitido.

Alguns de meus aplicativos da web contam com bancos de dados como MySQL. Inicialmente, _não_ usei a opção -p no Docker Compose. Eu sabia que não era necessário, já que meu aplicativo da web e o servidor db compartilhavam a mesma rede Docker definida pelo usuário. Mas, como ex-DBA, estou sempre pensando em backups. Portanto, ativei a opção -p para permitir que os cron jobs e as ferramentas de backup do meu

Depois da minha surpresa no Nginx, decidi sabiamente verificar se o MySQL também não estava exposto. Tentei conectar (do meu laptop) ao banco de dados MySQL no meu VPS remoto. E fiquei novamente chocado quando conectei com sucesso imediatamente.

Nada do que estou dizendo não foi discutido longamente em posts anteriores. Muito obrigado a @BenjamenMeyer @jest @jacoscaz por sua investigação e sugestões úteis. Mas para aqueles que solicitam experiências e casos de uso modernos? Aqui está. Mais de 4 anos após o início deste tópico, pessoas como eu ainda enfrentam esse comportamento. E ainda voltando me sentindo chocado.

Sim, existem muitas soluções alternativas. Vou perseguir alguns imediatamente. Mas, para implementá-los, você precisa realmente saber que esse problema existe. Isso é o que mais me decepciona. Não que os desenvolvedores do Docker tenham tomado certas decisões de design, sejam boas ou ruins.

Mas esse "comportamento de desvio do firewall" não é claramente divulgado, avisado em voz alta e documentado de forma mais ampla. Quando se trata de segurança de rede, surpresas nunca são boas.

Desejo compartilhar minha solução alternativa para esse problema, caso outros a considerem útil. Usando iptables e ipset. Testado no Ubuntu, CentOS 7 e RHEL 7. ipset é usado uma vez que os IPs "confiáveis" nem sempre estão no mesmo intervalo de IP (correção da limitação do iptables).
Ele funciona com Docker normal e Docker Swarm (também conhecido como SwarmKit).
Ele garante que você esteja seguro por padrão, permitindo apenas que os IPs especificados por você sejam capazes de se conectar às portas do sistema operacional e às portas do Docker (usando iptables e ipset) que você especifica que devem ser abertos. Também tem a opção de tornar uma porta do sistema operacional ou porta do Docker "pública" (aberta para todos os IPs)

O Ansible não é necessário, mas torna-se mais fácil. Função do Ansible: https://github.com/ryandaniels/ansible-role-iptables-docker
Etapas manuais para CentOS / RHEL:
https://github.com/ryandaniels/ansible-role-iptables-docker#manual -commands-centosrhel
E Ubuntu 18.04 / 20.04:
https://github.com/ryandaniels/ansible-role-iptables-docker#manual -commands-ubuntu-2004

Você precisará instalar / configurar o ipset além do iptables (veja os links acima se tiver problemas).
Exemplo de configuração de iptables (com a porta 22 do ssh aberta a todos):

*filter
:DOCKER-USER - [0:0]
:FILTERS - [0:0]
#Can't flush INPUT. wipes out docker swarm encrypted overlay rules
#-F INPUT
#Use ansible or run manually once instead to add -I INPUT -j FILTERS
#-I INPUT -j FILTERS
-A DOCKER-USER -m state --state RELATED,ESTABLISHED -j RETURN
-A DOCKER-USER -i docker_gwbridge -j RETURN
-A DOCKER-USER -s 172.18.0.0/16 -j RETURN
-A DOCKER-USER -i docker0 -j RETURN
-A DOCKER-USER -s 172.17.0.0/16 -j RETURN
#Below Docker ports open to everyone if uncommented
#-A DOCKER-USER -p tcp -m tcp -m multiport --dports 8000,8001 -j RETURN
#-A DOCKER-USER -p udp -m udp -m multiport --dports 9000,9001 -j RETURN
-A DOCKER-USER -m set ! --match-set ip_allow src -j DROP
-A DOCKER-USER -j RETURN
-F FILTERS
#Because Docker Swarm encrypted overlay network just appends rules to INPUT
-A FILTERS -p udp -m policy --dir in --pol ipsec -m udp --dport 4789 -m set --match-set ip_allow src -j RETURN
-A FILTERS -m state --state RELATED,ESTABLISHED -j ACCEPT
-A FILTERS -p icmp -j ACCEPT
-A FILTERS -i lo -j ACCEPT
#Below OS ports open to everyone if uncommented
-A FILTERS -p tcp -m state --state NEW -m tcp -m multiport --dports 22 -j ACCEPT
#-A FILTERS -p udp -m udp -m multiport --dports 53,123 -j ACCEPT
-A FILTERS -m set ! --match-set ip_allow src -j DROP
-A FILTERS -j RETURN
COMMIT
Esta página foi útil?
0 / 5 - 0 avaliações