Configurar um ambiente de produção robusto exige não apenas a conteinerização da aplicação, mas também a gestão eficiente de certificados SSL e um servidor de borda para proxy reverso. Neste guia, exploraremos como orquestrar o Nginx e o .NET Core no Docker, integrando o Certbot para garantir que os certificados Let's Encrypt sejam renovados automaticamente sem intervenção manual.
Arquitetura da Solução
A estratégia consiste em separar as responsabilidades em dois serviços principais:
- Gateway (Nginx + Certbot): Responsável por receber o tráfego HTTPS, gerenciar os certificados e encaminhar as requisições.
- Aplicação (ASP.NET Core): Prcoessa a lógica de negócio via HTTP puro, confiando nos cabeçalhos encaminhados pelo proxy.
Preparação do Container Nginx com Certbot
Em vez de usar uma imagem padrão, criaremos uma imagem personalizada baseada no Alpine Linux para incluir as ferramentas de automação do SSL.
# Dockerfile-gateway
FROM nginx:alpine
# Instalação do Certbot e dependências de rede
RUN apk update && \
apk add --no-cache certbot certbot-nginx
# Criação de diretórios para persistência
RUN mkdir -p /etc/letsencrypt
# Cópia da configuração inicial
COPY gateway.conf /etc/nginx/nginx.conf
Configuração do Proxy Reverso
O arquivo nginx.conf inicial deve permitir que o Certbot valide o domínio através do desafio HTTP-01 (porta 80). Substitua meudominio.com pelo seu domínio real.
user nginx;
worker_processes auto;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
server {
listen 80;
server_name meudominio.com;
location / {
root /usr/share/nginx/html;
index index.html;
}
# Localização necessária para o desafio do Certbot
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
}
}
Ajustes na Aplicação .NET Core
Para que a aplicação identifique corretamente o protocolo HTTPS original e o IP do cliente, é essencial configurar o middleware de cabeçalhos encaminhados no Startup.cs ou Program.cs.
// Configuração do middleware para processar headers de proxy
var optionsHeaders = new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
};
// Importante: Limpar as redes conhecidas se o proxy estiver no mesmo Docker Bridge
optionsHeaders.KnownNetworks.Clear();
optionsHeaders.KnownProxies.Clear();
app.UseForwardedHeaders(optionsHeaders);
Orquestração com Docker Compose
O uso do Docker Compose facilita a criação de redes isoladas e a persistência de volumes, o que é crítico para manter os certificados após o reinício dos containers.
version: '3.8'
services:
web-api:
image: minha-app-dotnet:latest
container_name: backend_app
networks:
- malha_interna
gateway:
build:
context: .
dockerfile: Dockerfile-gateway
container_name: nginx_proxy
ports:
- "80:80"
- "443:443"
volumes:
- ./certs:/etc/letsencrypt
- ./conf/gateway.conf:/etc/nginx/nginx.conf
- ./logs:/var/log/nginx
environment:
- DOMAIN_NAME=meudominio.com
command: >
sh -c "echo '0 0 1 * * certbot renew --quiet && nginx -s reload' > /etc/periodic/monthly/renew &&
crond &&
nginx -g 'daemon off;'"
depends_on:
- web-api
networks:
- malha_interna
networks:
malha_interna:
driver: bridge
Emissão Inicial do Certificado
Com os containers em execução, execute o comando interativo para gerar o certificado pela primeira vez:
docker exec -it nginx_proxy certbot --nginx -d meudominio.com
Siga as instruções no terminal. O Certbot modificará automaticamente o seu arquivo de configuração mapeado no volume para incluir as diretivas SSL.
Considerações de Conectividade
Dentro da rede do Docker, o Nginx deve referenciar o serviço .NET pelo nome definido no docker-compose.yml. No arquivo de configuração do Nginx, o proxy_pass deve ser ajustado:
location /api {
proxy_pass http://web-api:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
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;
}
Solução de Problemas Comuns
Erro 502 Bad Gateway: Geralmente ocorre quando o container do Nginx tenta acessar o backend via localhost. Lembre-se que cada container tem seu próprio loopback. Use o nome do serviço definido no Compose.
Certificado expirado mesmo após renovação: O Nginx mantém o certificado antigo na memória. O comando de renovação deve obrigatoriamente incluir um nginx -s reload ou o container deve ser reiniciado.
Persistência de Dados: Certifique-se de que a pasta /etc/letsencrypt está mapeada para um volume externo. Caso contrário, toda vez que o container to recriado, você atingirá o limite de requisições do Let's Encrypt ao tentar gerar novos certificados.