
Servidor de e-mail sem DKIM e DMARC em 2026 é servidor com mensagem indo para o spam — ou voltando. Desde fevereiro de 2024, Gmail e Yahoo exigem autenticação de todo remetente, e a partir de novembro de 2025 o Gmail passou a recusar, temporária ou definitivamente, o tráfego fora das regras. Abaixo, o caminho completo num Postfix: SPF, assinatura DKIM com OpenDKIM, política DMARC e a verificação do lado de quem recebe com OpenDMARC. Tudo testado em laboratório no Ubuntu 24.04 e no 26.04.
O que os grandes provedores exigem
As regras que empurraram todo mundo para isso:
- Gmail, desde 1º de fevereiro de 2024: todo remetente precisa de SPF ou DKIM, DNS reverso (PTR) válido, TLS e taxa de spam abaixo de 0,3%. Quem manda mais de 5.000 mensagens por dia para contas Gmail precisa de SPF e DKIM, DMARC publicado (
p=nonebasta),From:alinhado e descadastro em um clique nos e-mails de marketing. Em novembro de 2025 o Google começou a endurecer a aplicação, com recusas temporárias e permanentes. - Yahoo: as mesmas exigências desde fevereiro de 2024, sem divulgar um número mínimo de mensagens para ser considerado envio em massa.
- Outlook.com: desde 5 de maio de 2025, domínios que mandam mais de 5.000 mensagens por dia precisam de SPF, DKIM e DMARC. Quem não tem vai para o lixo eletrônico.
Mesmo quem manda pouco ganha com os três: sem eles, qualquer um pode mandar e-mail com o seu domínio no From:, e o destino não tem como distinguir.
As três peças e o alinhamento
- SPF (RFC 7208): um registro TXT no domínio lista quais IPs podem entregar correio em nome dele. O destino compara com o IP que abriu a conexão. Vale para o endereço do envelope (
MAIL FROM), não para oFrom:que o leitor vê. - DKIM (RFC 6376): o servidor de saída assina cabeçalhos e corpo com uma chave privada; a chave pública fica no DNS. Se alguém mexer na mensagem no caminho, a assinatura quebra. Sobrevive a encaminhamento, o que o SPF não faz.
- DMARC: amarra os dois ao
From:. Exige que SPF ou DKIM passe e que o domínio validado seja o mesmo doFrom:— isso é o alinhamento. Também diz ao destino o que fazer quando falha (none,quarantine,reject) e para onde mandar relatórios. Em maio de 2026 a especificação virou padrão da IETF como RFC 9989, com os relatórios nas RFCs 9990 e 9991, substituindo a RFC 7489.

Neste post: domínio example.com, servidor mail.example.com, IP de saída 203.0.113.10. Troque pelos seus.
Versões e o detalhe do chroot
| Pacote | Ubuntu 24.04 | Ubuntu 26.04 | Debian 13 |
|---|---|---|---|
| postfix | 3.8.6 | 3.10.6 | 3.10.13 |
| opendkim | 2.11.0~beta2 | 2.11.0~beta2 | 2.11.0~beta2 |
| opendmarc | 1.4.2 | 1.4.2 | 1.4.2 |
| postfix-policyd-spf-python | 3.0.4 | 3.1.0 | 3.1.0 |
O OpenDKIM nunca teve uma 2.11 final: o upstream parou na beta2 de 2018 e as distribuições empacotam essa, com correções próprias. O OpenDMARC está na 1.4.2 desde 2021. Os dois continuam os milters padrão para Postfix.
A diferença que importa: no Ubuntu 24.04 e no Debian 13 o smtpd do Postfix roda em chroot em /var/spool/postfix; no 26.04 o pacote finalmente desligou o chroot por padrão. Confira no seu:
grep -E '^smtp\s+inet' /etc/postfix/master.cf
# smtp inet n - y - - smtpd ← "y" na 5ª coluna = chroot
Por isso os sockets dos milters vão ficar dentro de /var/spool/postfix e o Postfix vai apontar para eles com caminho relativo — funciona com e sem chroot, e foi assim que testei nos dois Ubuntu.
SPF: o primeiro registro
No DNS do domínio, um TXT na raiz:
example.com. IN TXT "v=spf1 mx ip4:203.0.113.10 -all"
mx autoriza os servidores do registro MX, ip4: o IP de saída, e -all diz que o resto não pode. Se outro serviço manda em seu nome (newsletter, ERP, Google Workspace), acrescente o include: que ele documenta. Dois cuidados:
- Um registro SPF só. Dois TXT começando com
v=spf1dão erro permanente. - No máximo 10 consultas DNS (
include,a,mx,redirect…) na avaliação inteira, contando osincludedos outros. Estourou, o SPF dápermerror.
Enquanto não tiver certeza de que listou tudo, use ~all (softfail). Com DMARC funcionando, o -all pesa menos do que parece — já explico por quê.
Aproveite e confira o DNS reverso: o PTR de 203.0.113.10 deve apontar para mail.example.com, e esse nome deve resolver de volta para o mesmo IP. O Gmail exige.
OpenDKIM: gerar a chave
sudo apt install -y opendkim opendkim-tools
sudo mkdir -p /etc/opendkim/keys/example.com
sudo opendkim-genkey -b 2048 -h sha256 -r -s s2026 -d example.com \
-D /etc/opendkim/keys/example.com
Isso cria s2026.private (a chave privada) e s2026.txt (o registro DNS pronto). -s s2026 é o seletor, o nome da chave; usar o ano facilita a troca depois. -b 2048 segue a RFC 8301, que exige no mínimo 1024 bits e recomenda 2048; o pacote do Debian/Ubuntu já usa 2048 por padrão, mas deixar explícito não custa. -r restringe a chave a e-mail. O opendkim-genkey empacotado só gera RSA — nada de Ed25519 por enquanto.
Permissões — a chave privada só pode ser lida pelo OpenDKIM:
sudo chown -R opendkim:opendkim /etc/opendkim
sudo chmod 700 /etc/opendkim/keys/example.com
sudo chmod 600 /etc/opendkim/keys/example.com/s2026.private
OpenDKIM: tabelas e opendkim.conf
Três arquivos pequenos. /etc/opendkim/key.table diz qual chave cada seletor usa:
s2026._domainkey.example.com example.com:s2026:/etc/opendkim/keys/example.com/s2026.private
/etc/opendkim/signing.table diz quais remetentes assinam com qual chave:
*@example.com s2026._domainkey.example.com
/etc/opendkim/trusted.hosts lista de onde vem correio que deve ser assinado (não verificado):
127.0.0.1
::1
localhost
Se aplicações de outras máquinas usam este Postfix como relay, acrescente os IPs delas. Quem envia autenticado via SMTP AUTH (porta 587, cliente de e-mail, webmail) é assinado automaticamente: o OpenDKIM assina quando o IP é interno ou quando o Postfix informa que a sessão foi autenticada.
Agora substitua o /etc/opendkim.conf:
Syslog yes
SyslogSuccess yes
LogWhy yes
Canonicalization relaxed/simple
Mode sv
OversignHeaders From
UserID opendkim
UMask 007
Socket local:/var/spool/postfix/opendkim/opendkim.sock
PidFile /run/opendkim/opendkim.pid
TrustAnchorFile /usr/share/dns/root.key
KeyTable refile:/etc/opendkim/key.table
SigningTable refile:/etc/opendkim/signing.table
ExternalIgnoreList /etc/opendkim/trusted.hosts
InternalHosts /etc/opendkim/trusted.hosts
Mode sv: assina o que sai e verifica o que chega.Canonicalization relaxed/simple: tolera mudanças de espaço nos cabeçalhos no caminho; o corpo tem de chegar idêntico.OversignHeaders From: impede que alguém acrescente um segundoFrom:sem quebrar a assinatura.refile:noSigningTableé o que faz o*@example.comfuncionar como curinga.Socketdentro de/var/spool/postfix, pelo motivo do chroot.
O arquivo /etc/default/opendkim ainda existe, mas o próprio pacote o marca como legado: tudo vai no opendkim.conf. Crie o diretório do socket, ponha o Postfix no grupo do OpenDKIM (o UMask 007 dá acesso ao grupo) e teste a configuração:
sudo mkdir -p /var/spool/postfix/opendkim
sudo chown opendkim:postfix /var/spool/postfix/opendkim
sudo chmod 750 /var/spool/postfix/opendkim
sudo usermod -aG opendkim postfix
sudo opendkim -n -x /etc/opendkim.conf && echo config OK
sudo systemctl restart opendkim
Publicar a chave no DNS
sudo cat /etc/opendkim/keys/example.com/s2026.txt
s2026._domainkey IN TXT ( "v=DKIM1; h=sha256; k=rsa; s=email; "
"p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2pGx9Ms2..."
"tyJOiuSbPxdG4hH7kpWfJscarmix9gP6iAVhM+Dieij2..." )
Crie um TXT com nome s2026._domainkey no domínio. Uma chave de 2048 bits não cabe numa única string DNS (limite de 255 caracteres), por isso o arquivo vem quebrado em várias strings entre aspas; o destino junta tudo sem espaços. No BIND, cole o bloco como está. Em painéis de provedor, a maioria aceita o valor inteiro colado numa linha só — v=DKIM1; h=sha256; k=rsa; s=email; p=MIIB...IDAQAB — e divide sozinha.
Quando o DNS propagar, teste:
dig +short TXT s2026._domainkey.example.com
sudo opendkim-testkey -d example.com -s s2026 -vvv
opendkim-testkey: checking key 's2026._domainkey.example.com'
opendkim-testkey: key not secure
opendkim-testkey: key OK
key OK é o que importa: a chave pública no DNS bate com a privada no disco. key not secure só avisa que o domínio não tem DNSSEC — não impede nada. Se aparecer Revoked key, o DNS devolveu um registro com p= vazio: seletor errado, registro ainda não propagado ou um curinga no domínio.
Um detalhe que confunde em rede interna: com TrustAnchorFile configurado, o OpenDKIM resolve DNS sozinho, a partir da raiz, e ignora o /etc/resolv.conf. Se você publicou a chave só num DNS interno, ele não vai achá-la.
Ligar os milters no Postfix
sudo postconf -e \
'smtpd_milters = local:opendkim/opendkim.sock, local:opendmarc/opendmarc.sock' \
'non_smtpd_milters = $smtpd_milters' \
'milter_default_action = accept'
sudo postfix check
O caminho relativo (opendkim/opendkim.sock) é resolvido a partir de /var/spool/postfix, com chroot ou sem. non_smtpd_milters faz assinar também o que entra pelo comando sendmail local (cron, scripts, aplicações PHP). O OpenDMARC entra na próxima seção; se quiser testar só o DKIM agora, deixe apenas o primeiro socket.
milter_default_action decide o que acontece se o milter estiver fora do ar. O padrão é tempfail: o Postfix recusa temporariamente e o remetente tenta de novo depois — nada se perde, mas nada entra nem sai enquanto o OpenDKIM estiver parado. Com accept, o correio continua fluindo, só que sem assinatura. Escolha consciente; eu prefiro accept com monitoramento do serviço.
sudo systemctl restart postfix
echo "teste" | mail -s "teste DKIM" voce@gmail.com # pacote mailutils
sudo grep opendkim /var/log/mail.log | tail -3
opendkim[4242]: 52C29B42EDE: DKIM-Signature field added (s=s2026, d=example.com)
DMARC: publicar a política
Outro TXT, em _dmarc.example.com. Comece observando:
_dmarc.example.com. IN TXT "v=DMARC1; p=none; rua=mailto:dmarc@example.com"
p=none: não muda nada na entrega; os provedores só passam a mandar relatórios.rua=: para onde vão os relatórios agregados — um XML por dia, por provedor, listando cada IP que mandou mensagem com o seu domínio e se passou em SPF e DKIM. É ali que aparece o sistema esquecido que também envia em seu nome.adkimeaspf(padrãor, relaxado): no modo relaxado,news.example.comalinha comexample.com. Deixe o padrão.sp=: política para subdomínios, se quiser diferente.
Depois de duas a quatro semanas lendo relatórios sem surpresas, suba para p=quarantine (falha vai para o spam) e, por fim, p=reject (falha é recusada). A tag pct=, usada para aplicar a política só a uma porcentagem das mensagens, foi removida na RFC 9989 e substituída por t=y (modo de teste). Muitos receptores ainda seguem a RFC antiga; a subida em degraus de none para reject funciona com os dois.
Se o rua= apontar para outro domínio (um serviço de relatórios, por exemplo), esse domínio precisa autorizar publicando example.com._report._dmarc.relatorios.example.net TXT "v=DMARC1" — sem isso, os provedores não mandam.
Para ler os XML sem sofrimento, o parsedmarc (Apache 2.0) lê a caixa de relatórios por IMAP e gera JSON, CSV ou manda para Elasticsearch/OpenSearch. O próprio OpenDMARC traz opendmarc-import e opendmarc-reports, mas eles servem para gerar relatórios sobre quem manda para você — e exigem um MySQL.
OpenDMARC: verificar o que chega
Até aqui você assina e publica. Do outro lado, o seu servidor também recebe e-mail — e pode recusar quem falsifica domínios com p=reject. É o trabalho do OpenDMARC:
sudo apt install -y opendmarc publicsuffix
Na instalação, o debconf pergunta se deve configurar um banco de dados com dbconfig-common; responda não — ele só serve para gerar relatórios. Substitua o /etc/opendmarc.conf:
AuthservID mail.example.com
TrustedAuthservIDs mail.example.com
IgnoreAuthenticatedClients true
IgnoreHosts /etc/opendmarc/ignore.hosts
RejectFailures true
RequiredHeaders true
SPFSelfValidate true
FailureReports false
PidFile /run/opendmarc/opendmarc.pid
PublicSuffixList /usr/share/publicsuffix/public_suffix_list.dat
Socket local:/var/spool/postfix/opendmarc/opendmarc.sock
Syslog true
UMask 0002
UserID opendmarc
AuthservIDeTrustedAuthservIDs: o nome que aparece nos cabeçalhosAuthentication-Results. O OpenDMARC lê o resultado do DKIM que o OpenDKIM anotou com esse nome.RejectFailures true: recusa quando o domínio do remetente publicap=rejecte a mensagem falha. Comfalse, só anota o cabeçalho.SPFSelfValidate true: o próprio OpenDMARC avalia o SPF, sem depender de outro componente.IgnoreAuthenticatedClientseIgnoreHosts: não verifica o correio dos seus usuários nem o local.RequiredHeaders true: recusa mensagens semFrom:válido — um truque comum de falsificação.FailureReports false: não manda relatórios de falha individuais, que carregam trechos da mensagem.
sudo mkdir -p /etc/opendmarc /var/spool/postfix/opendmarc
printf '127.0.0.1\n::1\n' | sudo tee /etc/opendmarc/ignore.hosts
sudo chown opendmarc:postfix /var/spool/postfix/opendmarc
sudo chmod 750 /var/spool/postfix/opendmarc
sudo usermod -aG opendmarc postfix
sudo opendmarc -n -c /etc/opendmarc.conf && echo config OK
sudo systemctl restart opendmarc postfix
O smtpd_milters da seção anterior já lista o socket do OpenDMARC depois do OpenDKIM — a ordem importa, porque o DMARC precisa do resultado do DKIM.
O teste que convence
No laboratório, com os três registros publicados num DNS local e p=reject, mandei de fora duas mensagens com From: ana@example.com, a partir de um IP que o SPF não autoriza. A primeira, com a assinatura DKIM original, foi entregue com estes cabeçalhos:
Authentication-Results: mail.example.com; dmarc=pass (p=reject dis=none) header.from=example.com
Authentication-Results: mail.example.com;
dkim=pass (2048-bit key; unprotected) header.d=example.com header.i=@example.com header.a=rsa-sha256 header.s=s2026
Received-SPF: Fail (mailfrom) identity=mailfrom; client-ip=172.17.0.2; ...
O SPF falhou, mas o DKIM passou e está alinhado com o From: — então o DMARC passa. É exatamente o caso de uma mensagem encaminhada. A segunda era a mesma mensagem com o corpo trocado por “Pague o boleto anexo.”:
opendkim[5617]: 8F25AB44DDF: bad signature data
opendmarc[5622]: 8F25AB44DDF: example.com fail
postfix/cleanup: 8F25AB44DDF: milter-reject: END-OF-MESSAGE ... 5.7.1 rejected by DMARC policy for example.com
Recusada. O mesmo aconteceu com uma mensagem forjada em nome de paypal.com, que publica p=reject de verdade.
Para testar o seu domínio de verdade, mande uma mensagem para uma conta Gmail e abra Mostrar original: o topo mostra SPF, DKIM e DMARC com PASS. O mail-tester.com dá um endereço descartável e uma nota com o diagnóstico de cada item.
SPF no Postfix: anotar, não recusar
É tentador recusar logo no RCPT TO tudo que falha no SPF, com o postfix-policyd-spf-python. No laboratório, isso fez o Postfix recusar a mensagem legítima do teste acima antes de o DKIM ser olhado: o SPF falhou e acabou ali. Encaminhamentos e listas de discussão caem nesse buraco o tempo todo. Com DMARC, a decisão fica melhor com ele. Se quiser o cabeçalho Received-SPF para o seu antispam, instale o policyd-spf só para anotar:
sudo apt install -y postfix-policyd-spf-python
sudo sed -i 's/^Mail_From_reject = Fail/Mail_From_reject = False/' \
/etc/postfix-policyd-spf-python/policyd-spf.conf
No /etc/postfix/master.cf:
policyd-spf unix - n n - 0 spawn
user=policyd-spf argv=/usr/bin/policyd-spf
E no main.cf, com o check_policy_service depois do reject_unauth_destination:
sudo postconf -e 'policyd-spf_time_limit = 3600' \
'smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination, check_policy_service unix:private/policyd-spf'
sudo systemctl reload postfix
Duas pegadinhas. No policyd-spf.conf, TestOnly = 1 é o modo normal, que aplica as recusas; 0 é que desliga — o nome engana. E o processo do policyd-spf fica vivo até uma hora (policyd-spf_time_limit) sem reler a configuração: depois de editar, reinicie o Postfix.
Trocar a chave sem perder mensagens
Troque a chave DKIM uma vez por ano, e imediatamente se a privada vazar. O seletor no nome é o que permite fazer isso sem janela de falha:
- Gere a nova com outro seletor:
opendkim-genkey -b 2048 -h sha256 -r -s s2027 -d example.com -D /etc/opendkim/keys/example.com, e acerte as permissões. - Publique
s2027._domainkeyno DNS e espere propagar (opendkim-testkey -s s2027). - Troque
s2026pors2027nokey.tablee nosigning.tablee reinicie o OpenDKIM. - Mantenha o registro
s2026no DNS por uma ou duas semanas — mensagens assinadas com ele ainda estão em filas e caixas sendo verificadas. - Remova o
s2026do DNS e apague a chave antiga.
O que costuma quebrar
warning: connect to Milter service local:opendkim/opendkim.sock: No such file or directory— oSocketdoopendkim.confnão está dentro de/var/spool/postfix, ou o serviço não subiu.Permission deniedno socket — faltouusermod -aG opendkim postfix(e reiniciar o Postfix depois), ou o diretório do socket não é do grupopostfix.- Mensagem sai sem assinatura — o remetente não casa com o
signing.table(semrefile:, o*@não funciona) ou o IP de origem não está emInternalHostse a sessão não foi autenticada.LogWhy yesexplica no log. dkim=failno destino com assinatura presente — algo alterou a mensagem depois de assinada: um filtro de conteúdo, disclaimer ou antivírus reinjetando depois do milter.- Registro DKIM colado com espaço ou aspas no meio da chave no painel do provedor. Compare com
dig +short TXT s2026._domainkey.example.com. - Consultas lentas — o SPF de alguns domínios grandes gera dezenas de consultas DNS. No laboratório, com o DNS lento de um roteador, uma única avaliação levou quase um minuto. Um resolver local com cache no servidor de e-mail resolve.
E o Rspamd?
O Rspamd faz assinatura DKIM, verificação de SPF, DKIM e DMARC, ARC e antispam num só serviço, também como milter do Postfix, e está empacotado em todas as distribuições citadas. Para um servidor novo com volume, é a escolha moderna. OpenDKIM e OpenDMARC continuam fazendo sentido quando você quer só a autenticação, com peças pequenas e configuração em arquivos de texto — que é o que mostrei aqui.
Para montar o resto do servidor: go-postfixadmin para as caixas virtuais, e a história por trás das peças em A história do Postfix e A história do Dovecot. Quem usa painel tem o DMARC integrado no ISPConfig 3.3.2.