Certificado TLS com ACME-DNS e DNS-01

Diagrama exemplo
Descrição
Diversos sistemas e serviços que utilizamos na Internet fazem uso de certificados digitais para validar se uma fonte é legítima, também para criptografar a comunicação e assim garantir segurança e privacidade entre o visitante e o sistema ou a comunicação entre sistemas. Diversos serviços conhecidos utilizam os certificados digitais como: milhares de sites web via HTTPS, servidores de envio e recebimento de e-mails (MTAs), clientes de e-mail (MUAs) quando se comunicam com os MTAs, DNS quando utilizamos DNSSEC e qualquer outro sistema que necessite de certificado digital para operar com criptografia dos dados. Em um passado não tão distante, sites eram acessados de maneira insegura usando HTTP (80/tcp) e era muito comum enganar um visitante com um site falso para roubar-lhe os dados ou induzi-lo a executar algum malware hospedado. Um certificado serve para garantir que aquele domínio acessado é de fato legítimo e não fraudulento. Não garante que seus dados estarão seguros armazenados naquele sistema, mas isso é uma outra história. O sistema funciona com criptografia assimétrica onde temos um par de chaves, a chave pública e uma chave privada. Nós assinamos as coisas com nossa chave privada e os sistemas conseguem confirmar que é nosso através da nossa chave pública. Tá mas quem garante que eu, sou eu mesmo e não um impostor? Para resolver esse problema entra em ação as Autoridades Certificadoras (CAs), elas assinam seu certificado atestando que você é quem diz ser. Existem diversas CAs no mundo mas a que vamos falar e utilizar aqui é a Let's Encrypt. Ela é uma opção gratuita e extremamente utilizada na Internet.
Como funciona a Let's Encrypt
Aqui vamos falar de assinatura de certificados para nossos domínios de Internet. Vamos dizer que você tenha registrado o domínio seudominio.com.br lá no Registro.br. O Registro.br sabe quem você é e possui seus dados, que foram passados durante o registro do domínio. Lá constam os servidores de DNS Autoritativos que são responsáveis pelos registros do seu domínio. Até aqui não temos nenhum certificado digital que garanta para os visitantes que o site visitado é realmente o seu site. Imagina que por um problema de DNS comprometido em algum ISP, seu cliente foi parar em um servidor que não é o seu e abrindo um site que não é o seu mas que é muito idêntico ao seu. Aqui vai entrar o seu certificado digital assinado pela CA, para salvar o seu cliente de ter seus dados comprometidos.
A Let's Encrypt para assinar seu certificado também precisa confirmar que você é quem diz ser, não é mesmo? Ela faz isso confirmando que você é o controlador do domínio, que no nosso exemplo é o seudominio.com.br. Ela pode fazer isso de duas formas:
- HTTP-01.
- DNS-01.
No modelo de confirmação HTTP-01 é necessário que a Let's Encrypt acesse seu servidor através do FQDN para o qual você está solicitando o seu certificado digital. Estamos falando de uma conexão entrante (80/tcp) e que é possível quando existe uma rede pública de acesso. Mas e se você quisesse um certificado digital para um servidor privado seu? Um com IP 192.168.0.10 e FC00::192:168:0:10. Aí nesse caso podemos tomar para uso a autenticação via DNS-01.
Usando DNS-01, a Let's Encrypt conseguirá confirmar a sua identidade através de um registro TXT de DNS contendo um desafio gerado pela Let's Encrypt e que provará que você é o detentor do domínio. Assim você poderá gerar certificados para qualquer IP da sua rede, sendo ele privado ou público. O único problema desse tipo de autenticação, é que precisaríamos de um DNS compatível com um cliente ACME e que permitisse inclusões e atualizações dos registros TXT com os desafios mas são pouquíssimos os DNS(s) Autoritativos que possuem essa capacidade. Felizmente temos uma outra solução bem fácil de implementar, acme-dns.
Meu artigo será exatamente sobre o uso do DNS-01 e o serviço acme-dns.
ACME-DNS
O acme-dns é um micro servidor de DNS programado em Go por Joona Hoikkala e que também desenvolveu o acme-dns-client que também iremos utilizar. O acme-dns terá a função de armazenar e atualizar os registros TXT de DNS, com os desafios gerados pela Let's Encrypt e este possui uma API HTTPS para se comunicar com o acme-dns-client. Assim, toda vez que necessitar de atualizar os certificados antes dos 3 meses de expiração, poderá ser feito automaticamente e veremos isso aqui.
Vamos utilizar os seguintes programas neste artigo:
- Certbot, o cliente ACME mais utilizado para solicitar os certificados digitais Let's Encrypt.
- ACME-DNS-CLIENT, o software utilizado com o certbot, para atualizar os registros TXT de DNS com os desafios corretos do seu servidor.
- ACME-DNS, o serviço de DNS que armazenará os desafios para o seu servidor.
O funcionamento do conjunto acme-dns/acme-dns-client é bem simples, é necessário criarmos um subdomínio para a nossa gerência de desafios apontando para onde nosso servidor acme-dns ficará rodando. Vamos usar para isso um sistema GNU/Linux Debian mas você pode adaptar para outras distribuições que desejar. Abaixo nosso exemplo de configuração inicial no nosso DNS Autoritativo:
auth.seudominio.com.br. IN NS auth.seudominio.com.br. auth.seudominio.com.br. IN A 198.18.0.1 auth.seudominio.com.br. IN A 2001:db8::198:18:0:1
Acima estamos criando nosso subdomínio auth e apontando para os IPs fictícios 198.18.0.1 e 2001:db8:198:0:1 que é onde estará rodando nosso servidor acme-dns. Esse serviço uma vez rodando, você poderá usá-lo para criar certificados TLS usando Let's Encrypt DNS-01 para qualquer domínio e servidor seu, seja ele público ou privado.
Como estaremos usando o Debian 12 (Bookworm) certifique-se de estar com o repositório backports abaixo, no seu /etc/apt/sources.list:
deb http://deb.debian.org/debian bookworm-backports main non-free-firmware contrib non-free
O Go precisaremos apenas para compilar o acme-dns e o acme-dns-client e depois, se quiser, poderá desinstalá-lo.
# apt update # apt install golang-1.23 golang-github-mattn-go-sqlite3-dev git sqlite3 sqlite3-tools # export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/go-1.23/bin" # cd /usr/local/src # git clone https://github.com/joohoi/acme-dns # cd acme-dns # export GOPATH=/tmp/acme-dns # go build # mv acme-dns /usr/local/bin # adduser --system --gecos "acme-dns Service" --disabled-password --group --home /var/lib/acme-dns acme-dns # mv acme-dns.service /etc/systemd/system/acme-dns.service # systemctl daemon-reload # systemctl enable acme-dns.service
Se quiser manter o Go funcionando, adicione na PATH em /etc/profile o caminho /usr/lib/go-1.23/bin.
Os comandos acima irão instalar o Go e um módulo pro SQLite3 para o Go, o SQLite3, irá baixar o projeto oficial do acme-dns, irá compilá-lo e jogar o binário acme-dns em /usr/local/bin, irá criar um usuário não privilegiado para ele e por fim colocará para rodar como um serviço no systemd.
Agora falta pouco para termos rodando nosso acme-dns. Crie o diretório onde ficará a configuração do seu acme-dns:
# mkdir -p /etc/acme-dns
Dentro desse diretório criaremos um arquivo chamado config.cfg e o conteúdo dele está configurado para o nosso exemplo, mas nesse caso é só você alterar as configurações para o seu uso em produção:
Conteúdo de /etc/acme-dns/config.cfg:
[general] # DNS interface. Note that systemd-resolved may reserve port 53 on 127.0.0.53 # In this case acme-dns will error out and you will need to define the listening interface # for example: listen = "127.0.0.1:53" listen = ":53" # protocol, "both", "both4", "both6", "udp", "udp4", "udp6" or "tcp", "tcp4", "tcp6" protocol = "both" # domain name to serve the requests off of domain = "auth.seudominio.com.br" # zone name server nsname = "auth.seudominio.com.br" # admin email address, where @ is substituted with . nsadmin = "noc.seudominio.com.br" # predefined records served in addition to the TXT records = [ # domain pointing to the public IP of your acme-dns server "auth.seudominio.com.br. A 198.18.0.1", "auth.seudominio.com.br. AAAA 2001:db8:198:18:0:1", # specify that auth.example.org will resolve any *.auth.example.org records "auth.seudominio.com.br. NS auth.seudominio.com.br.", ] # debug messages from CORS etc debug = false [database] # Database engine to use, sqlite3 or postgres engine = "sqlite3" # Connection string, filename for sqlite3 and postgres://$username:$password@$host/$db_name for postgres # Please note that the default Docker image uses path /var/lib/acme-dns/acme-dns.db for sqlite3 connection = "/var/lib/acme-dns/acme-dns.db" # connection = "postgres://user:password@localhost/acmedns_db" [api] # listen ip eg. 127.0.0.1 ip = "0.0.0.0" # disable registration endpoint disable_registration = false # listen port, eg. 443 for default HTTPS port = "443" # possible values: "letsencrypt", "letsencryptstaging", "cert", "none" tls = "letsencrypt" # only used if tls = "cert" tls_cert_privkey = "/etc/tls/example.org/privkey.pem" tls_cert_fullchain = "/etc/tls/example.org/fullchain.pem" # only used if tls = "letsencrypt" acme_cache_dir = "api-certs" # optional e-mail address to which Let's Encrypt will send expiration notices for the API's cert notification_email = "" # CORS AllowOrigins, wildcards can be used corsorigins = [ "*" ] # use HTTP header to get the client ip use_header = false # header name to pull the ip address / list of ip addresses from header_name = "X-Forwarded-For" [logconfig] # logging level: "error", "warning", "info" or "debug" loglevel = "debug" # possible values: stdout, TODO file & integrations logtype = "stdout" # file path for logfile TODO # logfile = "./acme-dns.log" # format, either "json" or "text" logformat = "text"
O arquivo já possui comentários das opções de configuração para ajudá-lo a personalizar mais o serviço mas não entraremos em detalhe. Só buscar na documentação do projeto. Acima configuramos ele para funcionar com nossos dados fictícios, estará funcionando tanto via IPv4 quanto via IPv6 apontados para auth.seudominio.com.br. Ele também já está configurado para solicitar para ele um certificado Let's Encrypt automaticamente.
Após terminada a configuração acima, iniciaremos o serviço:
# systemctl start acme-dns.service
Após iniciar o serviço teremos as seguintes portas up: 53/udp, 53/tcp e a 443/tcp. Esta última porta é para a comunicação API entre o acme-dns-client e o acme-dns. Para checar se o serviço está rodando:
# systemctl status acme-dns

Importante! Para funcionar o serviço de DNS Autoritativo tem que estar configurado certinho. Depois que o acme-dns estiver rodando você pode checar assim. Lembre-se de fazer com seus dados reais.
# host auth.seudominio.com.br auth.seudominio.com.br has address 198.18.0.1 auth.seudominio.com.br has IPv6 address 2001:db8:198:18:0:1
# apt update
# apt install golang-1.23 golang-github-mattn-go-sqlite3-dev git sqlite3 sqlite3-tools
# export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/go-1.23/bin"Se apresentar erro de NXDOMAIN, então algo não ficou bem configurado no seu DNS Autoritativo, ou na configuração do seu acme-dns. Dê uma revisada novamente aqui no artigo.
Se tudo ocorreu bem, então você tem um serviço acme-dns rodando e pronto para armazenar os desafios TXT da Let's Encrypt.
CERTBOT e ACME-DNS-CLIENT
Agora vamos entrar na parte de como gerar um certificado TLS para o seu servidor, mesmo que ele possua apenas IP privado, sem abertura de portas e mais seguro. Para todo sistema que precisarmos gerar certificados, utilizaremos o certbot e do acme-dns-client.
No diagrama deste artigo dou como exemplo um servidor Web HTTPS e um outro com aplicação corporativa mas você poderá gerar certificados para qualquer ambiente.
Mais uma vez nosso ambiente será um GNU/Linux Debian 12 (Bookworm).
Primeiramente vamos precisar instalar o Go para compilar o acme-dns-client mas depois você poderá removê-lo se quiser. Não esqueça do repositório backports, como visto no início do artigo. Vamos seguir os passos abaixo:
# apt update # apt install golang-1.23 certbot git # export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/go-1.23/bin" # cd /usr/local/src # git clone https://github.com/acme-dns/acme-dns-client ; cd acme-dns-client ; go get ; go build # mv acme-dns-client /usr/local/bin/
Pronto! Instalamos o certbot, cliente ACME para fazer as solicitações e atualizações dos nossos certificados para a Let's Encrypt e também instalamos nosso acme-dns-client.
Registrando seu domínio e subdomínios que ira utilizar no seu servidor
Vamos supor que você tenha um ERP da empresa no IP 192.168.10.10 e que queira gerar o certificado para ele. Após fazer os passos acima, façamos nosso registro:
# acme-dns-client register -d erp.seudominio.com.br -s https://auth.seudominio.com.br
Esse comando acima vai registrar no seu acme-dns o registro para erp.seudominio.com.br e ele vai te vai solicitar que crie um desafio CNAME apontando para o seu acme-dns. No exemplo abaixo a saída vai ser parecida mas o CNAME diferente para você cadastrar no seu DNS Autoritativo. O acme-dns-client ficará aguardando e testando até que você gere esse registro CNAME. Aqui não optei por usar o registro CAA (Certification Authority Authorization) que seria para só autorizar determinada autoridade certificadora à assinar seu certificado.
# acme-dns-client register -d erp.seudominio.com.br -s https://auth.seudominio.com.br [*] New acme-dns account for domain seudominio.com.br successfully registered! Do you want acme-dns-client to monitor the CNAME record change? [Y/n]: Y To finalize the setup, you need to create a CNAME record pointing from _acme-challenge.erp.seudominio.com.br to the newly created acme-dns domain 42fa6f9f-5a56-4985-bbd7-c8a0e0ce4e1b.auth.seudominio.com.br A correctly set up CNAME record should look like the following: _acme-challenge.erp.seudominio.com.br. IN CNAME 42fa6f9f-5a56-4985-bbd7-c8a0e0ce4e1b.auth.seudominio.com.br. Waiting for CNAME record to be set up for domain erp.seudominio.com.br Querying the authoritative nameserver every 15 seconds. [*] CNAME record is now correctly set up! A CAA record allows you to control additional certificate issuance safeguards. The currently supported version allows the domain owner to control which certificate authorities are allowed to issue certificates for the domain in question. The certificate authorities MUST check and respect the CAA records in the validation process. There's also a standard (RFC 8657) that extends the CAA record to limit the issuance of certificates to a specific validation method and/or to a specific ACME account. While they can be tested using staging environment of Let's Encrypt for example, they're not enabled in the production yet. It is still be worthwhile to configure them so you'll be protected when the feature gets enabled. Do you wish to set up a CAA record now? [y/N]: N
Vejam como é interessante seu funcionamento: O Let's Encrypt para confirmar que você é o dono do domínio, ele procura pelo TXT com o desafio em _acme-challenge.seudominio.com.br só que ele não está no seu DNS Autoritativo e sim no seu servidor acme-dns. O que o CNAME faz é repassar a consulta para o domínio 42fa6f9f-5a56-4985-bbd7-c8a0e0ce4e1b.auth.seudominio.com.br que está criado no seu acme-dns, que por sua vez contém o desafio TXT que a Let's Encrypt está procurando.
Lembra que no início do artigo criamos o subdomínio auth.seudomínio.com.br e apontamos tudo que for para ele, consultar no acme-dns? Ou seja 42fa6f9f-5a56-4985-bbd7-c8a0e0ce4e1b é um subdomínio de auth.seudominio.com.br. É confuso para quem nunca configurou um serviço de DNS mas se você realmente não está conseguindo entender, procure dar uma estudada sobre DNS (Domain Name System).
Após conseguirmos registrar o domínio acima, passamos para o uso com o certbot, para que possamos solicitar nosso certificado digital para a Let's Encrypt.
Usando o certbot com o acme-dns-client
Abaixo estamos solicitamos ao certbot ,usando o nosso acme-dns-client, para gerar o certificado TLS para o erp.seudominio.com.br. O Let's Encrypt gerará um desafio TXT (DNS-01) para o erp.seudomínio.com.br e o acme-dns-client incluirá o registro no acme-dns fazendo com que a Let's Encrypt encontre o desafio e libere o certificado digital.
# certbot certonly --manual --preferred-challenges dns --manual-auth-hook '/usr/local/bin/acme-dns-client' -d erp.seudominio.com.br Saving debug log to /var/log/letsencrypt/letsencrypt.log Enter email address (used for urgent renewal and security notices) (Enter 'c' to cancel): [email protected] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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: Y - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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: N Account registered. Requesting a certificate for erp.seudominio.com.br Successfully received certificate. Certificate is saved at: /etc/letsencrypt/live/erp.seudominio.com.br/fullchain.pem Key is saved at: /etc/letsencrypt/live/erp.seudominio.com.br/privkey.pem This certificate expires on 2025-07-22. These files will be updated when the certificate renews. Certbot has set up a scheduled task to automatically renew this certificate in the background. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Os certificados são gerados em /etc/letsencrypt e aí só usá-los em suas aplicações. Para atualizar automaticamente os certificados basta colocar algo assim em seu /etc/crontab:
00 00 1 * * root /usr/bin/certbot renew --post-hook="/usr/bin/systemctl reload nginx.service"
Acima estamos renovando o certificado todo dia 01 de cada mês às 00:00 e na sequência recarregando o nginx para conter o novo certificado. Você pode adaptar para a sua realidade.
Espero que seja útil este artigo e que agora não tenhamos mais que usar certificados TLS auto-assinados que são feios, não passam segurança e não confirmam a identidade de quem está fornecendo o serviço. Coloque certificado TLS válido em todos os seus serviços e mantenha as boas práticas em dia.
Essa documentação foi útil? Compartilhe, divulgue e ajude outras pessoas. Meus contatos podem ser vistos aqui.