Configurando HTTPS com Let's Encrypt e Certbot

1. Introdução ao HTTPS e Let's Encrypt no Contexto DevOps

Em ambientes modernos de DevOps, a segurança das comunicações não é opcional — é requisito fundamental. HTTPS garante que os dados trafeguem criptografados entre cliente e servidor, protegendo contra ataques man-in-the-middle, garantindo integridade das informações e aumentando a confiança dos usuários. Além disso, mecanismos de busca como Google priorizam sites com HTTPS, impactando diretamente no SEO.

O Let's Encrypt revolucionou a obtenção de certificados SSL/TLS ao oferecer certificados gratuitos e automatizados através do protocolo ACME (Automated Certificate Management Environment). O ACME permite que servidores obtenham e renovem certificados sem intervenção manual, eliminando o custo e a complexidade dos certificados tradicionais.

O Certbot é a ferramenta oficial da Electronic Frontier Foundation (EFF) para interagir com o Let's Encrypt. Em ambientes containerizados com Docker e Kubernetes, o Certbot pode ser executado como container temporário ou integrado a serviços existentes, permitindo automação completa do ciclo de vida dos certificados.

2. Pré-requisitos e Configuração do Ambiente

Antes de iniciar, certifique-se de ter:
- Um domínio válido (ex: meudominio.com)
- Registro DNS A/AAAA apontando para o IP do servidor
- Docker e Docker Compose instalados
- Portas 80 e 443 abertas no firewall

Vamos criar um ambiente básico com Nginx como reverse proxy usando Docker Compose:

# docker-compose.yml
version: '3.8'

services:
  nginx:
    image: nginx:alpine
    container_name: nginx-proxy
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./data/certbot/conf:/etc/letsencrypt
      - ./data/certbot/www:/var/www/certbot
    networks:
      - web-network

networks:
  web-network:
    driver: bridge

Configuração inicial do Nginx:

# nginx.conf
events {
    worker_connections 1024;
}

http {
    server {
        listen 80;
        server_name meudominio.com www.meudominio.com;

        location /.well-known/acme-challenge/ {
            root /var/www/certbot;
        }

        location / {
            return 301 https://$host$request_uri;
        }
    }
}

3. Obtendo Certificados com Certbot (Modo Standalone e Webroot)

Modo Standalone

O modo standalone é útil quando o Nginx não está rodando ou você quer obter certificados de forma isolada:

docker run -it --rm \
  -v ./data/certbot/conf:/etc/letsencrypt \
  -v ./data/certbot/www:/var/www/certbot \
  -p 80:80 \
  certbot/certbot certonly --standalone \
  -d meudominio.com -d www.meudominio.com \
  --email seuemail@exemplo.com \
  --agree-tos --no-eff-email

Modo Webroot

O modo webroot é mais adequado quando o Nginx já está em execução, pois o Certbot utiliza o diretório exposto pelo servidor para validação:

docker run -it --rm \
  -v ./data/certbot/conf:/etc/letsencrypt \
  -v ./data/certbot/www:/var/www/certbot \
  certbot/certbot certonly --webroot \
  -w /var/www/certbot \
  -d meudominio.com -d www.meudominio.com \
  --email seuemail@exemplo.com \
  --agree-tos --no-eff-email

Após a execução bem-sucedida, os certificados estarão disponíveis em ./data/certbot/conf/live/meudominio.com/.

4. Configuração Automática do Nginx com Certificado HTTPS

Com os certificados obtidos, atualizamos o Nginx para servir HTTPS:

# nginx.conf (versão completa)
events {
    worker_connections 1024;
}

http {
    # Redirecionamento HTTP -> HTTPS
    server {
        listen 80;
        server_name meudominio.com www.meudominio.com;

        location /.well-known/acme-challenge/ {
            root /var/www/certbot;
        }

        location / {
            return 301 https://$host$request_uri;
        }
    }

    # Servidor HTTPS
    server {
        listen 443 ssl http2;
        server_name meudominio.com www.meudominio.com;

        ssl_certificate /etc/letsencrypt/live/meudominio.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/meudominio.com/privkey.pem;

        # Configurações de segurança SSL
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers on;

        location / {
            root /usr/share/nginx/html;
            index index.html;
        }
    }
}

Teste a renovação manual para verificar se tudo está funcionando:

docker run --rm \
  -v ./data/certbot/conf:/etc/letsencrypt \
  -v ./data/certbot/www:/var/www/certbot \
  certbot/certbot renew --dry-run

5. Automação da Renovação de Certificados com Docker e Cron

Crie um script de renovação que também recarrega o Nginx:

#!/bin/bash
# renew-certs.sh

docker run --rm \
  -v $(pwd)/data/certbot/conf:/etc/letsencrypt \
  -v $(pwd)/data/certbot/www:/var/www/certbot \
  certbot/certbot renew --quiet

docker exec nginx-proxy nginx -s reload

Adicione ao crontab do host para executar diariamente:

0 3 * * * /caminho/para/renew-certs.sh >> /var/log/certbot-renew.log 2>&1

Docker Compose completo com serviço de renovação:

# docker-compose.yml (completo)
version: '3.8'

services:
  nginx:
    image: nginx:alpine
    container_name: nginx-proxy
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./data/certbot/conf:/etc/letsencrypt
      - ./data/certbot/www:/var/www/certbot
    networks:
      - web-network

  certbot:
    image: certbot/certbot
    container_name: certbot-renew
    volumes:
      - ./data/certbot/conf:/etc/letsencrypt
      - ./data/certbot/www:/var/www/certbot
    entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew --quiet; sleep 12h & wait $${!}; done;'"
    networks:
      - web-network

networks:
  web-network:
    driver: bridge

6. Integração com Kubernetes (Ingress e Cert-Manager)

No Kubernetes, a abordagem recomendada é utilizar o cert-manager, que automatiza completamente a obtenção e renovação de certificados:

# cluster-issuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: seuemail@exemplo.com
    privateKeySecretRef:
      name: letsencrypt-prod-key
    solvers:
    - http01:
        ingress:
          class: nginx
# certificate.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: meudominio-tls
  namespace: default
spec:
  secretName: meudominio-tls
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  dnsNames:
  - meudominio.com
  - www.meudominio.com
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: meudominio-ingress
  annotations:
    kubernetes.io/ingress.class: nginx
    cert-manager.io/cluster-issuer: letsencrypt-prod
    kubernetes.io/tls-acme: "true"
spec:
  tls:
  - hosts:
    - meudominio.com
    - www.meudominio.com
    secretName: meudominio-tls
  rules:
  - host: meudominio.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: meu-servico
            port:
              number: 80
  - host: www.meudominio.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: meu-servico
            port:
              number: 80

7. Boas Práticas e Troubleshooting

Monitoramento

Configure alertas para certificados prestes a expirar usando Prometheus com o blackbox-exporter ou ferramentas como cert-manager-issuer-metrics:

# Exemplo de regra Prometheus para alerta
- alert: CertificateExpiringSoon
  expr: certmanager_certificate_expiration_timestamp_seconds - time() < 604800
  for: 1h
  labels:
    severity: warning
  annotations:
    summary: "Certificado {{ $labels.name }} expira em menos de 7 dias"

Troubleshooting Comum

  • Rate limits: Let's Encrypt tem limite de 50 certificados por domínio por semana. Use staging (https://acme-staging-v02.api.letsencrypt.org/directory) para testes.
  • Validação de domínio: Verifique se o diretório .well-known/acme-challenge/ está acessível publicamente.
  • Permissões: Arquivos de chave privada (privkey.pem) devem ter permissão 600 e pertencer ao usuário do Nginx.

Segurança

  • Nunca compartilhe chaves privadas
  • Use chmod 600 nos arquivos de chave
  • Em clusters Kubernetes, utilize Secrets para armazenar certificados
  • Considere usar ssl_stapling no Nginx para melhor performance

Referências