Post

Postfix em Docker: Criando um Servidor de Relay SMTP Seguro para Aplicações Internas

Guia completo para configurar um servidor Mail Relay com Postfix em Docker, funcionando como smarthost seguro para o Gmail. Aprenda a criar uma rede isolada, configurar autenticação SASL, implementar hardening de TLS (sem TLS internamente, TLS forçado externamente), integrar com aplicações como Passbolt e monitorar a fila de emails com comandos essenciais de troubleshooting.

Postfix em Docker: Criando um Servidor de Relay SMTP Seguro para Aplicações Internas

Este tutorial detalha a configuração de um Mail Relay interno utilizando o Postfix em um container Docker. O objetivo é criar um servidor SMTP simples (Smarthost) que aceita e-mails de aplicações internas (como o Passbolt) e os encaminha para um serviço de e-mail externo (Gmail) de forma segura.

Sumário


Arquitetura do Sistema

1
2
3
4
5
6
7
8
9
┌─────────────────┐     ┌─────────────────────┐     ┌──────────────────┐
│   Passbolt      │     │   Postfix Relay     │     │     Gmail        │
│   Container     │────▶│   Container         │────▶│   Smarthost      │
│   172.16.254.2  │     │   172.16.254.254    │     │  smtp.gmail.com  │
│                 │     │                     │     │                  │
│ - SMTP:25 (não  │     │ - Recebe:25 (sem    │     │ - TLS obrigatório│
│   criptografado)│     │   TLS)              │     │ - Porta 587      │
│ - Rede: smtp-net│     │ - Envia:587 (TLS)   │     │ - Autenticação   │
└─────────────────┘     └─────────────────────┘     └──────────────────┘

Pré-requisitos

Antes de iniciar, certifique-se de ter as seguintes ferramentas instaladas:

  • Docker & Docker Compose: Para orquestração dos containers.
  • mkcert: Para geração de certificados SSL locais (necessário para o Passbolt).
  • Telnet ou Netcat: Para testes de conectividade SMTP.
  • Conta Gmail: Com Autenticação de Dois Fatores (2FA) ativa para gerar a “Senha de App”.

1. Configuração do Postfix Mail Relay

Nesta seção, configuraremos o serviço de Postfix para atuar como um relay, utilizando o Gmail como servidor de envio (Smarthost).

1.1. Criação da Rede Docker Externa

É uma boa prática isolar o serviço de e-mail em uma rede dedicada. Usaremos uma rede externa com sub-rede e IP fixo para facilitar a comunicação com outros projetos e garantir que o endereço do relay seja previsível.

1
2
3
4
5
6
7
# Cria a rede Docker 'smtp-net' com uma sub-rede específica.
docker network create \
  --driver=bridge \
  --subnet=172.16.254.0/24 \
  --gateway=172.16.254.1 \
  --ip-range=172.16.254.0/25 \
  smtp-net

Nota: O uso de IP fixo (172.16.254.254) é intencional para facilitar a conexão de aplicações de outros projetos sem a necessidade de configurar um servidor DNS interno neste laboratório. Em ambientes de produção, a prática recomendada é utilizar um servidor DNS (como o Bind ou CoreDNS) ou o serviço de Service Discovery do Docker para permitir a comunicação via nomes de host (FQDN), o que torna a infraestrutura mais flexível e fácil de manter.

1.2. Estrutura de Diretórios e Arquivos

Crie o diretório do projeto e navegue até ele:

1
2
mkdir postfix-relay
cd postfix-relay

1.3. Definição do docker-compose.yml

Crie o arquivo docker-compose.yml para definir o serviço Postfix.

1
vim docker-compose.yml 

Conteúdo do docker-compose.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# docker-compose.yml para o Postfix Mail Relay
services:
  postfix:
    image: boky/postfix
    container_name: postfix-relay
    # Define o FQDN (Fully Qualified Domain Name) do servidor de e-mail
    hostname: mail.lab4it.internal
    domainname: lab4it.internal
    restart: unless-stopped

    environment:
      # ===============================
      # Identidade do Servidor
      # ===============================
      MYHOSTNAME: mail.lab4it.internal
      MYDOMAIN: lab4it.internal
      # Permite que apenas remetentes deste domínio usem o relay
      ALLOWED_SENDER_DOMAINS: lab4it.internal

      # ===============================
      # Redes Permitidas (Relay Interno)
      # Define quais redes podem enviar e-mails através deste relay
      # O 172.16.254.0/24 é a sub-rede da rede 'smtp-net'
      # ===============================
      POSTFIX_mynetworks: 172.16.254.0/24

      # ===============================
      # Relay Externo (Smarthost) - Configuração para Gmail
      # As credenciais são lidas do arquivo .env
      # ===============================
      RELAYHOST: ${RELAY_HOST}
      RELAYHOST_USERNAME: ${RELAY_USER}
      RELAYHOST_PASSWORD: ${RELAY_PASSWORD}

      # ===============================
      # SMTPD (RECEPÇÃO) — Desabilitando TLS para comunicação interna
      # Como o tráfego é interno (container -> container), o TLS é desabilitado
      # ===============================
      POSTFIX_smtpd_tls_security_level: none

      # ===============================
      # SMTP (ENVIO) — Configuração TLS para o Gmail
      # O Postfix usará TLS para se conectar ao Gmail (porta 587)
      # ===============================
      POSTFIX_smtp_tls_security_level: encrypt
      POSTFIX_smtp_sasl_auth_enable: "yes"
      POSTFIX_smtp_sasl_security_options: noanonymous
      POSTFIX_smtp_sasl_mechanism_filter: plain,login
      POSTFIX_smtp_tls_CAfile: /etc/ssl/certs/ca-certificates.crt

      # ===============================
      # Hardening Básico (Proteção contra spam e abuso)
      # ===============================
      POSTFIX_smtpd_helo_required: "yes"
      POSTFIX_smtpd_delay_reject: "yes"
      POSTFIX_message_size_limit: "26214400"
    
    # Persistência da fila de e-mails
    # Garante que e-mails temporariamente não entregues sobrevivam a reinicializações
    volumes:
      - postfix_queue:/var/spool/postfix

    ports:
      # Mapeia a porta 2525 do host para a porta 25 do container (SMTP)
      # A porta 2525 é usada para evitar conflitos com a porta 25 padrão do host
      - "2525:25"

    networks:
      smtp-net:
        # Atribui o IP fixo ao container dentro da rede 'smtp-net'
        ipv4_address: 172.16.254.254

networks:
  smtp-net:
    external: true

volumes:
  postfix_queue:
    name: postfix-relay-queue

1.4. Arquivo de Variáveis de Ambiente (.env)

Crie o arquivo .env para armazenar as credenciais de forma segura.

1
vim .env

Conteúdo do .env:

1
2
3
4
5
6
# .env

# Credenciais do Relay (Gmail Smarthost)
RELAY_HOST=[smtp.gmail.com]:587
RELAY_USER=<SEU-EMAIL@gmail.com>
RELAY_PASSWORD=<SUA-SENHA-DE-APP>

IMPORTANTE: Para usar o Gmail como relay, você deve gerar uma Senha de App (App Password) nas configurações de segurança da sua conta Google, caso a autenticação de dois fatores (2FA) esteja ativada. O uso da senha principal da conta não é mais permitido.

1.5. Inicialização e Verificação

Suba o container e verifique os logs para garantir que o Postfix iniciou corretamente.

1
docker compose up -d
1
2
3
4
5
# Verificar logs para diagnosticar problemas
docker compose logs -f

# Verificar a configuração final do Postfix dentro do container
docker compose exec postfix postconf -n

1.6. Teste de Envio Manual (Telnet)

Use o telnet para simular o envio de um e-mail diretamente para o relay.

1
2
# Conecte-se ao servidor Postfix na porta mapeada (2525)
telnet localhost 2525

Comandos a serem digitados no telnet:

1
2
3
4
5
6
7
mail from:<passbolt@lab4it.internal>
rcpt to:<ola@lab4it.io>
data
subject: Teste de E-mail via Postfix Relay
corpo do email.
.
quit

Exemplo de sessão bem-sucedida:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
telnet localhost 2525
Trying ::1...
Connected to localhost.
Escape character is '^]'.
220 mail.lab4it.internal ESMTP Postfix (Debian)
ehlo teste
250-mail.lab4it.internal
# ... (outras linhas de resposta)
250 CHUNKING
mail from:<passbolt@lab4it.internal>
250 2.1.0 Ok
rcpt to:<alo@lab4it.io>
250 2.1.5 Ok
data
354 End data with <CR><LF>.<CR><LF>
subject: Teste de E-mail via Postfix Relay
corpo do email.
.
250 2.0.0 Ok: queued as [ID_DA_MENSAGEM]
quit
221 2.0.0 Bye
Connection closed by foreign host.

2. Monitoramento e Troubleshooting

Para garantir a saúde do seu servidor de e-mail, utilize os comandos abaixo para gerenciar a fila e diagnosticar problemas.

2.1. Comandos Essenciais de Fila

1
2
3
4
5
6
7
8
9
10
11
# Visualizar e-mails na fila (pendentes ou com erro)
docker compose exec postfix postqueue -p

# Tentar forçar o reenvio de todos os e-mails na fila
docker compose exec postfix postqueue -f

# Excluir todos os e-mails da fila (Cuidado!)
docker compose exec postfix postsuper -d ALL

# Ver estatísticas da fila
docker compose exec postfix qshape

2.2. Diagnóstico em Tempo Real

2.3. FAQ: Problemas Comuns e Soluções

P: Meus e-mails estão presos na fila com o erro “Authentication Required” ou “SASL authentication failed”. O que fazer?

R: Este é o erro mais comum e quase sempre está relacionado às credenciais do Gmail. Verifique os seguintes pontos:

  1. Senha de App: Você está usando uma Senha de App gerada na sua conta Google, e não a sua senha principal? Desde que o 2FA foi implementado, o Google exige senhas de app para autenticação em clientes de terceiros como o Postfix.
  2. Credenciais no .env: Verifique se não há erros de digitação no seu e-mail (RELAY_USER) ou na senha de app (RELAY_PASSWORD) dentro do arquivo .env.
  3. Recarregar o Container: Após qualquer alteração no arquivo .env, você precisa recriar o container para que ele leia as novas variáveis de ambiente. Execute docker compose down && docker compose up -d.

P: Vejo um erro de “TLS negotiation failed” ou “Connection timed out” nos logs ao tentar enviar para o Gmail.

R: Isso pode indicar um problema de conectividade de rede ou de configuração TLS.

  1. Conectividade: Verifique se o seu container consegue resolver o domínio smtp.gmail.com e se conectar à porta 587. Você pode testar isso de dentro do container:
    1
    
    docker compose exec postfix telnet smtp.gmail.com 587
    

    Você deve ver uma resposta como 220 smtp.gmail.com ESMTP .... Se não conseguir, pode haver um problema de DNS ou de firewall na sua rede.

  2. Configuração TLS: Confirme se a diretiva POSTFIX_smtp_tls_security_level está definida como encrypt no seu docker-compose.yml.

P: Enviei um e-mail de teste, mas ele não chegou e não está na fila. O que aconteceu?

R: Se o e-mail não está na fila (postqueue -p não mostra nada), significa que o Postfix acredita que o entregou com sucesso ao Gmail.

  1. Verifique os Logs: Use docker compose logs postfix e procure pela linha que contém status=sent. Isso confirma que o Gmail aceitou o e-mail.
  2. Caixa de Spam: Verifique a caixa de spam do destinatário. Se o e-mail estiver lá, é um problema de entregabilidade (reputação do remetente), que pode ser melhorado com a configuração de SPF, DKIM e DMARC (Seção 3).
  3. Políticas do Gmail: O Gmail pode silenciosamente descartar e-mails que considera suspeitos, mesmo após aceitá-los. Certifique-se de que o endereço de remetente (mail from) não é falso ou enganoso.

P: Como posso ver o motivo exato pelo qual um e-mail específico falhou?

R: Primeiro, liste a fila com postqueue -p para obter o ID da mensagem. Em seguida, use o postcat para ver o conteúdo e os metadados da mensagem, incluindo a mensagem de erro do servidor de destino.

1
2
# Substitua [ID_DA_MENSAGEM] pelo ID real
docker compose exec postfix postcat -q [ID_DA_MENSAGEM]

3. Entregabilidade e Segurança DNS

Se você estiver usando um domínio real (não apenas .internal), é crucial configurar registros DNS para evitar que seus e-mails caiam na caixa de spam. Como nosso relay utiliza o Gmail para o envio final, a configuração de SPF, DKIM e DMARC deve ser feita para autorizar o Google a enviar e-mails em nome do seu domínio.

3.1. Registros DNS Essenciais

RegistroDescriçãoExemplo de Valor
SPF (Sender Policy Framework)Autoriza os servidores do Google a enviarem e-mails em nome do seu domínio. Essencialmente, você está dizendo: “Se um e-mail vier do Google, mas disser que é do meu domínio, confie nele”.v=spf1 include:_spf.google.com ~all
DKIM (DomainKeys Identified Mail)Adiciona uma assinatura digital a cada e-mail enviado, garantindo que a mensagem não foi alterada em trânsito. A chave pública é publicada no seu DNS, e o Google assina o e-mail com a chave privada.A configuração é feita no painel do Google Workspace/Gmail, que fornecerá um registro TXT para ser adicionado ao seu DNS.
DMARC (Domain-based Message Authentication, Reporting, and Conformance)Define uma política sobre o que fazer se um e-mail falhar nas verificações de SPF ou DKIM (rejeitar, colocar em quarentena ou não fazer nada). Também permite receber relatórios sobre falhas.v=DMARC1; p=quarantine; rua=mailto:dmarc-reports@seu-dominio.com

3.2. Como Configurar os Registros DNS

  1. Acesse o Painel de Controle do seu Provedor de DNS: Pode ser Cloudflare, GoDaddy, Namecheap, Route 53 (AWS), etc.
  2. Crie um Registro TXT para o SPF:
    • Host/Nome: @ (representa a raiz do seu domínio)
    • Valor/Conteúdo: v=spf1 include:_spf.google.com ~all
  3. Configure o DKIM no Google:
    • Vá até o painel de administração do Google Workspace, na seção de autenticação de e-mail do Gmail.
    • Gere uma nova chave DKIM. O Google fornecerá um nome de host para o registro TXT (algo como google._domainkey) e um valor longo.
    • Crie um novo registro TXT no seu provedor de DNS com esses valores.
  4. Crie um Registro TXT para o DMARC:
    • Host/Nome: _dmarc
    • Valor/Conteúdo: v=DMARC1; p=quarantine; (comece com p=none para monitorar antes de aplicar uma política restritiva).

3.3. Alternativas de Smarthost

Embora o Gmail seja uma excelente opção para pequenos volumes e testes, para aplicações maiores ou com requisitos específicos, considere usar um serviço de e-mail transacional dedicado. A configuração no Postfix é muito similar, bastando alterar as variáveis no arquivo .env.

ServiçoVantagensExemplo de RELAY_HOST
Amazon SESAltamente escalável, custo-benefício excelente, integração com o ecossistema AWS.[email-smtp.us-east-1.amazonaws.com]:587
SendGridFoco em entregabilidade, APIs robustas e análise detalhada.[smtp.sendgrid.net]:587
MailgunFerramentas poderosas para desenvolvedores, validação de e-mail e parsing de entrada.[smtp.mailgun.org]:587

4. Integração com Aplicação Externa (Passbolt)

Esta seção demonstra como configurar uma aplicação (Passbolt) para utilizar o Mail Relay recém-criado.

4.1. Estrutura do Projeto Passbolt

Crie o diretório para o Passbolt e navegue até ele:

1
2
mkdir passbolt
cd passbolt

4.2. Geração de Certificados SSL (mkcert)

O Passbolt exige HTTPS. Usaremos o mkcert para gerar certificados confiáveis para o ambiente de desenvolvimento/laboratório.

1
2
3
4
5
6
7
8
mkdir certs && cd certs 
# Instala a CA local (se ainda não estiver instalada)
mkcert -install 
# Gera o certificado para o domínio interno
mkcert passbolt.lab4it.internal
# Renomeia os arquivos para o padrão esperado pelo docker-compose
mv passbolt.lab4it.internal.pem cert.pem 
mv passbolt.lab4it.internal-key.pem key.pem

4.3. Definição do docker-compose.yml do Passbolt

Retorne ao diretório principal do Passbolt e crie o arquivo docker-compose.yml.

1
2
cd ..
vim docker-compose.yml 

Conteúdo do docker-compose.yml (Apenas a seção relevante para o SMTP):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# docker-compose.yml para Passbolt com Mail Relay
services:
  db:
    image: mariadb:10.11
    container_name: passbolt-db
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
      MYSQL_DATABASE: ${DB_DATABASE}
      MYSQL_USER: ${DB_USERNAME}
      MYSQL_PASSWORD: ${DB_PASSWORD}
    volumes:
      - db_data:/var/lib/mysql
    command:
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_unicode_ci
      - --skip-name-resolve
      - --log_bin_trust_function_creators=1
    networks:
      - passbolt-net
    healthcheck:
      test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s

  passbolt:
    image: passbolt/passbolt:5.8.0-1-ce
    container_name: passbolt-app
    restart: unless-stopped
    depends_on:
      db:
        condition: service_healthy
    environment:
      # Configurações da Aplicação
      APP_FULL_BASE_URL: ${APP_URL}
      
      # Configurações do Banco de Dados
      DATASOURCES_DEFAULT_HOST: "db"
      DATASOURCES_DEFAULT_USERNAME: ${DB_USERNAME}
      DATASOURCES_DEFAULT_PASSWORD: ${DB_PASSWORD}
      DATASOURCES_DEFAULT_DATABASE: ${DB_DATABASE}
      
      # Configuração SMTP para usar o Mail Relay
      # O host é o IP fixo do container Postfix na rede 'smtp-net'
      # A porta é a porta 25 interna do Postfix (não a 2525 mapeada no host)
      # O TLS é desabilitado (conforme configurado no Postfix)
      EMAIL_TRANSPORT_DEFAULT_HOST: "172.16.254.254"
      EMAIL_TRANSPORT_DEFAULT_PORT: 25
      EMAIL_DEFAULT_FROM: "passbolt@lab4it.internal"
      
    volumes:
      - gpg_data:/etc/passbolt/gpg
      - jwt_data:/etc/passbolt/jwt
      # Mapeamento dos certificados SSL gerados pelo mkcert
      - ./certs/cert.pem:/etc/ssl/certs/certificate.crt:ro
      - ./certs/key.pem:/etc/ssl/certs/certificate.key:ro
    networks:
      - passbolt-net
      - smtp-net # Conecta o Passbolt à rede do Mail Relay
    command:
      - "/usr/bin/wait-for.sh"
      - "-t"
      - "120"
      - "db:3306"
      - "--"
      - "/docker-entrypoint.sh"
    ports:
      - 8080:80
      - 8443:443

networks:
  passbolt-net:
    driver: bridge
  smtp-net:
    external: true # Indica que esta rede foi criada previamente

volumes:
  db_data:
    name: passbolt-db
  gpg_data:
    name: passbolt-gpg
  jwt_data:
    name: passbolt-jwt

4.4. Arquivo de Variáveis de Ambiente do Passbolt (.env)

Crie o arquivo .env para as configurações do Passbolt e do banco de dados.

1
vim .env 

Conteúdo do .env:

1
2
3
4
5
6
7
8
9
10
11
12
13
# Arquivo: .env
# ============================================
# CONFIGURAÇÕES DE BANCO DE DADOS
# ============================================
DB_ROOT_PASSWORD=<SENHA-ROOT-DB>
DB_PASSWORD=<SENHA-PASSBOLT-DB>
DB_USERNAME=passbolt
DB_DATABASE=passbolt

# ============================================
# CONFIGURAÇÕES DO PASSBOLT
# ============================================
APP_URL=https://passbolt.lab4it.internal:8443

4.5. Inicialização e Configuração Final

Suba os containers do Passbolt e do MariaDB.

1
2
3
4
docker compose up -d
# Acompanhe os logs para garantir que a aplicação subiu corretamente
docker compose logs -f 
docker compose logs -f passbolt

4.6. Criação do Primeiro Usuário Administrador

Após a inicialização do Passbolt, execute o comando para criar o primeiro usuário administrador.

1
2
3
4
docker compose exec passbolt \
su -m -c "/usr/share/php/passbolt/bin/cake \
passbolt register_user -u <SEU-E-MAIL> \
-f 'NOME' -l 'SOBRENOME' -r admin" -s /bin/sh www-data

Passo Final: Para acessar o Passbolt via navegador, você deve adicionar o mapeamento do domínio interno (passbolt.lab4it.internal) para o IP do seu host no arquivo de hosts do seu sistema operacional.

  • Linux: /etc/hosts
  • Windows: C:\Windows\System32\drivers\etc\hosts

Exemplo de linha a ser adicionada: 127.0.0.1 passbolt.lab4it.internal


5. Considerações para Ambientes de Produção

Este tutorial foi projetado para ambientes de desenvolvimento e laboratório. Se você planeja adaptar esta solução para um ambiente de produção, considere os seguintes pontos:

Alta Disponibilidade e Redundância: Um único container de Postfix é um ponto único de falha. Para ambientes críticos, considere executar múltiplas instâncias do relay atrás de um load balancer (como HAProxy ou NGINX) ou utilizar um orquestrador como Kubernetes com múltiplas réplicas do pod do Postfix.

Monitoramento e Alertas: Implemente monitoramento ativo da saúde do Postfix e da fila de e-mails. Ferramentas como Prometheus (para coletar métricas do Postfix via exporters) e Grafana (para visualização) podem ser integradas. Configure alertas para situações como:

  • Fila de e-mails crescendo acima de um limite.
  • Falhas de autenticação com o smarthost.
  • Aumento na taxa de e-mails rejeitados.

Backup da Fila: A fila do Postfix (/var/spool/postfix) contém e-mails que ainda não foram entregues. Certifique-se de que o volume Docker postfix_queue seja incluído em sua estratégia de backup regular para evitar perda de mensagens em caso de falha de hardware.

Rotação de Logs: Os logs do Postfix podem crescer rapidamente em ambientes com alto volume de tráfego. Configure a rotação de logs com ferramentas como logrotate ou utilize um sistema de agregação de logs centralizado (ex: ELK Stack, Loki) para gerenciar e analisar os logs.

Rate Limiting e Quotas: Se o seu relay for acessível por múltiplas aplicações, considere implementar limites de taxa de envio por remetente ou por rede para prevenir abuso. Isso pode ser feito com as diretivas smtpd_client_connection_rate_limit e smtpd_recipient_limit do Postfix.

Segurança de Rede: Mesmo que o TLS esteja desabilitado para tráfego interno, considere usar uma VPN ou uma rede privada virtual (como AWS VPC) para isolar completamente o relay de redes não confiáveis. Nunca exponha a porta 25 do Postfix diretamente à internet sem autenticação e criptografia adequadas.


6. Referências e Documentação

This post is licensed under CC BY 4.0 by the author.