Motivação

Manter um servidor de e-mail próprio vai muito além de simplesmente receber e enviar mensagens. Com o aumento de ataques de downgrade de TLS, spoofing e interceptação de tráfego SMTP, a adoção de padrões modernos como DANE, DNSSEC e TLSA deixou de ser opcional para quem leva segurança a sério.

O objetivo deste post é documentar o processo de hardening de um servidor de e-mail rodando Postfix 3.11 no FreeBSD, até atingir 100% no Internet.nl — com entrada no Hall of Fame.


Pré-requisitos

  • Postfix 3.6+ (usamos 3.11)
  • DNSSEC ativo no domínio (via Cloudflare, neste caso)
  • Certificado EC-256 emitido via acme.sh
  • OpenSSL 3.x

1. Certificado EC-256 com acme.sh

Certificados ECDSA (EC-256) são mais eficientes e seguros que RSA 2048. Para emitir via acme.sh:

acme.sh --issue --dns dns_cf -d mail.domain.tld --keylength ec-256
acme.sh --install-cert -d mail.domain.tld --ecc \
  --cert-file /usr/local/etc/postfix/tls/cert.pem \
  --key-file /usr/local/etc/postfix/tls/key.pem \
  --fullchain-file /usr/local/etc/postfix/tls/fullchain.pem \
  --reloadcmd "service postfix reload"

Atenção: O acme.sh cria o diretório com sufixo _ecc para certificados EC. Verifique o caminho correto em ~/.acme.sh/mail.domain.tld_ecc/.


2. Configuração do Postfix (main.cf)

TLS para recebimento (smtpd)

# Certificado
smtpd_tls_cert_file = /usr/local/etc/postfix/tls/fullchain.pem
smtpd_tls_key_file = /usr/local/etc/postfix/tls/key.pem

# Protocolos — apenas TLS 1.3 para conexões obrigatórias
smtpd_tls_mandatory_protocols = >=TLSv1.3
smtpd_tls_protocols = >=TLSv1.2

# Ciphers
smtpd_tls_mandatory_ciphers = high
smtpd_tls_ciphers = high
smtpd_tls_exclude_ciphers = aNULL, CAMELLIA, SHA, ECDHE-ECDSA-AES256-CCM8, ECDHE-ECDSA-AES128-CCM8
smtpd_tls_mandatory_exclude_ciphers = aNULL, CAMELLIA, SHA, ECDHE-ECDSA-AES256-CCM8, ECDHE-ECDSA-AES128-CCM8

# Segurança
smtpd_tls_security_level = may
smtpd_tls_loglevel = 1

Nota: smtpd_tls_prefer_server_ciphers não existe no Postfix 3.6+. O parâmetro smtpd_tls_fingerprint_digest já é sha256 por padrão a partir da versão 3.6.

TLS para envio (smtp) com DANE

# Protocolos
smtp_tls_mandatory_protocols = >=TLSv1.2
smtp_tls_protocols = >=TLSv1.2

# Ciphers
smtp_tls_mandatory_ciphers = high
smtp_tls_ciphers = high
smtp_tls_exclude_ciphers = aNULL, CAMELLIA, SHA, DHE

# DANE — valida o certificado do servidor remoto via TLSA + DNSSEC
smtp_tls_security_level = dane
smtp_dns_support_level = dnssec
smtp_tls_loglevel = 1

3. DANE e TLSA

O que é DANE?

DANE (DNS-Based Authentication of Named Entities) permite publicar no DNS informações sobre o certificado TLS do servidor, protegidas por DNSSEC. Isso impede ataques de downgrade onde um atacante remove o STARTTLS da negociação.

Gerando o registro TLSA

O tipo 3 1 1 (DANE-EE, SPKI, SHA-256) é o mais recomendado: vincula o hash da chave pública do certificado, e não muda enquanto a chave privada for a mesma — mesmo após renovações.

openssl x509 -in /usr/local/etc/postfix/tls/fullchain.pem \
  -pubkey -noout | \
  openssl pkey -pubin -outform DER | \
  openssl dgst -sha256 -binary | \
  xxd -p -c 32

O resultado é o hash a ser publicado no DNS:

_25._tcp.mail.domain.tld. IN TLSA 3 1 1 <hash_aqui>

Requisitos

  • DNSSEC obrigatório no domínio do servidor de e-mail
  • O registro TLSA deve estar no domínio do MX (mail.domain.tld), não no domínio do e-mail (domain.tld)

Verificando

drill -D TLSA _25._tcp.mail.domain.tld

4. Removendo SHA-1 dos Signature Algorithms

O Internet.nl reporta os algoritmos de assinatura anunciados pelo servidor, não apenas os negociados. Por padrão, o OpenSSL anuncia ECDSA+SHA1 no TLS 1.2, mesmo que nunca seja usado na prática.

A solução é configurar o /etc/ssl/openssl.cnf globalmente.

Estrutura do openssl.cnf no FreeBSD

O arquivo já possui openssl_conf = openssl_init. Adicione ssl_conf na seção [openssl_init] e crie as seções necessárias:

[openssl_init]
providers = provider_sect
ssl_conf = ssl_sect          # <-- adicionar esta linha

[ssl_sect]
system_default = system_default_sect

[system_default_sect]
CipherString = DEFAULT:@SECLEVEL=2
SignatureAlgorithms = ecdsa_secp256r1_sha256:ecdsa_secp384r1_sha384:ecdsa_secp521r1_sha512:rsa_pss_rsae_sha256:rsa_pss_rsae_sha384:rsa_pss_rsae_sha512:rsa_pkcs1_sha256:rsa_pkcs1_sha384:rsa_pkcs1_sha512

O que cada diretiva faz:

DiretivaEfeito
CipherString = DEFAULT:@SECLEVEL=2Remove SHA-1, MD5, exige RSA >= 2048, desabilita compressão TLS
SignatureAlgorithmsRestringe os algoritmos de assinatura anunciados no TLS 1.2/1.3

Importante: CipherString sozinho não é suficiente — é necessário definir SignatureAlgorithms explicitamente usando os nomes IANA (ex: ecdsa_secp256r1_sha256), não o formato legado ECDSA+SHA256.

Após editar, reinicie todos os serviços:

service postfix restart
service nginx restart
service dovecot restart

Verificando

testssl.sh --starttls smtp mail.domain.tld:25 2>&1 | grep "TLS 1.2 sig_algs offered"

Resultado esperado (sem SHA1):

TLS 1.2 sig_algs offered: ECDSA+SHA256 ECDSA+SHA384 ECDSA+SHA512

5. Rollover de certificado com DANE

Ao trocar o certificado (com nova chave privada), o hash TLSA muda. O procedimento correto é:

  1. Gerar o novo certificado (sem colocar em produção)
  2. Calcular o hash da nova chave:
    openssl x509 -in /novo/cert.pem -pubkey -noout | \
      openssl pkey -pubin -outform DER | \
      openssl dgst -sha256 -binary | \
      xxd -p -c 32
    
  3. Publicar dois registros TLSA no DNS (antigo + novo)
  4. Aguardar propagação (verificar TTL com drill _25._tcp.mail.domain.tld TLSA)
  5. Colocar o novo certificado em produção e reiniciar os serviços
  6. Remover o registro TLSA antigo

Regra de ouro: DNS primeiro, certificado depois. A ordem inversa causa rejeição de e-mails por servidores que validam DANE.


6. Resultado

Após todas as configurações seu servidor alcançará 100% no Internet.nl, com entrada no Hall of Fame.

✅ IPv6
✅ DNSSEC
✅ DMARC (p=reject) + DKIM + SPF (-all)
✅ STARTTLS + DANE válido
✅ TLS 1.3 + TLS 1.2 sem SHA-1
✅ Cipher suites seguras (ECDHE-ECDSA)
✅ RPKI

Referências