{"id":1671,"date":"2026-09-23T09:17:00","date_gmt":"2026-09-23T12:17:00","guid":{"rendered":"https:\/\/www.linuxpro.com.br\/?p=1671"},"modified":"2026-09-23T09:17:00","modified_gmt":"2026-09-23T12:17:00","slug":"dkim-dmarc-postfix-opendkim-opendmarc","status":"publish","type":"post","link":"https:\/\/www.linuxpro.com.br\/en\/2026\/09\/dkim-dmarc-postfix-opendkim-opendmarc\/","title":{"rendered":"DKIM and DMARC on Postfix with OpenDKIM and OpenDMARC"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" alt=\"Mascote do LinuxPro carimbando um lacre de cera com uma chave nos envelopes que saem do servidor de e-mail, enquanto um portal com escudo confere as mensagens e o c\u00e3o caramelo observa\" src=\"\/wp-content\/uploads\/2026\/09\/dkim-dmarc-postfix.webp\" width=\"1486\" height=\"856\" \/><\/p>\n<p>Servidor de e-mail sem DKIM e DMARC em 2026 \u00e9 servidor com mensagem indo para o spam \u2014 ou voltando. Desde fevereiro de 2024, Gmail e Yahoo exigem autentica\u00e7\u00e3o de todo remetente, e a partir de novembro de 2025 o Gmail passou a recusar, tempor\u00e1ria ou definitivamente, o tr\u00e1fego fora das regras. Abaixo, o caminho completo num Postfix: SPF, assinatura DKIM com OpenDKIM, pol\u00edtica DMARC e a verifica\u00e7\u00e3o do lado de quem recebe com OpenDMARC. Tudo testado em laborat\u00f3rio no Ubuntu 24.04 e no 26.04.<\/p>\n<p><!-- more --><\/p>\n<h2>O que os grandes provedores exigem<\/h2>\n<p>As regras que empurraram todo mundo para isso:<\/p>\n<ul>\n<li><strong><a href=\"https:\/\/support.google.com\/a\/answer\/81126\">Gmail<\/a><\/strong>, desde 1\u00ba de fevereiro de 2024: <em>todo<\/em> remetente precisa de SPF <em>ou<\/em> DKIM, DNS reverso (PTR) v\u00e1lido, TLS e taxa de spam abaixo de 0,3%. Quem manda mais de 5.000 mensagens por dia para contas Gmail precisa de SPF <em>e<\/em> DKIM, DMARC publicado (<code>p=none<\/code> basta), <code>From:<\/code> alinhado e descadastro em um clique nos e-mails de marketing. Em novembro de 2025 o Google come\u00e7ou a <a href=\"https:\/\/support.google.com\/a\/answer\/14229414\">endurecer a aplica\u00e7\u00e3o<\/a>, com recusas tempor\u00e1rias e permanentes.<\/li>\n<li><strong><a href=\"https:\/\/senders.yahooinc.com\/best-practices\/\">Yahoo<\/a><\/strong>: as mesmas exig\u00eancias desde fevereiro de 2024, sem divulgar um n\u00famero m\u00ednimo de mensagens para ser considerado envio em massa.<\/li>\n<li><strong><a href=\"https:\/\/sendersupport.olc.protection.outlook.com\/pm\/\">Outlook.com<\/a><\/strong>: desde 5 de maio de 2025, dom\u00ednios que mandam mais de 5.000 mensagens por dia precisam de SPF, DKIM e DMARC. Quem n\u00e3o tem vai para o lixo eletr\u00f4nico.<\/li>\n<\/ul>\n<p>Mesmo quem manda pouco ganha com os tr\u00eas: sem eles, qualquer um pode mandar e-mail com o seu dom\u00ednio no <code>From:<\/code>, e o destino n\u00e3o tem como distinguir.<\/p>\n<h2>As tr\u00eas pe\u00e7as e o alinhamento<\/h2>\n<ul>\n<li><strong>SPF<\/strong> (<a href=\"https:\/\/www.rfc-editor.org\/rfc\/rfc7208\">RFC 7208<\/a>): um registro TXT no dom\u00ednio lista quais IPs podem entregar correio em nome dele. O destino compara com o IP que abriu a conex\u00e3o. Vale para o endere\u00e7o do <em>envelope<\/em> (<code>MAIL FROM<\/code>), n\u00e3o para o <code>From:<\/code> que o leitor v\u00ea.<\/li>\n<li><strong>DKIM<\/strong> (<a href=\"https:\/\/www.rfc-editor.org\/rfc\/rfc6376\">RFC 6376<\/a>): o servidor de sa\u00edda assina cabe\u00e7alhos e corpo com uma chave privada; a chave p\u00fablica fica no DNS. Se algu\u00e9m mexer na mensagem no caminho, a assinatura quebra. Sobrevive a encaminhamento, o que o SPF n\u00e3o faz.<\/li>\n<li><strong>DMARC<\/strong>: amarra os dois ao <code>From:<\/code>. Exige que SPF <em>ou<\/em> DKIM passe <em>e<\/em> que o dom\u00ednio validado seja o mesmo do <code>From:<\/code> \u2014 isso \u00e9 o <strong>alinhamento<\/strong>. Tamb\u00e9m diz ao destino o que fazer quando falha (<code>none<\/code>, <code>quarantine<\/code>, <code>reject<\/code>) e para onde mandar relat\u00f3rios. Em maio de 2026 a especifica\u00e7\u00e3o virou padr\u00e3o da IETF como <a href=\"https:\/\/www.rfc-editor.org\/rfc\/rfc9989\">RFC 9989<\/a>, com os relat\u00f3rios nas RFCs 9990 e 9991, substituindo a RFC 7489.<\/li>\n<\/ul>\n<p><img loading=\"lazy\" decoding=\"async\" alt=\"Diagrama do fluxo: o Postfix entrega a mensagem ao OpenDKIM, que assina com a chave privada; o servidor de destino verifica SPF, DKIM e DMARC consultando tr\u00eas registros TXT no DNS do dom\u00ednio e decide entre entregar, mandar para o spam ou recusar\" src=\"\/wp-content\/uploads\/2026\/09\/dkim-dmarc-postfix-fluxo.webp\" width=\"1200\" height=\"760\" \/><\/p>\n<p>Neste post: dom\u00ednio <code>example.com<\/code>, servidor <code>mail.example.com<\/code>, IP de sa\u00edda <code>203.0.113.10<\/code>. Troque pelos seus.<\/p>\n<h2>Vers\u00f5es e o detalhe do chroot<\/h2>\n<table>\n<thead>\n<tr>\n<th>Pacote<\/th>\n<th>Ubuntu 24.04<\/th>\n<th>Ubuntu 26.04<\/th>\n<th>Debian 13<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>postfix<\/td>\n<td>3.8.6<\/td>\n<td>3.10.6<\/td>\n<td>3.10.13<\/td>\n<\/tr>\n<tr>\n<td>opendkim<\/td>\n<td>2.11.0~beta2<\/td>\n<td>2.11.0~beta2<\/td>\n<td>2.11.0~beta2<\/td>\n<\/tr>\n<tr>\n<td>opendmarc<\/td>\n<td>1.4.2<\/td>\n<td>1.4.2<\/td>\n<td>1.4.2<\/td>\n<\/tr>\n<tr>\n<td>postfix-policyd-spf-python<\/td>\n<td>3.0.4<\/td>\n<td>3.1.0<\/td>\n<td>3.1.0<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>O OpenDKIM nunca teve uma 2.11 final: o upstream parou na beta2 de 2018 e as distribui\u00e7\u00f5es empacotam essa, com corre\u00e7\u00f5es pr\u00f3prias. O OpenDMARC est\u00e1 na 1.4.2 desde 2021. Os dois continuam os milters padr\u00e3o para Postfix.<\/p>\n<p>A diferen\u00e7a que importa: no Ubuntu 24.04 e no Debian 13 o <code>smtpd<\/code> do Postfix roda em <strong>chroot<\/strong> em <code>\/var\/spool\/postfix<\/code>; no 26.04 o pacote finalmente desligou o chroot por padr\u00e3o. Confira no seu:<\/p>\n<pre><code class=\"language-bash\">grep -E '^smtp\\s+inet' \/etc\/postfix\/master.cf\n# smtp  inet  n  -  y  -  -  smtpd    \u2190 \"y\" na 5\u00aa coluna = chroot\n<\/code><\/pre>\n<p>Por isso os sockets dos milters v\u00e3o ficar <em>dentro<\/em> de <code>\/var\/spool\/postfix<\/code> e o Postfix vai apontar para eles com caminho relativo \u2014 funciona com e sem chroot, e foi assim que testei nos dois Ubuntu.<\/p>\n<h2>SPF: o primeiro registro<\/h2>\n<p>No DNS do dom\u00ednio, um TXT na raiz:<\/p>\n<pre><code class=\"language-text\">example.com.   IN TXT   \"v=spf1 mx ip4:203.0.113.10 -all\"\n<\/code><\/pre>\n<p><code>mx<\/code> autoriza os servidores do registro MX, <code>ip4:<\/code> o IP de sa\u00edda, e <code>-all<\/code> diz que o resto n\u00e3o pode. Se outro servi\u00e7o manda em seu nome (newsletter, ERP, Google Workspace), acrescente o <code>include:<\/code> que ele documenta. Dois cuidados:<\/p>\n<ul>\n<li><strong>Um registro SPF s\u00f3.<\/strong> Dois TXT come\u00e7ando com <code>v=spf1<\/code> d\u00e3o erro permanente.<\/li>\n<li><strong>No m\u00e1ximo 10 consultas DNS<\/strong> (<code>include<\/code>, <code>a<\/code>, <code>mx<\/code>, <code>redirect<\/code>\u2026) na avalia\u00e7\u00e3o inteira, contando os <code>include<\/code> dos outros. Estourou, o SPF d\u00e1 <code>permerror<\/code>.<\/li>\n<\/ul>\n<p>Enquanto n\u00e3o tiver certeza de que listou tudo, use <code>~all<\/code> (softfail). Com DMARC funcionando, o <code>-all<\/code> pesa menos do que parece \u2014 j\u00e1 explico por qu\u00ea.<\/p>\n<p>Aproveite e confira o DNS reverso: o PTR de <code>203.0.113.10<\/code> deve apontar para <code>mail.example.com<\/code>, e esse nome deve resolver de volta para o mesmo IP. O Gmail exige.<\/p>\n<h2>OpenDKIM: gerar a chave<\/h2>\n<pre><code class=\"language-bash\">sudo apt install -y opendkim opendkim-tools\nsudo mkdir -p \/etc\/opendkim\/keys\/example.com\nsudo opendkim-genkey -b 2048 -h sha256 -r -s s2026 -d example.com \\\n  -D \/etc\/opendkim\/keys\/example.com\n<\/code><\/pre>\n<p>Isso cria <code>s2026.private<\/code> (a chave privada) e <code>s2026.txt<\/code> (o registro DNS pronto). <code>-s s2026<\/code> \u00e9 o <strong>seletor<\/strong>, o nome da chave; usar o ano facilita a troca depois. <code>-b 2048<\/code> segue a <a href=\"https:\/\/www.rfc-editor.org\/rfc\/rfc8301\">RFC 8301<\/a>, que exige no m\u00ednimo 1024 bits e recomenda 2048; o pacote do Debian\/Ubuntu j\u00e1 usa 2048 por padr\u00e3o, mas deixar expl\u00edcito n\u00e3o custa. <code>-r<\/code> restringe a chave a e-mail. O <code>opendkim-genkey<\/code> empacotado s\u00f3 gera RSA \u2014 nada de Ed25519 por enquanto.<\/p>\n<p>Permiss\u00f5es \u2014 a chave privada s\u00f3 pode ser lida pelo OpenDKIM:<\/p>\n<pre><code class=\"language-bash\">sudo chown -R opendkim:opendkim \/etc\/opendkim\nsudo chmod 700 \/etc\/opendkim\/keys\/example.com\nsudo chmod 600 \/etc\/opendkim\/keys\/example.com\/s2026.private\n<\/code><\/pre>\n<h2>OpenDKIM: tabelas e opendkim.conf<\/h2>\n<p>Tr\u00eas arquivos pequenos. <code>\/etc\/opendkim\/key.table<\/code> diz qual chave cada seletor usa:<\/p>\n<pre><code class=\"language-text\">s2026._domainkey.example.com example.com:s2026:\/etc\/opendkim\/keys\/example.com\/s2026.private\n<\/code><\/pre>\n<p><code>\/etc\/opendkim\/signing.table<\/code> diz quais remetentes assinam com qual chave:<\/p>\n<pre><code class=\"language-text\">*@example.com s2026._domainkey.example.com\n<\/code><\/pre>\n<p><code>\/etc\/opendkim\/trusted.hosts<\/code> lista de onde vem correio que deve ser <em>assinado<\/em> (n\u00e3o verificado):<\/p>\n<pre><code class=\"language-text\">127.0.0.1\n::1\nlocalhost\n<\/code><\/pre>\n<p>Se aplica\u00e7\u00f5es de outras m\u00e1quinas usam este Postfix como relay, acrescente os IPs delas. Quem envia autenticado via SMTP AUTH (porta 587, cliente de e-mail, webmail) \u00e9 assinado automaticamente: o OpenDKIM assina quando o IP \u00e9 interno <em>ou<\/em> quando o Postfix informa que a sess\u00e3o foi autenticada.<\/p>\n<p>Agora substitua o <code>\/etc\/opendkim.conf<\/code>:<\/p>\n<pre><code class=\"language-text\">Syslog                  yes\nSyslogSuccess           yes\nLogWhy                  yes\nCanonicalization        relaxed\/simple\nMode                    sv\nOversignHeaders         From\nUserID                  opendkim\nUMask                   007\nSocket                  local:\/var\/spool\/postfix\/opendkim\/opendkim.sock\nPidFile                 \/run\/opendkim\/opendkim.pid\nTrustAnchorFile         \/usr\/share\/dns\/root.key\nKeyTable                refile:\/etc\/opendkim\/key.table\nSigningTable            refile:\/etc\/opendkim\/signing.table\nExternalIgnoreList      \/etc\/opendkim\/trusted.hosts\nInternalHosts           \/etc\/opendkim\/trusted.hosts\n<\/code><\/pre>\n<ul>\n<li><code>Mode sv<\/code>: assina o que sai e verifica o que chega.<\/li>\n<li><code>Canonicalization relaxed\/simple<\/code>: tolera mudan\u00e7as de espa\u00e7o nos cabe\u00e7alhos no caminho; o corpo tem de chegar id\u00eantico.<\/li>\n<li><code>OversignHeaders From<\/code>: impede que algu\u00e9m acrescente um segundo <code>From:<\/code> sem quebrar a assinatura.<\/li>\n<li><code>refile:<\/code> no <code>SigningTable<\/code> \u00e9 o que faz o <code>*@example.com<\/code> funcionar como curinga.<\/li>\n<li><code>Socket<\/code> dentro de <code>\/var\/spool\/postfix<\/code>, pelo motivo do chroot.<\/li>\n<\/ul>\n<p>O arquivo <code>\/etc\/default\/opendkim<\/code> ainda existe, mas o pr\u00f3prio pacote o marca como legado: tudo vai no <code>opendkim.conf<\/code>. Crie o diret\u00f3rio do socket, ponha o Postfix no grupo do OpenDKIM (o <code>UMask 007<\/code> d\u00e1 acesso ao grupo) e teste a configura\u00e7\u00e3o:<\/p>\n<pre><code class=\"language-bash\">sudo mkdir -p \/var\/spool\/postfix\/opendkim\nsudo chown opendkim:postfix \/var\/spool\/postfix\/opendkim\nsudo chmod 750 \/var\/spool\/postfix\/opendkim\nsudo usermod -aG opendkim postfix\nsudo opendkim -n -x \/etc\/opendkim.conf &amp;&amp; echo config OK\nsudo systemctl restart opendkim\n<\/code><\/pre>\n<h2>Publicar a chave no DNS<\/h2>\n<pre><code class=\"language-bash\">sudo cat \/etc\/opendkim\/keys\/example.com\/s2026.txt\n<\/code><\/pre>\n<pre><code class=\"language-text\">s2026._domainkey\tIN\tTXT\t( \"v=DKIM1; h=sha256; k=rsa; s=email; \"\n\t  \"p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2pGx9Ms2...\"\n\t  \"tyJOiuSbPxdG4hH7kpWfJscarmix9gP6iAVhM+Dieij2...\" )\n<\/code><\/pre>\n<p>Crie um TXT com nome <code>s2026._domainkey<\/code> no dom\u00ednio. Uma chave de 2048 bits n\u00e3o cabe numa \u00fanica string DNS (limite de 255 caracteres), por isso o arquivo vem quebrado em v\u00e1rias strings entre aspas; o destino junta tudo sem espa\u00e7os. No BIND, cole o bloco como est\u00e1. Em pain\u00e9is de provedor, a maioria aceita o valor inteiro colado numa linha s\u00f3 \u2014 <code>v=DKIM1; h=sha256; k=rsa; s=email; p=MIIB...IDAQAB<\/code> \u2014 e divide sozinha.<\/p>\n<p>Quando o DNS propagar, teste:<\/p>\n<pre><code class=\"language-bash\">dig +short TXT s2026._domainkey.example.com\nsudo opendkim-testkey -d example.com -s s2026 -vvv\n<\/code><\/pre>\n<pre><code class=\"language-text\">opendkim-testkey: checking key 's2026._domainkey.example.com'\nopendkim-testkey: key not secure\nopendkim-testkey: key OK\n<\/code><\/pre>\n<p><code>key OK<\/code> \u00e9 o que importa: a chave p\u00fablica no DNS bate com a privada no disco. <code>key not secure<\/code> s\u00f3 avisa que o dom\u00ednio n\u00e3o tem DNSSEC \u2014 n\u00e3o impede nada. Se aparecer <code>Revoked key<\/code>, o DNS devolveu um registro com <code>p=<\/code> vazio: seletor errado, registro ainda n\u00e3o propagado ou um curinga no dom\u00ednio.<\/p>\n<p>Um detalhe que confunde em rede interna: com <code>TrustAnchorFile<\/code> configurado, o OpenDKIM resolve DNS sozinho, a partir da raiz, e ignora o <code>\/etc\/resolv.conf<\/code>. Se voc\u00ea publicou a chave s\u00f3 num DNS interno, ele n\u00e3o vai ach\u00e1-la.<\/p>\n<h2>Ligar os milters no Postfix<\/h2>\n<pre><code class=\"language-bash\">sudo postconf -e \\\n  'smtpd_milters = local:opendkim\/opendkim.sock, local:opendmarc\/opendmarc.sock' \\\n  'non_smtpd_milters = $smtpd_milters' \\\n  'milter_default_action = accept'\nsudo postfix check\n<\/code><\/pre>\n<p>O caminho relativo (<code>opendkim\/opendkim.sock<\/code>) \u00e9 resolvido a partir de <code>\/var\/spool\/postfix<\/code>, com chroot ou sem. <code>non_smtpd_milters<\/code> faz assinar tamb\u00e9m o que entra pelo comando <code>sendmail<\/code> local (cron, scripts, aplica\u00e7\u00f5es PHP). O OpenDMARC entra na pr\u00f3xima se\u00e7\u00e3o; se quiser testar s\u00f3 o DKIM agora, deixe apenas o primeiro socket.<\/p>\n<p><code>milter_default_action<\/code> decide o que acontece se o milter estiver fora do ar. O padr\u00e3o \u00e9 <code>tempfail<\/code>: o Postfix recusa temporariamente e o remetente tenta de novo depois \u2014 nada se perde, mas nada entra nem sai enquanto o OpenDKIM estiver parado. Com <code>accept<\/code>, o correio continua fluindo, s\u00f3 que sem assinatura. Escolha consciente; eu prefiro <code>accept<\/code> com monitoramento do servi\u00e7o.<\/p>\n<pre><code class=\"language-bash\">sudo systemctl restart postfix\necho \"teste\" | mail -s \"teste DKIM\" voce@gmail.com   # pacote mailutils\nsudo grep opendkim \/var\/log\/mail.log | tail -3\n<\/code><\/pre>\n<pre><code class=\"language-text\">opendkim[4242]: 52C29B42EDE: DKIM-Signature field added (s=s2026, d=example.com)\n<\/code><\/pre>\n<h2>DMARC: publicar a pol\u00edtica<\/h2>\n<p>Outro TXT, em <code>_dmarc.example.com<\/code>. Comece observando:<\/p>\n<pre><code class=\"language-text\">_dmarc.example.com.   IN TXT   \"v=DMARC1; p=none; rua=mailto:dmarc@example.com\"\n<\/code><\/pre>\n<ul>\n<li><code>p=none<\/code>: n\u00e3o muda nada na entrega; os provedores s\u00f3 passam a mandar relat\u00f3rios.<\/li>\n<li><code>rua=<\/code>: para onde v\u00e3o os relat\u00f3rios agregados \u2014 um XML por dia, por provedor, listando cada IP que mandou mensagem com o seu dom\u00ednio e se passou em SPF e DKIM. \u00c9 ali que aparece o sistema esquecido que tamb\u00e9m envia em seu nome.<\/li>\n<li><code>adkim<\/code> e <code>aspf<\/code> (padr\u00e3o <code>r<\/code>, relaxado): no modo relaxado, <code>news.example.com<\/code> alinha com <code>example.com<\/code>. Deixe o padr\u00e3o.<\/li>\n<li><code>sp=<\/code>: pol\u00edtica para subdom\u00ednios, se quiser diferente.<\/li>\n<\/ul>\n<p>Depois de duas a quatro semanas lendo relat\u00f3rios sem surpresas, suba para <code>p=quarantine<\/code> (falha vai para o spam) e, por fim, <code>p=reject<\/code> (falha \u00e9 recusada). A tag <code>pct=<\/code>, usada para aplicar a pol\u00edtica s\u00f3 a uma porcentagem das mensagens, foi <strong>removida<\/strong> na RFC 9989 e substitu\u00edda por <code>t=y<\/code> (modo de teste). Muitos receptores ainda seguem a RFC antiga; a subida em degraus de <code>none<\/code> para <code>reject<\/code> funciona com os dois.<\/p>\n<p>Se o <code>rua=<\/code> apontar para outro dom\u00ednio (um servi\u00e7o de relat\u00f3rios, por exemplo), esse dom\u00ednio precisa autorizar publicando <code>example.com._report._dmarc.relatorios.example.net TXT \"v=DMARC1\"<\/code> \u2014 sem isso, os provedores n\u00e3o mandam.<\/p>\n<p>Para ler os XML sem sofrimento, o <a href=\"https:\/\/github.com\/domainaware\/parsedmarc\">parsedmarc<\/a> (Apache 2.0) l\u00ea a caixa de relat\u00f3rios por IMAP e gera JSON, CSV ou manda para Elasticsearch\/OpenSearch. O pr\u00f3prio OpenDMARC traz <code>opendmarc-import<\/code> e <code>opendmarc-reports<\/code>, mas eles servem para <em>gerar<\/em> relat\u00f3rios sobre quem manda para voc\u00ea \u2014 e exigem um MySQL.<\/p>\n<h2>OpenDMARC: verificar o que chega<\/h2>\n<p>At\u00e9 aqui voc\u00ea assina e publica. Do outro lado, o seu servidor tamb\u00e9m recebe e-mail \u2014 e pode recusar quem falsifica dom\u00ednios com <code>p=reject<\/code>. \u00c9 o trabalho do OpenDMARC:<\/p>\n<pre><code class=\"language-bash\">sudo apt install -y opendmarc publicsuffix\n<\/code><\/pre>\n<p>Na instala\u00e7\u00e3o, o debconf pergunta se deve configurar um banco de dados com <code>dbconfig-common<\/code>; responda <strong>n\u00e3o<\/strong> \u2014 ele s\u00f3 serve para gerar relat\u00f3rios. Substitua o <code>\/etc\/opendmarc.conf<\/code>:<\/p>\n<pre><code class=\"language-text\">AuthservID                 mail.example.com\nTrustedAuthservIDs         mail.example.com\nIgnoreAuthenticatedClients true\nIgnoreHosts                \/etc\/opendmarc\/ignore.hosts\nRejectFailures             true\nRequiredHeaders            true\nSPFSelfValidate            true\nFailureReports             false\nPidFile                    \/run\/opendmarc\/opendmarc.pid\nPublicSuffixList           \/usr\/share\/publicsuffix\/public_suffix_list.dat\nSocket                     local:\/var\/spool\/postfix\/opendmarc\/opendmarc.sock\nSyslog                     true\nUMask                      0002\nUserID                     opendmarc\n<\/code><\/pre>\n<ul>\n<li><code>AuthservID<\/code> e <code>TrustedAuthservIDs<\/code>: o nome que aparece nos cabe\u00e7alhos <code>Authentication-Results<\/code>. O OpenDMARC l\u00ea o resultado do DKIM que o OpenDKIM anotou com esse nome.<\/li>\n<li><code>RejectFailures true<\/code>: recusa quando o dom\u00ednio do remetente publica <code>p=reject<\/code> e a mensagem falha. Com <code>false<\/code>, s\u00f3 anota o cabe\u00e7alho.<\/li>\n<li><code>SPFSelfValidate true<\/code>: o pr\u00f3prio OpenDMARC avalia o SPF, sem depender de outro componente.<\/li>\n<li><code>IgnoreAuthenticatedClients<\/code> e <code>IgnoreHosts<\/code>: n\u00e3o verifica o correio dos seus usu\u00e1rios nem o local.<\/li>\n<li><code>RequiredHeaders true<\/code>: recusa mensagens sem <code>From:<\/code> v\u00e1lido \u2014 um truque comum de falsifica\u00e7\u00e3o.<\/li>\n<li><code>FailureReports false<\/code>: n\u00e3o manda relat\u00f3rios de falha individuais, que carregam trechos da mensagem.<\/li>\n<\/ul>\n<pre><code class=\"language-bash\">sudo mkdir -p \/etc\/opendmarc \/var\/spool\/postfix\/opendmarc\nprintf '127.0.0.1\\n::1\\n' | sudo tee \/etc\/opendmarc\/ignore.hosts\nsudo chown opendmarc:postfix \/var\/spool\/postfix\/opendmarc\nsudo chmod 750 \/var\/spool\/postfix\/opendmarc\nsudo usermod -aG opendmarc postfix\nsudo opendmarc -n -c \/etc\/opendmarc.conf &amp;&amp; echo config OK\nsudo systemctl restart opendmarc postfix\n<\/code><\/pre>\n<p>O <code>smtpd_milters<\/code> da se\u00e7\u00e3o anterior j\u00e1 lista o socket do OpenDMARC depois do OpenDKIM \u2014 a ordem importa, porque o DMARC precisa do resultado do DKIM.<\/p>\n<h2>O teste que convence<\/h2>\n<p>No laborat\u00f3rio, com os tr\u00eas registros publicados num DNS local e <code>p=reject<\/code>, mandei de fora duas mensagens com <code>From: ana@example.com<\/code>, a partir de um IP que o SPF <em>n\u00e3o<\/em> autoriza. A primeira, com a assinatura DKIM original, foi entregue com estes cabe\u00e7alhos:<\/p>\n<pre><code class=\"language-text\">Authentication-Results: mail.example.com; dmarc=pass (p=reject dis=none) header.from=example.com\nAuthentication-Results: mail.example.com;\n\tdkim=pass (2048-bit key; unprotected) header.d=example.com header.i=@example.com header.a=rsa-sha256 header.s=s2026\nReceived-SPF: Fail (mailfrom) identity=mailfrom; client-ip=172.17.0.2; ...\n<\/code><\/pre>\n<p>O SPF falhou, mas o DKIM passou e est\u00e1 alinhado com o <code>From:<\/code> \u2014 ent\u00e3o o DMARC passa. \u00c9 exatamente o caso de uma mensagem encaminhada. A segunda era a mesma mensagem com o corpo trocado por \u201cPague o boleto anexo.\u201d:<\/p>\n<pre><code class=\"language-text\">opendkim[5617]: 8F25AB44DDF: bad signature data\nopendmarc[5622]: 8F25AB44DDF: example.com fail\npostfix\/cleanup: 8F25AB44DDF: milter-reject: END-OF-MESSAGE ... 5.7.1 rejected by DMARC policy for example.com\n<\/code><\/pre>\n<p>Recusada. O mesmo aconteceu com uma mensagem forjada em nome de <code>paypal.com<\/code>, que publica <code>p=reject<\/code> de verdade.<\/p>\n<p>Para testar o seu dom\u00ednio de verdade, mande uma mensagem para uma conta Gmail e abra <strong>Mostrar original<\/strong>: o topo mostra SPF, DKIM e DMARC com <code>PASS<\/code>. O <a href=\"https:\/\/www.mail-tester.com\/\">mail-tester.com<\/a> d\u00e1 um endere\u00e7o descart\u00e1vel e uma nota com o diagn\u00f3stico de cada item.<\/p>\n<h2>SPF no Postfix: anotar, n\u00e3o recusar<\/h2>\n<p>\u00c9 tentador recusar logo no <code>RCPT TO<\/code> tudo que falha no SPF, com o <code>postfix-policyd-spf-python<\/code>. No laborat\u00f3rio, isso fez o Postfix recusar a mensagem <em>leg\u00edtima<\/em> do teste acima antes de o DKIM ser olhado: o SPF falhou e acabou ali. Encaminhamentos e listas de discuss\u00e3o caem nesse buraco o tempo todo. Com DMARC, a decis\u00e3o fica melhor com ele. Se quiser o cabe\u00e7alho <code>Received-SPF<\/code> para o seu antispam, instale o policyd-spf s\u00f3 para anotar:<\/p>\n<pre><code class=\"language-bash\">sudo apt install -y postfix-policyd-spf-python\nsudo sed -i 's\/^Mail_From_reject = Fail\/Mail_From_reject = False\/' \\\n  \/etc\/postfix-policyd-spf-python\/policyd-spf.conf\n<\/code><\/pre>\n<p>No <code>\/etc\/postfix\/master.cf<\/code>:<\/p>\n<pre><code class=\"language-text\">policyd-spf  unix  -       n       n       -       0       spawn\n  user=policyd-spf argv=\/usr\/bin\/policyd-spf\n<\/code><\/pre>\n<p>E no <code>main.cf<\/code>, com o <code>check_policy_service<\/code> <strong>depois<\/strong> do <code>reject_unauth_destination<\/code>:<\/p>\n<pre><code class=\"language-bash\">sudo postconf -e 'policyd-spf_time_limit = 3600' \\\n  'smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination, check_policy_service unix:private\/policyd-spf'\nsudo systemctl reload postfix\n<\/code><\/pre>\n<p>Duas pegadinhas. No <code>policyd-spf.conf<\/code>, <code>TestOnly = 1<\/code> \u00e9 o modo <em>normal<\/em>, que aplica as recusas; <code>0<\/code> \u00e9 que desliga \u2014 o nome engana. E o processo do policyd-spf fica vivo at\u00e9 uma hora (<code>policyd-spf_time_limit<\/code>) sem reler a configura\u00e7\u00e3o: depois de editar, reinicie o Postfix.<\/p>\n<h2>Trocar a chave sem perder mensagens<\/h2>\n<p>Troque a chave DKIM uma vez por ano, e imediatamente se a privada vazar. O seletor no nome \u00e9 o que permite fazer isso sem janela de falha:<\/p>\n<ol>\n<li>Gere a nova com outro seletor: <code>opendkim-genkey -b 2048 -h sha256 -r -s s2027 -d example.com -D \/etc\/opendkim\/keys\/example.com<\/code>, e acerte as permiss\u00f5es.<\/li>\n<li>Publique <code>s2027._domainkey<\/code> no DNS e espere propagar (<code>opendkim-testkey -s s2027<\/code>).<\/li>\n<li>Troque <code>s2026<\/code> por <code>s2027<\/code> no <code>key.table<\/code> e no <code>signing.table<\/code> e reinicie o OpenDKIM.<\/li>\n<li>Mantenha o registro <code>s2026<\/code> no DNS por uma ou duas semanas \u2014 mensagens assinadas com ele ainda est\u00e3o em filas e caixas sendo verificadas.<\/li>\n<li>Remova o <code>s2026<\/code> do DNS e apague a chave antiga.<\/li>\n<\/ol>\n<h2>O que costuma quebrar<\/h2>\n<ul>\n<li><strong><code>warning: connect to Milter service local:opendkim\/opendkim.sock: No such file or directory<\/code><\/strong> \u2014 o <code>Socket<\/code> do <code>opendkim.conf<\/code> n\u00e3o est\u00e1 dentro de <code>\/var\/spool\/postfix<\/code>, ou o servi\u00e7o n\u00e3o subiu.<\/li>\n<li><strong><code>Permission denied<\/code> no socket<\/strong> \u2014 faltou <code>usermod -aG opendkim postfix<\/code> (e reiniciar o Postfix depois), ou o diret\u00f3rio do socket n\u00e3o \u00e9 do grupo <code>postfix<\/code>.<\/li>\n<li><strong>Mensagem sai sem assinatura<\/strong> \u2014 o remetente n\u00e3o casa com o <code>signing.table<\/code> (sem <code>refile:<\/code>, o <code>*@<\/code> n\u00e3o funciona) ou o IP de origem n\u00e3o est\u00e1 em <code>InternalHosts<\/code> e a sess\u00e3o n\u00e3o foi autenticada. <code>LogWhy yes<\/code> explica no log.<\/li>\n<li><strong><code>dkim=fail<\/code> no destino com assinatura presente<\/strong> \u2014 algo alterou a mensagem depois de assinada: um filtro de conte\u00fado, disclaimer ou antiv\u00edrus reinjetando depois do milter.<\/li>\n<li><strong>Registro DKIM colado com espa\u00e7o ou aspas no meio da chave<\/strong> no painel do provedor. Compare com <code>dig +short TXT s2026._domainkey.example.com<\/code>.<\/li>\n<li><strong>Consultas lentas<\/strong> \u2014 o SPF de alguns dom\u00ednios grandes gera dezenas de consultas DNS. No laborat\u00f3rio, com o DNS lento de um roteador, uma \u00fanica avalia\u00e7\u00e3o levou quase um minuto. Um resolver local com cache no servidor de e-mail resolve.<\/li>\n<\/ul>\n<h2>E o Rspamd?<\/h2>\n<p>O Rspamd faz assinatura DKIM, verifica\u00e7\u00e3o de SPF, DKIM e DMARC, ARC e antispam num s\u00f3 servi\u00e7o, tamb\u00e9m como milter do Postfix, e est\u00e1 empacotado em todas as distribui\u00e7\u00f5es citadas. Para um servidor novo com volume, \u00e9 a escolha moderna. OpenDKIM e OpenDMARC continuam fazendo sentido quando voc\u00ea quer s\u00f3 a autentica\u00e7\u00e3o, com pe\u00e7as pequenas e configura\u00e7\u00e3o em arquivos de texto \u2014 que \u00e9 o que mostrei aqui.<\/p>\n<p>Para montar o resto do servidor: <a href=\"\/2026\/09\/go-postfixadmin\/\">go-postfixadmin<\/a> para as caixas virtuais, e a hist\u00f3ria por tr\u00e1s das pe\u00e7as em <a href=\"\/2026\/09\/a-historia-do-postfix\/\">A hist\u00f3ria do Postfix<\/a> e <a href=\"\/2026\/09\/a-historia-do-dovecot\/\">A hist\u00f3ria do Dovecot<\/a>. Quem usa painel tem o DMARC integrado no <a href=\"\/2026\/09\/ispconfig-3-3-2-ubuntu-26-04-dmarc-e-correcoes-de-seguranca\/\">ISPConfig 3.3.2<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>SPF, DKIM signing with OpenDKIM, DMARC policy, and verification with OpenDMARC on Postfix, tested on Ubuntu 24.04 and 26.04.<\/p>","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11,78,2,120,3],"tags":[469,470,39,79,472,473,233,471],"class_list":["post-1671","post","type-post","status-publish","format-standard","hentry","category-debian","category-email","category-linux","category-servidores","category-ubuntu","tag-dkim","tag-dmarc","tag-dns","tag-email","tag-opendkim","tag-opendmarc","tag-postfix","tag-spf"],"_links":{"self":[{"href":"https:\/\/www.linuxpro.com.br\/en\/wp-json\/wp\/v2\/posts\/1671","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.linuxpro.com.br\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.linuxpro.com.br\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.linuxpro.com.br\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.linuxpro.com.br\/en\/wp-json\/wp\/v2\/comments?post=1671"}],"version-history":[{"count":1,"href":"https:\/\/www.linuxpro.com.br\/en\/wp-json\/wp\/v2\/posts\/1671\/revisions"}],"predecessor-version":[{"id":1677,"href":"https:\/\/www.linuxpro.com.br\/en\/wp-json\/wp\/v2\/posts\/1671\/revisions\/1677"}],"wp:attachment":[{"href":"https:\/\/www.linuxpro.com.br\/en\/wp-json\/wp\/v2\/media?parent=1671"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.linuxpro.com.br\/en\/wp-json\/wp\/v2\/categories?post=1671"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.linuxpro.com.br\/en\/wp-json\/wp\/v2\/tags?post=1671"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}