Configurar DoH/DoT
En esta guía abordaremos la configuración de DNS mediante HTTPS (DoH) y DNS mediante TLS (DoT) en AdGuard Home, una potente solución de bloqueo de publicidad y rastreo a nivel de red. Como administradores de nuestra infraestructura, sabemos que proteger la privacidad y la integridad de las consultas DNS es fundamental. Los protocolos DoH y DoT permiten cifrar estas consultas, impidiendo que terceros las intercepten o manipulen.
Nos adentraremos paso a paso en el proceso, desde la creación de un certificado autofirmado hasta la correcta configuración del servidor para aceptar conexiones cifradas. Este enfoque nos permite implementar estas tecnologías de forma segura incluso en entornos domésticos o de laboratorio.
Crear un certificado autofirmado
Instalar dependencias
Nos aseguramos de que el sistema tenga instalados los paquetes curl, nss-tools, ca-certificates y openssl. Si no estuvieran presentes, los instalamos con:
dns1:~# apk add curl nss-tools ca-certificates openssl
El sistema procederá a resolver las dependencias necesarias y mostrará un mensaje de confirmación al finalizar la instalación:
(1/13) Installing ca-certificates (20241121-r1)
(2/13) Installing brotli-libs (1.1.0-r2)
(3/13) Installing c-ares (1.34.5-r0)
(4/13) Installing libunistring (1.2-r0)
(5/13) Installing libidn2 (2.3.7-r0)
(6/13) Installing nghttp2-libs (1.64.0-r0)
(7/13) Installing libpsl (0.21.5-r3)
(8/13) Installing libcurl (8.12.1-r1)
(9/13) Installing curl (8.12.1-r1)
(10/13) Installing nspr (4.36-r0)
(11/13) Installing sqlite-libs (3.48.0-r2)
(12/13) Installing nss (3.109-r0)
(13/13) Installing nss-tools (3.109-r0)
Executing busybox-1.37.0-r12.trigger
Executing ca-certificates-20241121-r1.trigger
OK: 77 MiB in 70 packages
Instalar mkcert
En la distribución Alpine Linux, el paquete mkcert no se encuentra en los repositorios oficiales, por lo que es necesario descargarlo manualmente. Luego le otorgamos permisos de ejecución y lo movemos a la ruta de binarios:
dns1:~# curl -L -o mkcert https://dl.filippo.io/mkcert/latest?for=linux/amd64
dns1:~# chmod +x mkcert
dns1:~# mv mkcert /usr/local/bin/
Instalar la CA local
Creamos una autoridad certificadora (CA) local y la instalamos en el sistema:
dns1:~# mkcert -install
En la salida podemos observar algo similar a lo siguiente:
Created a new local CA 💥
The local CA is now installed in the system trust store! ⚡️
Esto añadirá la CA en /root/.local/share/mkcert y en el almacén de certificados del sistema. A continuación, actualizamos dicho almacén:
dns1:~# update-ca-certificates
De esta forma, el sistema operativo reconocerá como válidos los certificados generados por mkcert.
Generar el certificado
Nos ubicamos en el directorio de instalación de AdGuard Home y generamos el certificado:
dns1:~# cd /opt/AdGuardHome
dns1:/opt/AdGuardHome# mkcert dns1.local
En la salida podemos observar algo similar a lo siguiente:
Created a new certificate valid for the following names 📜
- "dns1.local"
The certificate is at "./dns1.local.pem" and the key at "./dns1.local-key.pem" ✅
It will expire on 15 August 2027 🗓
Se generarán dos archivos en el directorio actual:
-
dns1.local.pem: certificado -
dns1.local-key.pem: clave privada
Podemos usar cualquier nombre de host local, siempre que los dispositivos puedan resolverlo por DNS o mediante el archivo /etc/hosts.
Configurar el certificado
Accedemos a la interfaz de AdGuard Home y, en el menú Configuración, elegimos la opción Configuración de cifrado. Activamos Habilitar cifrado (HTTPS, DNS mediante HTTPS y DNS mediante TLS). En Nombre de servidor introducimos dns1.local y marcamos la opción Redireccionar a HTTPS automáticamente.
Indicamos las rutas de los archivos generados:
-
Certificado:
/opt/AdGuardHome/dns1.local.pem -
Clave privada:
/opt/AdGuardHome/dns1.local-key.pem
Es normal que aparezcan advertencias como:
Estado:
La cadena de certificado no es válida
Asunto: OU=root@dns1.local,O=mkcert development certificate
Emisor: CN=mkcert root@dns1.local,OU=root@dns1.local,O=mkcert development CA
Expira: 2027-08-15 00:00:00
Nombres de hosts: dns1.local
Estado:
Esta es una clave privada RSA válida
Advertencia: validating certificate pair: certificate does not verify: x509: certificate signed by unknown authority
Esto se debe a que el certificado es autofirmado. Si se desea evitar estas advertencias, puede emplearse un certificado de Let’s Encrypt, aunque será necesario verificar el dominio públicamente.
Crear un certificado con Let’s Encrypt
Utilizar Let’s Encrypt con validación DNS (DNS-01 challenge) es una forma segura de obtener certificados válidos sin exponer AdGuard Home a Internet. Solo es necesario tener control sobre la zona DNS del dominio.
Validación DNS manual (sin plugin)
Instalar certbot en el sistema
Instalamos el paquete certbot si aún no está presente:
dns1:~# apk add certbot
El sistema procederá a resolver las dependencias necesarias y mostrará un mensaje de confirmación al finalizar la instalación:
(1/62) Installing libbz2 (1.0.8-r6)
(2/62) Installing libexpat (2.7.0-r0)
(3/62) Installing libffi (3.4.7-r0)
(4/62) Installing gdbm (1.24-r0)
(5/62) Installing libgcc (14.2.0-r4)
(6/62) Installing libstdc++ (14.2.0-r4)
(7/62) Installing mpdecimal (4.0.0-r0)
(8/62) Installing libpanelw (6.5_p20241006-r3)
(9/62) Installing readline (8.2.13-r0)
(10/62) Installing sqlite-libs (3.48.0-r2)
(11/62) Installing python3 (3.12.10-r0)
(12/62) Installing python3-pycache-pyc0 (3.12.10-r0)
(13/62) Installing pyc (3.12.10-r0)
(14/62) Installing py3-configargparse (1.7-r1)
(15/62) Installing py3-configargparse-pyc (1.7-r1)
(16/62) Installing py3-six (1.16.0-r9)
(17/62) Installing py3-six-pyc (1.16.0-r9)
(18/62) Installing py3-configobj (5.0.9-r0)
(19/62) Installing py3-configobj-pyc (5.0.9-r0)
(20/62) Installing py3-distro (1.9.0-r2)
(21/62) Installing py3-distro-pyc (1.9.0-r2)
(22/62) Installing py3-distutils-extra (2.47-r3)
(23/62) Installing py3-distutils-extra-pyc (2.47-r3)
(24/62) Installing py3-openssl (24.1.0-r1)
(25/62) Installing py3-openssl-pyc (24.1.0-r1)
(26/62) Installing py3-josepy (1.14.0-r1)
(27/62) Installing py3-josepy-pyc (1.14.0-r1)
(28/62) Installing py3-future (1.0.0-r1)
(29/62) Installing py3-future-pyc (1.0.0-r1)
(30/62) Installing py3-parsedatetime (2.6-r6)
(31/62) Installing py3-parsedatetime-pyc (2.6-r6)
(32/62) Installing py3-tz (2024.2-r0)
(33/62) Installing py3-tz-pyc (2024.2-r0)
(34/62) Installing py3-pyrfc3339 (1.1-r9)
(35/62) Installing py3-pyrfc3339-pyc (1.1-r9)
(36/62) Installing py3-parsing (3.1.4-r0)
(37/62) Installing py3-parsing-pyc (3.1.4-r0)
(38/62) Installing py3-packaging (24.2-r0)
(39/62) Installing py3-packaging-pyc (24.2-r0)
(40/62) Installing py3-setuptools (70.3.0-r0)
(41/62) Installing py3-setuptools-pyc (70.3.0-r0)
(42/62) Installing certbot-pyc (3.0.1-r0)
(43/62) Installing py3-certifi (2024.8.30-r0)
(44/62) Installing py3-certifi-pyc (2024.8.30-r0)
(45/62) Installing py3-charset-normalizer (3.4.0-r0)
(46/62) Installing py3-charset-normalizer-pyc (3.4.0-r0)
(47/62) Installing py3-idna (3.10-r0)
(48/62) Installing py3-idna-pyc (3.10-r0)
(49/62) Installing py3-urllib3 (1.26.20-r0)
(50/62) Installing py3-urllib3-pyc (1.26.20-r0)
(51/62) Installing py3-requests (2.32.3-r0)
(52/62) Installing py3-requests-pyc (2.32.3-r0)
(53/62) Installing py3-acme-pyc (3.0.1-r0)
(54/62) Installing py3-cparser (2.22-r1)
(55/62) Installing py3-cparser-pyc (2.22-r1)
(56/62) Installing py3-cffi (1.17.1-r1)
(57/62) Installing py3-cffi-pyc (1.17.1-r1)
(58/62) Installing py3-cryptography-pyc (44.0.0-r0)
(59/62) Installing python3-pyc (3.12.10-r0)
(60/62) Installing py3-cryptography (44.0.0-r0)
(61/62) Installing py3-acme (3.0.1-r0)
(62/62) Installing certbot (3.0.1-r0)
Executing busybox-1.37.0-r12.trigger
OK: 129 MiB in 119 packages
Ejecutar certbot en modo manual
Ejecutamos:
dns1:~# certbot certonly --manual --preferred-challenges dns -d dns1.dominio.tld
Se nos pedirá un correo electrónico para el certificado:
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Enter email address (used for urgent renewal and security notices)
(Enter 'c' to cancel): correo@dns.dominio.tld
A continuación, se nos pedirá la lectura de los términos y condiciones de uso lo cual leemos y aceptamos:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.5-February-24-2025.pdf. You must
agree in order to register with the ACME server. Do you agree?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: Yes
Luego, se nos ofrecerá una suscripción a un boletín informativo que no es obligatorio aceptar:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing, once your first certificate is successfully issued, to
share your email address with the Electronic Frontier Foundation, a founding
partner of the Let's Encrypt project and the non-profit organization that
develops Certbot? We'd like to send you email about our work encrypting the web,
EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: No
Finalmente, se nos pedirá la creación de un registro TXT para verificar el dominio:
Account registered.
Requesting a certificate for dns1.dominio.tld
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please deploy a DNS TXT record under the name:
_acme-challenge.dns1.dominio.tld.
with the following value:
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Before continuing, verify the TXT record has been deployed. Depending on the DNS
provider, this may take some time, from a few seconds to multiple minutes. You can
check if it has finished deploying with aid of online tools, such as the Google
Admin Toolbox: https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns1.dominio.tld.
Look for one or more bolded line(s) below the line ';ANSWER'. It should show the
value(s) you've just added.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue
Añadimos el registro TXT en nuestra zona DNS y pulsamos Intro. Tras ello, se descargará el certificado y se indicará la ruta donde se ha descargado:
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/dns1.dominio.tld/fullchain.pem
Key is saved at: /etc/letsencrypt/live/dns1.dominio.tld/privkey.pem
This certificate expires on 2025-08-15.
These files will be updated when the certificate renews.
NEXT STEPS:
- This certificate will not be renewed automatically. Autorenewal of --manual certificates requires the use of an authentication hook script (--manual-auth-hook) but one was not provided. To renew this certificate, repeat this same certbot command before the certificate's expiry date.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
If you like Certbot, please consider supporting our work by:
* Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
* Donating to EFF: https://eff.org/donate-le
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Tengamos en cuenta que este certificado solo es válido por tres meses. Debido a que hicimos los cambios manualmente, no podemos renovar el certificado de forma automática, dado que necesitamos modificar el registro TXT en cada renovación.
Podemos solventar ese inconveniente usando el API de algún proveedor o herramientas como acme.sh que soporta varios plugins y puede automatizar más casos que el propio Certbot.
Validación DNS automática
Como ejemplo, usaremos Cloudflare, proveedor que administra una parte significativa del tráfico global.
Instalar Certbot y el plugin DNS
Instalamos los paquetes certbot y certbot-dns-cloudflare:
dns1:~# apk add certbot certbot-dns-cloudflare
El sistema procederá a resolver las dependencias necesarias y mostrará un mensaje de confirmación al finalizar la instalación:
(1/76) Installing libbz2 (1.0.8-r6)
(2/76) Installing libexpat (2.7.0-r0)
(3/76) Installing libffi (3.4.7-r0)
(4/76) Installing gdbm (1.24-r0)
(5/76) Installing libgcc (14.2.0-r4)
(6/76) Installing libstdc++ (14.2.0-r4)
(7/76) Installing mpdecimal (4.0.0-r0)
(8/76) Installing libpanelw (6.5_p20241006-r3)
(9/76) Installing readline (8.2.13-r0)
(10/76) Installing sqlite-libs (3.48.0-r2)
(11/76) Installing python3 (3.12.10-r0)
(12/76) Installing python3-pycache-pyc0 (3.12.10-r0)
(13/76) Installing pyc (3.12.10-r0)
(14/76) Installing py3-configargparse (1.7-r1)
(15/76) Installing py3-configargparse-pyc (1.7-r1)
(16/76) Installing py3-six (1.16.0-r9)
(17/76) Installing py3-six-pyc (1.16.0-r9)
(18/76) Installing py3-configobj (5.0.9-r0)
(19/76) Installing py3-configobj-pyc (5.0.9-r0)
(20/76) Installing py3-distro (1.9.0-r2)
(21/76) Installing py3-distro-pyc (1.9.0-r2)
(22/76) Installing py3-distutils-extra (2.47-r3)
(23/76) Installing py3-distutils-extra-pyc (2.47-r3)
(24/76) Installing py3-openssl (24.1.0-r1)
(25/76) Installing py3-openssl-pyc (24.1.0-r1)
(26/76) Installing py3-josepy (1.14.0-r1)
(27/76) Installing py3-josepy-pyc (1.14.0-r1)
(28/76) Installing py3-future (1.0.0-r1)
(29/76) Installing py3-future-pyc (1.0.0-r1)
(30/76) Installing py3-parsedatetime (2.6-r6)
(31/76) Installing py3-parsedatetime-pyc (2.6-r6)
(32/76) Installing py3-tz (2024.2-r0)
(33/76) Installing py3-tz-pyc (2024.2-r0)
(34/76) Installing py3-pyrfc3339 (1.1-r9)
(35/76) Installing py3-pyrfc3339-pyc (1.1-r9)
(36/76) Installing py3-parsing (3.1.4-r0)
(37/76) Installing py3-parsing-pyc (3.1.4-r0)
(38/76) Installing py3-packaging (24.2-r0)
(39/76) Installing py3-packaging-pyc (24.2-r0)
(40/76) Installing py3-setuptools (70.3.0-r0)
(41/76) Installing py3-setuptools-pyc (70.3.0-r0)
(42/76) Installing certbot-pyc (3.0.1-r0)
(43/76) Installing py3-certifi (2024.8.30-r0)
(44/76) Installing py3-certifi-pyc (2024.8.30-r0)
(45/76) Installing py3-charset-normalizer (3.4.0-r0)
(46/76) Installing py3-charset-normalizer-pyc (3.4.0-r0)
(47/76) Installing py3-idna (3.10-r0)
(48/76) Installing py3-idna-pyc (3.10-r0)
(49/76) Installing py3-urllib3 (1.26.20-r0)
(50/76) Installing py3-urllib3-pyc (1.26.20-r0)
(51/76) Installing py3-requests (2.32.3-r0)
(52/76) Installing py3-requests-pyc (2.32.3-r0)
(53/76) Installing py3-acme-pyc (3.0.1-r0)
(54/76) Installing py3-cparser (2.22-r1)
(55/76) Installing py3-cparser-pyc (2.22-r1)
(56/76) Installing py3-cffi (1.17.1-r1)
(57/76) Installing py3-cffi-pyc (1.17.1-r1)
(58/76) Installing py3-cryptography-pyc (44.0.0-r0)
(59/76) Installing python3-pyc (3.12.10-r0)
(60/76) Installing py3-cryptography (44.0.0-r0)
(61/76) Installing py3-acme (3.0.1-r0)
(62/76) Installing certbot (3.0.1-r0)
(63/76) Installing py3-soupsieve (2.6-r0)
(64/76) Installing py3-soupsieve-pyc (2.6-r0)
(65/76) Installing py3-beautifulsoup4 (4.12.3-r3)
(66/76) Installing py3-beautifulsoup4-pyc (4.12.3-r3)
(67/76) Installing py3-attrs (24.2.0-r0)
(68/76) Installing py3-attrs-pyc (24.2.0-r0)
(69/76) Installing py3-jsonlines (4.0.0-r1)
(70/76) Installing py3-jsonlines-pyc (4.0.0-r1)
(71/76) Installing yaml (0.2.5-r2)
(72/76) Installing py3-yaml (6.0.2-r0)
(73/76) Installing py3-yaml-pyc (6.0.2-r0)
(74/76) Installing py3-cloudflare (2.14.3-r1)
(75/76) Installing py3-cloudflare-pyc (2.14.3-r1)
(76/76) Installing certbot-dns-cloudflare (3.0.1-r0)
Executing busybox-1.37.0-r12.trigger
OK: 132 MiB in 133 package
Obtener un token API
Entramos en nuestra cuenta de Cloudflare. En el menú Perfil seleccionamos la opción Tokens de API y creamos un token personalizado con los siguientes permisos:
-
Zona > Zona > Leer
-
Zona > DNS > Editar
Por motivos de seguridad, limitamos el token solo a las zonas necesarias, y lo guardamos en un lugar seguro.
Crear archivos de credenciales
Creamos la carpeta para la configuración de Let’s Encrypt:
dns1:~# mkdir -p /etc/letsencrypt
Creamos el archivo de credenciales:
dns1:~# echo ‘dns_cloudflare_api_token = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX’ > /etc/letsencrypt/cloudflare.ini
Protegemos el archivo
dns1:~# chmod 600 /etc/letsencrypt/cloudflare.ini
Obtener el certificado
Ejecutamos:
dns1:~# certbot certonly --dns-cloudflare --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini --email hostmaster@dominio.tld --agree-tos --no-eff-email -d dns1.dominio.tld
Certbot descargará el certificado e indicará su ubicación:
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Requesting a certificate for dns1.dominio.tld
Waiting 10 seconds for DNS changes to propagate
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/dns1.dominio.tld/fullchain.pem
Key is saved at: /etc/letsencrypt/live/dns1.dominio.tld/privkey.pem
This certificate expires on 2025-10-01.
These files will be updated when the certificate renews.
NEXT STEPS:
- The certificate will need to be renewed before it expires. Certbot can automatically renew the certificate in the background, but you may need to take steps to enable that functionality. See https://certbot.org/renewal-setup for instructions.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
If you like Certbot, please consider supporting our work by:
* Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
* Donating to EFF: https://eff.org/donate-le
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Los archivos se guardarán en:
-
Certificado:
/etc/letsencrypt/live/dns1.dominio.tld/fullchain.pem -
Clave privada:
/etc/letsencrypt/live/dns1.dominio.tld/privkey.pem
Configuramos AdGuard Home como en el paso 5. Configurar certificado de la sección Crear un certificado autofirmado.
Configurar renovación automática
Certbot incluye una tare de renovación automática mediante cron, pero podemos garantizar el reinicio de AdGuard Home tras cada renovación añadiendo una línea en crontab:
dns1:~# crontab -e
Añadimos la siguiente línea:
0 2 * * * certbot renew --quiet --deploy-hook "rc-service AdGuardHome restart"
Esto hará que la renovación se ejecute cada día a las 2 de la mañana. Si el certificado no requiere renovación, el comando no tendrá efecto. Así se garantiza una operación robusta y sin interrupciones.