Como criar seu próprio Dynamic DNS com Cloudflare usando Node.js e Docker

Se você já precisou acessar sua rede doméstica remotamente, provavelmente já ouviu falar de serviços de Dynamic DNS (DDNS). Existem diversas opções pagas e gratuitas no mercado, como NOIP, desec.io e o famoso DuckDNS. Mas, se você gosta de colocar a mão na massa e quer aprender como tudo funciona por trás dos panos, este post é para você!

Atenção: Essa é uma solução caseira e autogerenciável, ideal para estudos e uso pessoal. Se você precisa de algo escalável e com alta confiabilidade, recomendo optar por soluções pagas e profissionais.

Outras opções gratuitas

Se você não quer usar seu próprio domínio, existem alternativas gratuitas como FreeDNS e [freemyip.com], que já oferecem subdomínios prontos para uso.


Passo a passo: Integrando seu domínio com Cloudflare

1. Tenha seu domínio gerenciado pela Cloudflare

Você pode transferir um domínio existente ou até mesmo comprar um novo diretamente pela Cloudflare. O importante é que seu domínio esteja sob o gerenciamento deles, pois é assim que conseguiremos automatizar as atualizações de DNS via API.

2. Gere um token de API

No painel da Cloudflare, crie um token de API com permissão para editar zonas DNS. Esse token será usado para autenticar as requisições da sua aplicação e permitir a alteração dos registros IP.


Configurando o ambiente

Clone o repositório e configure suas variáveis de ambiente em um arquivo .env baseado no exemplo abaixo:

CLOUDFLARE_TOKEN=Bearer SEUTOKENAQUI
CLOUDFLARE_EMAIL=seuemail@mail.com.br
CLOUDFLARE_DOMAIN=seudominio.com.br
CLOUDFLARE_SUBDOMAIN=subdomain1;subdomain2

Rodando com Docker

Com as variáveis configuradas, basta clonar a imagem e rodar o container:

docker pull paulosix12/cloudflareupdater:latest
docker run --env-file .env paulosix12/cloudflareupdater:latest

Pronto! O script já estará monitorando seu IP e atualizando os registros DNS automaticamente.


Quer customizar? Veja o código completo

Se quiser entender, modificar ou contribuir, segue o código principal do projeto:

const path = require('path');
const { CronJob } = require('cron');
require('dotenv').config({ path: path.join(__dirname, '../.env') });
const axios = require('axios');

// Configuração de variáveis de ambiente
const domain = process.env.CLOUDFLARE_DOMAIN;
const subdomains = process.env.CLOUDFLARE_SUBDOMAIN.split(';');
const cronSchedule = process.env.CRON_SCHEDULE || '*/30 * * * *';
const apiEndPt = 'https://api.cloudflare.com/client/v4';

// Configuração global do Axios
axios.defaults.headers.common['X-Auth-Email'] = process.env.CLOUDFLARE_EMAIL;
axios.defaults.headers.common['Authorization'] = process.env.CLOUDFLARE_TOKEN;
axios.defaults.headers.common['Content-Type'] = 'application/json';

// Função para obter o IP atual
async function getMyIp() {
  try {
    const response = await axios.get('https://api.ipify.org/');
    return response.data;
  } catch (error) {
    console.error('[getMyIp] Erro ao obter IP:', error.message);
    throw error;
  }
}

// Função para obter o Zone ID
async function getDnsZone() {
  try {
    const response = await axios.get(`${apiEndPt}/zones?name=${domain}&status=active`);
    if (response.data.result.length === 0) {
      throw new Error('Zone não encontrada');
    }
    return response.data.result[0].id;
  } catch (error) {
    console.log(error)
    console.error('[getDnsZone] Erro ao obter Zone ID:', error.message);
    throw error;
  }
}

// Função para obter registro DNS de um subdomínio
async function getDnsRecord(zone_id, subdomain) {
  try {
    const response = await axios.get(`${apiEndPt}/zones/${zone_id}/dns_records?type=A&name=${subdomain}.${domain}`);
    return response.data.result[0] || null;
  } catch (error) {
    console.error(`[getDnsRecord] Erro ao obter registro DNS para ${subdomain}:`, error.message);
    return null;
  }
}

// Função para atualizar registro DNS
async function updateDnsIP(zone_id, record, ip) {
  try {
    await axios.put(`${apiEndPt}/zones/${zone_id}/dns_records/${record.id}`, {
      type: 'A',
      name: record.name,
      content: ip,
      ttl: 3600,
      proxied: false,
    });
    console.log(`[updateDnsIP] Registro DNS atualizado para ${record.name}: ${ip}`);
  } catch (error) {
    console.error(`[updateDnsIP] Erro ao atualizar DNS para ${record.name}:`, error.message);
  }
}

// Função principal de atualização
async function updateAllDnsRecords() {
  try {
    const ipAtual = await getMyIp();
    const zoneId = await getDnsZone();

    for (const subdomain of subdomains) {
      const record = await getDnsRecord(zoneId, subdomain);

      if (!record) {
        console.warn(`[updateAllDnsRecords] Subdomínio ${subdomain} não encontrado no Cloudflare.`);
        continue;
      }

      if (record.content !== ipAtual) {
        await updateDnsIP(zoneId, record, ipAtual);
      } else {
        console.log(`[updateAllDnsRecords] IP de ${subdomain} já está atualizado (${ipAtual}).`);
      }
    }
  } catch (error) {
    console.error('[updateAllDnsRecords] Erro geral:', error.message);
  }
}

// Cron Job para rodar a cada 30 minutos
const jobUpdate = new CronJob(cronSchedule, updateAllDnsRecords);

// Inicia o job
jobUpdate.start();
console.log('Cloudflare DNS Updater iniciado.')

Considerações finais

Com poucos passos, você pode ter seu próprio serviço de Dynamic DNS rodando em casa, usando Node.js, Docker e a API da Cloudflare. Isso é perfeito para quem gosta de aprender na prática e ter controle total sobre as próprias soluções.

Se quiser contribuir, sugerir melhorias ou relatar bugs, fique à vontade para comentar ou abrir um PR no repositório!

Boas automações e até o próximo post! 🚀

© Paulo EduardoRSS