Se um contêiner do estivador já se vincular à porta 443 em um dos IPs de suas interfaces (ou 0.0.0.0 significando todas as interfaces), outros contêineres do docker não poderão se vincular ao mesmo IP. Verifique com o netstat, enquanto um contêiner 1 está ativo:
sudo netstat -nalp64 | grep 443
tcp 0 0 0.0.0.0:443 0.0.0.0:* LISTEN 26547/docker-proxy
Como a porta 443 em 0.0.0.0 já é usada por um contêiner do docker, novos contêineres não podem ser vinculados a esse IP + Porta.
Visualização
0.0.0.0:443 (Error: Port 443 already in use)
| \
+--------------+ +--------------+
| CONTAINER | | CONTAINER |
| 172.0.0.2 | | 172.0.0.3 |
+--------------+ +--------------+
Em vez de vincular vários contêineres à mesma Porta, você precisa de uma vinculação de software à porta, redirecionando as conexões para o contêiner apropriado.
Isso é feito com mais facilidade através da execução de um proxy reverso dedicado, que é o único programa vinculado à porta (443). O propósito do proxy reverso é encaminhar as conexões de entrada com base no host HTTP solicitado.
O proxy reverso pode ser executado no host físico executando a janela de encaixe ou dentro de um contêiner docker.
O proxy reverso também pode finalizar conexões SSL, o que significa que essa instância nginx manipula todos os en / descriptografia de / para clientes, enquanto as conexões para o backend (containers) não são criptografadas.
Eu não acho que isso seja estritamente necessário, os navegadores modernos suportam SNI, então o nginx ainda pode encaminhar solicitações para o back-end apropriado sem descriptografar todo o tráfego. Mas, usando uma terminação SSL central, você só precisa de certificados em um local e o SSL só precisa ser configurado uma vez globalmente para a maioria dos casos de uso.
Para configurar tal proxy reverso com terminação SSL
- Instale o nginx (proxy reverso) no host do docker
- Definir IPs estáticos ou Hostnames para contêineres
- Disponibilizar os arquivos SSL Certificate + Private Key dos contêineres para o proxy reverso nginx
- Defina nginx upstreams para seus contêineres do Docker dentro da configuração de proxy reverso
- Defina servidores nginx ("vhosts") para exibir nomes de domínio definidos por
server_name
- Encaminhar solicitações para upstreams definidas em
location
usandoproxy_pass
Exemplo:
Meu /etc/nginx/sites-enabled/dockerproxy
é assim:
# gitlab
upstream gitlab
{
server 172.20.0.2;
}
# docker registry
upstream registry
{
server 172.20.0.3:5050;
}
# dev.mycompany.org
server
{
listen 10.10.10.40:80 default;
listen 10.10.10.40:443 ssl default;
server_name dev.mycompany.org;
ssl_certificate /data/run/certbot/data/live/dev.mycompany.org/fullchain.pem;
ssl_certificate_key /data/run/certbot/data/live/dev.mycompany.org/privkey.pem;
location /
{
proxy_pass http://gitlab/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# registry.mycompany.org
server
{
listen 10.10.10.40:443 ssl;
server_name registry.mycompany.org;
ssl_certificate /data/run/certbot/data/live/registry.mycompany.org/fullchain.pem;
ssl_certificate_key /data/run/certbot/data/live/registry.mycompany.org/privkey.pem;
ssl_session_cache builtin:1000 shared:SSL:60m;
ssl_session_timeout 60m;
client_max_body_size 0;
chunked_transfer_encoding on;
location /
{
proxy_pass http://registry/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Observe que as diretivas proxy_set_header
estritamente não são necessárias, elas dependem do que os aplicativos de back-end individuais esperam.
Como você pode ver, esta configuração diz ao nginx para:
- Vincular a 10.10.10.40:443
- Solicitações de proxy para dev.mycompany.org para 172.20.0.2 [: 80] (IP do contêiner do gitlab)
- Solicitações de proxy para registry.mycompany.org para 172.20.0.3:5050 (IP do contêiner de registro)
- Encerrar o SSL usando os arquivos de certificado fornecidos (diretamente de um contêiner do certbot, no meu caso)
Visualização
0.0.0.0:443
|
+-----------------------+
| nginx |
+-----------------------+
| |
+--------------+ +--------------+
| VHOST | | VHOST |
| web.app1.com | | web.app2.com |
+--------------+ +--------------+
| |
+--------------+ +--------------+
| CONTAINER | | CONTAINER |
| 172.0.0.2 | | 172.0.0.3 |
+--------------+ +--------------+
Definindo outros objetos upstream
e server
usando diferentes diretivas server_name
, você pode disponibilizar outros Serviços HTTP (S) usando a mesma Interface IP + Porta.
Observe que a diretiva listen 10.10.10.40:443
é usada várias vezes na configuração do nginx. Isso é possível porque o nginx apenas se vincula a esse IP uma vez e, em seguida, analisa o cabeçalho Host
nas solicitações dos clientes para determinar qual server
(vhost) atenderá a essa solicitação.
Minha configuração usa IPs estáticos na definição upstream
, mas você também pode usar nomes de host de contêineres, apenas certifique-se de que eles sejam conhecidos de antemão (definidos no docker-compose, consulte link ) e resolvível pelo nginx.
E, por último, não mapeie os Portos dos contêineres / serviços para as Portas do Host! Eles não precisam estar disponíveis para o mundo exterior, apenas o nginx precisa acessá-los.