<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>DNS on inetshell</title><link>https://inet.sh/es/posts/dns/</link><description>Recent content in DNS on inetshell</description><generator>Hugo</generator><language>es</language><lastBuildDate>Sat, 26 Sep 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://inet.sh/es/posts/dns/index.xml" rel="self" type="application/rss+xml"/><item><title>acme-dns: DNS-01 sin dar la llave de toda la zona (delegación CNAME de _acme-challenge) con certbot y cert-manager</title><link>https://inet.sh/es/posts/dns/acme-dns-dns-01-delegacion-cname/</link><pubDate>Sat, 26 Sep 2026 00:00:00 +0000</pubDate><guid>https://inet.sh/es/posts/dns/acme-dns-dns-01-delegacion-cname/</guid><description>Para un certificado wildcard hace falta el reto DNS-01, y eso suele significar guardar en el servidor un token de API con permiso sobre toda la zona DNS. acme-dns es un servidor DNS mínimo que solo sabe responder TXT bajo su propio subdominio: delegas _acme-challenge.tu-dominio con un CNAME y cada cliente recibe credenciales que solo sirven para su registro. Instalación del servidor, registro con allowfrom, el hook acme-dns-auth.py de certbot (primera ejecución con --debug-challenges), el solver acmeDNS de cert-manager con su Secret JSON, y las trampas de TTL y de renovación.</description><content:encoded><![CDATA[<h2 id="en-corto">En corto</h2>
<ol>
<li>
<p><strong>Delega una zona pequeña</strong> a la máquina donde correrá acme-dns (registros en tu DNS real):</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">auth.example.com.   NS   acme.example.com.
</span></span><span class="line"><span class="cl">acme.example.com.   A    203.0.113.10
</span></span></code></pre></td></tr></table>
</div>
</div></li>
<li>
<p><strong>acme-dns</strong> en esa máquina (<code>/etc/acme-dns/config.cfg</code>), escuchando DNS en el 53 y la API en el 443:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">general</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">listen</span> <span class="p">=</span> <span class="s2">&#34;0.0.0.0:53&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">protocol</span> <span class="p">=</span> <span class="s2">&#34;both&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">domain</span> <span class="p">=</span> <span class="s2">&#34;auth.example.com&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">nsname</span> <span class="p">=</span> <span class="s2">&#34;acme.example.com&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">nsadmin</span> <span class="p">=</span> <span class="s2">&#34;hostmaster.example.com&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">records</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">  <span class="s2">&#34;auth.example.com. A 203.0.113.10&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="s2">&#34;auth.example.com. NS acme.example.com.&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">api</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">ip</span> <span class="p">=</span> <span class="s2">&#34;0.0.0.0&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">port</span> <span class="p">=</span> <span class="s2">&#34;443&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">tls</span> <span class="p">=</span> <span class="s2">&#34;letsencrypt&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">disable_registration</span> <span class="p">=</span> <span class="kc">false</span>
</span></span></code></pre></td></tr></table>
</div>
</div></li>
<li>
<p><strong>Registra</strong> un cliente y crea el CNAME:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">curl -s -X POST https://acme.example.com/register -H <span class="s1">&#39;Content-Type: application/json&#39;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  -d <span class="s1">&#39;{&#34;allowfrom&#34;: [&#34;203.0.113.0/24&#34;]}&#39;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># {&#34;username&#34;:&#34;eabcdb41-…&#34;,&#34;password&#34;:&#34;pbAX…&#34;,&#34;fulldomain&#34;:&#34;d420c923-….auth.example.com&#34;,&#34;subdomain&#34;:&#34;d420c923-…&#34;,&#34;allowfrom&#34;:[&#34;203.0.113.0/24&#34;]}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">_acme-challenge.example.com.   CNAME   d420c923-….auth.example.com.
</span></span></code></pre></td></tr></table>
</div>
</div></li>
<li>
<p><strong>certbot</strong> con el hook:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">curl -fsSL -o /etc/letsencrypt/acme-dns-auth.py https://raw.githubusercontent.com/joohoi/acme-dns-certbot-joohoi/master/acme-dns-auth.py
</span></span><span class="line"><span class="cl">chmod +x /etc/letsencrypt/acme-dns-auth.py
</span></span><span class="line"><span class="cl">sed -i <span class="s1">&#39;s|^#!/usr/bin/env python$|#!/usr/bin/env python3|; s|^ACMEDNS_URL = .*|ACMEDNS_URL = &#34;https://acme.example.com&#34;|&#39;</span> /etc/letsencrypt/acme-dns-auth.py
</span></span><span class="line"><span class="cl">certbot certonly --manual --manual-auth-hook /etc/letsencrypt/acme-dns-auth.py <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --preferred-challenges dns --debug-challenges -d <span class="s1">&#39;*.example.com&#39;</span> -d example.com
</span></span></code></pre></td></tr></table>
</div>
</div><p>La primera vez el hook se registra solo, guarda las credenciales en <code>/etc/letsencrypt/acmedns.json</code> y <strong>se para</strong> para que crees el CNAME que imprime; Enter, y valida. Las renovaciones (<code>certbot renew</code>) ya no preguntan nada.</p>
</li>
</ol>
<h2 id="por-qué">Por qué</h2>
<p>DNS-01 es el único reto que da <strong>wildcards</strong> y el único que funciona para máquinas sin puerto 80 público. El precio habitual es un token de API de tu proveedor DNS en cada servidor que pide certificados; ese token normalmente puede cambiar <strong>cualquier registro de la zona</strong>, y vive en texto plano en <code>/etc/letsencrypt</code>.</p>
<p>acme-dns le da la vuelta: es un servidor DNS autoritativo de una zona propia (<code>auth.example.com</code>) que solo sabe servir registros TXT bajo subdominios aleatorios, con una API para actualizarlos. Como <code>_acme-challenge.example.com</code> es un <strong>CNAME</strong> hacia uno de esos subdominios, Let&rsquo;s Encrypt sigue la cadena y valida el TXT allí. Las credenciales de cada cliente solo sirven para su subdominio; robadas, lo peor que pasa es que alguien pida un certificado para ese nombre, no que reescriba tu zona.</p>
<h2 id="cert-manager">cert-manager</h2>
<p>El mismo esquema, con las credenciales en un Secret:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">kubectl -n cert-manager create secret generic acme-dns --from-file<span class="o">=</span>acmedns.json<span class="o">=</span>./acmedns.json
</span></span></code></pre></td></tr></table>
</div>
</div><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;example.com&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;username&#34;</span><span class="p">:</span> <span class="s2">&#34;eabcdb41-d89f-4580-826f-3e62e9755ef2&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;password&#34;</span><span class="p">:</span> <span class="s2">&#34;&lt;password del registro&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;fulldomain&#34;</span><span class="p">:</span> <span class="s2">&#34;d420c923-bbd7-4056-ab64-c3ca54c9b3cf.auth.example.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;subdomain&#34;</span><span class="p">:</span> <span class="s2">&#34;d420c923-bbd7-4056-ab64-c3ca54c9b3cf&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;allowfrom&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;203.0.113.0/24&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">cert-manager.io/v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">ClusterIssuer</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">letsencrypt-dns</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">acme</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">server</span><span class="p">:</span><span class="w"> </span><span class="l">https://acme-v02.api.letsencrypt.org/directory</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">email</span><span class="p">:</span><span class="w"> </span><span class="l">admin@example.com</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">privateKeySecretRef</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">letsencrypt-dns-account-key</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">solvers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">dns01</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">acmeDNS</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">host</span><span class="p">:</span><span class="w"> </span><span class="l">https://acme.example.com</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">accountSecretRef</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">acme-dns</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="l">acmedns.json</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>La clave del JSON es el dominio del certificado; para <code>*.example.com</code> y <code>example.com</code> vale la misma entrada (el CNAME <code>_acme-challenge.example.com</code> cubre ambos). Cada dominio distinto necesita su registro y su CNAME.</p>
<h2 id="trampas">Trampas</h2>
<ul>
<li><strong><code>allowfrom</code> con la IP que ve acme-dns</strong>: la pública de salida del servidor o del clúster, no la del pod. Si falla el <code>/update</code> con 401, es esto o la contraseña.</li>
<li><strong>TTL del CNAME</strong>: ponlo bajo (60–300 s) la primera vez; con un TTL de una hora, un error en el CNAME tarda una hora en corregirse a ojos de Let&rsquo;s Encrypt. El TXT lo sirve acme-dns con TTL 1.</li>
<li><strong>Puerto 53 de verdad</strong>: acme-dns tiene que ser alcanzable por UDP y TCP desde Internet, y ser el <strong>NS de <code>auth.example.com</code></strong> en tu zona padre. Sin el registro NS, la delegación no existe y la validación devuelve NXDOMAIN.</li>
<li><strong>Un registro por dominio</strong>, no por servidor: si tres servidores piden certificados para <code>example.com</code>, comparten el mismo registro y el mismo <code>acmedns.json</code>, o cada uno registra el suyo y el CNAME solo puede apuntar a uno. Comparte las credenciales.</li>
<li><strong><code>--manual</code> renueva bien</strong> porque el hook está guardado en <code>/etc/letsencrypt/renewal/&lt;dominio&gt;.conf</code>; lo que no puedes es cambiar el hook sin volver a ejecutar <code>certonly</code>.</li>
<li><strong>Versión</strong>: acme-dns 2.0.2 es de febrero de 2025 y el proyecto sigue activo; el hook de certbot no ha cambiado en años y funciona con Python 3 tras corregir el shebang.</li>
</ul>
<p>Referencias: <a href="https://github.com/joohoi/acme-dns">acme-dns</a>, <a href="https://github.com/joohoi/acme-dns-certbot-joohoi">acme-dns-certbot-joohoi</a> y <a href="https://cert-manager.io/docs/configuration/acme/dns01/acme-dns/">ACMEDNS en cert-manager</a>.</p>
<h2 id="ver-también">Ver también</h2>
<ul>
<li><a href="/es/posts/kubernetes/cert-manager-http01-308-ingressclassname/">cert-manager: el reto HTTP-01 devuelve 308</a> — cuando HTTP-01 es suficiente y no lo es.</li>
<li><a href="/es/posts/pfsense/haproxy-acme-letsencrypt-cloudflare/">HAProxy en pfSense con ACME y Cloudflare</a> — DNS-01 con el token del proveedor, el caso que acme-dns evita.</li>
<li><a href="/es/posts/dns/dkim-2048-txt-255-caracteres/">DKIM de 2048 bits en un TXT partido</a> — otro registro DNS que se rompe por detalles.</li>
</ul>
]]></content:encoded></item><item><title>DKIM de 2048 bits: la llave no cabe en un TXT de 255 caracteres</title><link>https://inet.sh/es/posts/dns/dkim-2048-txt-255-caracteres/</link><pubDate>Fri, 25 Sep 2026 00:00:00 +0000</pubDate><guid>https://inet.sh/es/posts/dns/dkim-2048-txt-255-caracteres/</guid><description>Cómo publicar una llave DKIM de 2048 bits (unos 410 caracteres) cuando el DNS rechaza el TXT por pasar de 255: partirla en varias cadenas entre comillas dentro del mismo registro. Comandos para Route 53 (CharacterStringTooLong), Google Cloud DNS, BIND y Terraform, y verificación con dig y openssl.</description><content:encoded><![CDATA[<h2 id="en-corto">En corto</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="nv">PUB</span><span class="o">=</span><span class="k">$(</span>openssl rsa -in dkim.key -pubout -outform DER 2&gt;/dev/null <span class="p">|</span> base64 <span class="p">|</span> tr -d <span class="s1">&#39;\n&#39;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;v=DKIM1; k=rsa; p=</span><span class="nv">$PUB</span><span class="s2">&#34;</span> <span class="p">|</span> fold -w <span class="m">255</span> <span class="p">|</span> sed <span class="s1">&#39;s/.*/&#34;&amp;&#34;/&#39;</span> <span class="p">|</span> paste -sd<span class="s1">&#39; &#39;</span> -
</span></span></code></pre></td></tr></table>
</div>
</div><p>Sale el valor listo para pegar en el proveedor de DNS, dos cadenas entre comillas separadas por un espacio:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">&#34;v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvs7EROOYh35C/1s7M//pIaRvu1XWXzdexgBR2dO7eq1oDw+X08BNJUVUEte3z7os5CNeP7YfZrAqyDJvA59wMK1qul6oHyy7z5P1aB4zNczvO7SZ20HogWjHjzYJ0qwmSUUtjlKvECQzoTCbS/F0gB9rdUZN02MJopDs8YWcohy8oqNLnrflHdxWm6jAb3sSa&#34; &#34;EQReu6iRKKPiXTGUISSetY+iXLo2j/2c5g5ppiIYkUTq58asamMNiCzB7BeF4WNkjz2P+9IJmmtkkJ0M/U4oThUMvBBtQ/XUV4i2N4PGB359G5d/PIMiCj4go2axA4p8ul7Qafbtv0fyPwOIGSojwIDAQAB&#34;
</span></span></code></pre></td></tr></table>
</div>
</div><p>Las dos van en <strong>el mismo registro TXT</strong>. Quien verifica DKIM las concatena sin espacios. Si el texto te lo dio Google Workspace o Microsoft 365 y no tienes la llave privada, es lo mismo sin el <code>openssl</code>: <code>echo &quot;&lt;valor&gt;&quot; | fold -w 255 | sed 's/.*/&quot;&amp;&quot;/' | paste -sd' ' -</code>.</p>
<h2 id="el-problema">El problema</h2>
<p>Un registro TXT se compone de una o más <em>character-strings</em> y cada una tiene un tope de <strong>255 bytes</strong> (RFC 1035). El registro entero puede ser mucho más largo; lo que no puede es tener una sola cadena de 410 caracteres.</p>
<table>
  <thead>
      <tr>
          <th>Llave RSA</th>
          <th>Largo de <code>p=</code></th>
          <th>Con <code>v=DKIM1; k=rsa; p=</code></th>
          <th>¿Cabe en una cadena?</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>1024 bits</td>
          <td>216</td>
          <td>234</td>
          <td>Sí</td>
      </tr>
      <tr>
          <td>2048 bits</td>
          <td>392</td>
          <td>410</td>
          <td>No: hacen falta dos</td>
      </tr>
      <tr>
          <td>4096 bits</td>
          <td>736</td>
          <td>754</td>
          <td>No: tres</td>
      </tr>
  </tbody>
</table>
<p>Google Workspace y Microsoft 365 generan llaves de 2048 por defecto, y con razón: 1024 está desaconsejado desde la RFC 8301. Así que hoy casi cualquier DKIM nuevo tropieza con esto, y cada proveedor reacciona distinto:</p>
<table>
  <thead>
      <tr>
          <th>Proveedor</th>
          <th>Qué pasa con 410 caracteres seguidos</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Route 53</td>
          <td>Rechaza con <code>CharacterStringTooLong</code>, en consola y CLI</td>
      </tr>
      <tr>
          <td>Google Cloud DNS</td>
          <td>Rechaza el valor; tienes que escribir tú las cadenas entre comillas</td>
      </tr>
      <tr>
          <td>Cloudflare</td>
          <td>Lo acepta y lo parte solo en trozos de 255</td>
      </tr>
      <tr>
          <td>BIND y la mayoría de servidores propios</td>
          <td>El archivo de zona no carga hasta que uses varias cadenas</td>
      </tr>
  </tbody>
</table>
<p>La RFC 6376 (§3.6.2.2) obliga a los verificadores a concatenar las cadenas antes de usarlas, así que partir la llave no es un truco: es el formato previsto.</p>
<h2 id="por-proveedor">Por proveedor</h2>
<h3 id="bind--archivo-de-zona">BIND / archivo de zona</h3>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">selector._domainkey  300  IN  TXT  ( &#34;v=DKIM1; k=rsa; p=MIIBIjANBgkq...primera parte&#34;
</span></span><span class="line"><span class="cl">                                     &#34;segunda parte...IDAQAB&#34; )
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="route-53">Route 53</h3>
<p>En la consola, en <strong>Value</strong> pega las dos cadenas entre comillas separadas por un espacio o un salto de línea. Por CLI el valor es una sola string con las comillas escapadas:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">aws route53 change-resource-record-sets --hosted-zone-id Z0123456789EXAMPLE --change-batch <span class="s1">&#39;{
</span></span></span><span class="line"><span class="cl"><span class="s1">  &#34;Changes&#34;: [{&#34;Action&#34;: &#34;UPSERT&#34;, &#34;ResourceRecordSet&#34;: {
</span></span></span><span class="line"><span class="cl"><span class="s1">    &#34;Name&#34;: &#34;selector._domainkey.example.com&#34;, &#34;Type&#34;: &#34;TXT&#34;, &#34;TTL&#34;: 300,
</span></span></span><span class="line"><span class="cl"><span class="s1">    &#34;ResourceRecords&#34;: [{&#34;Value&#34;: &#34;\&#34;v=DKIM1; k=rsa; p=...primera parte\&#34; \&#34;segunda parte...\&#34;&#34;}]
</span></span></span><span class="line"><span class="cl"><span class="s1">  }}]}&#39;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="google-cloud-dns">Google Cloud DNS</h3>
<p>En la consola, mismo formato en el campo de datos. Con <code>gcloud</code>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">gcloud dns record-sets create selector._domainkey.example.com. --zone<span class="o">=</span>example-zone <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --type<span class="o">=</span>TXT --ttl<span class="o">=</span><span class="m">300</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --rrdatas<span class="o">=</span><span class="s1">&#39;&#34;v=DKIM1; k=rsa; p=...primera parte&#34; &#34;segunda parte...&#34;&#39;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Con Terraform (<code>google_dns_record_set</code>), un solo elemento en <code>rrdatas</code> con las comillas escapadas:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-hcl" data-lang="hcl"><span class="line"><span class="cl"><span class="n">rrdatas</span> <span class="o">=</span><span class="n"> [&#34;\&#34;v</span><span class="o">=</span><span class="n">DKIM1; k</span><span class="o">=</span><span class="n">rsa; p</span><span class="o">=</span><span class="p">...</span><span class="k">primera</span> <span class="k">parte</span><span class="err">\</span><span class="s2">&#34; \&#34;segunda parte...\&#34;&#34;</span><span class="p">]</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="cloudflare">Cloudflare</h3>
<p>Pega el valor completo en <strong>Content</strong>; el panel lo parte al guardar. Al consultarlo con <code>dig</code> verás dos cadenas aunque tú metiste una: es normal.</p>
<h2 id="verificar">Verificar</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">dig +short TXT selector._domainkey.example.com
</span></span></code></pre></td></tr></table>
</div>
</div><p>Lo esperado es <code>&quot;v=DKIM1; k=rsa; p=MIIB...&quot; &quot;...IDAQAB&quot;</code>: dos cadenas, en un solo registro. Después comprueba que la llave decodifica a 2048 bits, lo que confirma que no se perdió ningún carácter en el corte:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">dig +short TXT selector._domainkey.example.com <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  <span class="p">|</span> tr -d <span class="s1">&#39;&#34; \n&#39;</span> <span class="p">|</span> sed -E <span class="s1">&#39;s/.*p=([A-Za-z0-9+/=]+).*/\1/&#39;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  <span class="p">|</span> base64 -d <span class="p">|</span> openssl pkey -pubin -inform DER -noout -text <span class="p">|</span> head -1
</span></span><span class="line"><span class="cl"><span class="c1"># Public-Key: (2048 bit)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Si <code>base64</code> protesta, se coló un espacio dentro de la llave o faltan caracteres. Cierra con un correo de prueba y <code>Authentication-Results: ... dkim=pass</code> en la cabecera; para mirarlo con más detalle, el <a href="https://dmarcian.com/dkim-inspector/">DKIM Inspector de dmarcian</a>.</p>
<h2 id="trampas">Trampas</h2>
<ul>
<li><strong>El corte puede ir en cualquier sitio</strong>, incluso a media base64, pero <strong>sin meter espacios dentro de la llave</strong>. El espacio entre las dos cadenas separa cadenas, no forma parte del dato.</li>
<li><strong>No dupliques comillas.</strong> Si el panel ya las pone por ti y pegas <code>&quot;...&quot;</code>, <code>dig</code> devuelve <code>&quot;\&quot;v=DKIM1...\&quot;&quot;</code> y el verificador falla.</li>
<li><strong>Un registro, no dos.</strong> Dos TXT distintos con el mismo nombre son dos valores independientes, y ninguno es una llave válida.</li>
<li><strong>2048 es el punto medio.</strong> 4096 son tres cadenas y una respuesta DNS que se acerca al límite UDP; funciona, pero no gana nada para DKIM.</li>
<li><strong>Rotación</strong>: publica la llave nueva con otro selector, cambia el firmante, y borra el selector viejo después del TTL. Nunca edites el selector en uso.</li>
<li><strong>SPF tiene el mismo límite</strong>, pero un SPF que no cabe en 255 suele ser señal de demasiados <code>include:</code>; ahí la solución es acortarlo, no partirlo.</li>
</ul>
<p>Referencias: <a href="https://knowledge.workspace.google.com/admin/security/troubleshoot-dkim-issues">Google Workspace — problemas con DKIM</a>, <a href="https://repost.aws/knowledge-center/route53-resolve-dkim-text-record-error">AWS re:Post — CharacterStringTooLong</a> y el <a href="https://www.mailhardener.com/tools/dns-record-splitter">DNS record splitter de Mailhardener</a> si prefieres no usar <code>fold</code>.</p>
]]></content:encoded></item></channel></rss>