Automatizar DNS na DigitalOcean com Python e API

11 min de leitura Automação

A gestão eficiente de infraestrutura como código (IaC) exige que os profissionais de DevOps e Sysadmins automatizem tarefas repetitivas. Um dos cenários mais comuns é a criação, atualização ou remoção dinâmica de registros DNS. Fazer isso manualmente na interface gráfica do DigitalOcean não escala bem em ambientes de alta disponibilidade ou microsserviços.

O objetivo deste tutorial é demonstrar como utilizar a API RESTful do DigitalOcean para gerenciar registros DNS via Python. Utilizaremos o SDK oficial python-digitalocean e, em uma alternativa mais leve, requisições HTTP diretas com a biblioteca requests. Este guia cobre desde a configuração da chave de API até a execução de scripts completos para adicionar e validar registros.

1. Pré-requisitos e Configuração do Ambiente

Antes de escrever qualquer código, é necessário garantir que o ambiente esteja preparado e que as permissões adequadas estejam configuradas no painel do DigitalOcean. A segurança é primordial; portanto, nunca compartilhe suas chaves de API em repositórios públicos.

1.1 Geração da Token de API

Acessamos o painel de controle do DigitalOcean e navegamos até a seção API no menu lateral (ou em Settings > API). Clique em Generate New Token.

  • Dê um nome descritivo ao token, como dns-automation-script.
  • Selecione o escopo de permissão: Write. O acesso apenas de leitura (Read) não permitirá a criação ou modificação de registros DNS.
  • Copie o token gerado imediatamente. Ele será exibido apenas uma vez.

Armazene essa chave em uma variável de ambiente segura ou utilize um gerenciador de segredos como o HashiCorp Vault ou o AWS Secrets Manager. Para fins deste tutorial, utilizaremos a variável de ambiente DIGITALOCEAN_TOKEN.

1.2 Instalação das Dependências Python

Criamos um diretório para o projeto e inicializamos um ambiente virtual para isolar as dependências:

mkdir do-dns-automation
cd do-dns-automation
python3 -m venv venv
source venv/bin/activate  # No Windows: venv\Scripts\activate

Instalamos a biblioteca oficial do DigitalOcean e a biblioteca requests, que será útil para demonstrar o método alternativo de requisições HTTP diretas:

pip install digitalocean requests

2. Automação via SDK Oficial (python-digitalocean)

A biblioteca python-digitalocean abstrai as chamadas HTTP, fornecendo objetos nativos para tokens, domínios e registros DNS. Este é o método recomendado para projetos que já utilizam outros recursos da DigitalOcean, como Droplets e Load Balancers.

2.1 Inicialização do Cliente

O primeiro passo no script é importar a classe DigitalOceanToken e autenticar a sessão. O SDK detecta automaticamente a variável de ambiente DIGITALOCEAN_TOKEN, mas podemos passá-la explicitamente para maior controle.

import digitalocean
from digitalocean import Token, Domain, Record

# Configuração da chave de API
api_token = os.environ.get('DIGITALOCEAN_TOKEN')
if not api_token:
    raise EnvironmentError("A variável DIGITALOCEAN_TOKEN não está definida.")

token = Token(token=api_token)

2.2 Listando Domínios Existentes

Antes de criar um registro, é crucial verificar se o domínio alvo já existe na sua conta DigitalOcean. Utilizamos a classe Domain para buscar os detalhes.

# Nome do domínio que desejamos gerenciar
domain_name = 'meudominio.com.br'

# Tenta obter o domínio
try:
    domain = Domain(token=token, name=domain_name)
    # Se a instância for criada sem erro de 404, o domínio existe
    print(f"Domínio {domain_name} encontrado. ID: {domain.id}")
except Exception as e:
    print(f"Erro ao acessar o domínio: {e}")

2.3 Criando um Registro DNS A (IPv4)

Agora, criamos a função para adicionar um registro do tipo A, que mapeia um nome de host para um endereço IPv4. Este é o cenário mais comum para apontar subdomínios para servidores web ou APIs.

def criar_registro_a(token, domain_name, record_name, ip_address):
    """
    Cria um registro DNS do tipo A no domínio especificado.
    """
    try:
        # Instancia o objeto de domínio para vincular o novo registro a ele
        domain = Domain(token=token, name=domain_name)
        
        # Define os parâmetros do registro
        record = Record(
            type='A',           # Tipo do registro
            name=record_name,   # Ex: 'www' ou 'api'
            data=ip_address,    # Endereço IP de destino (ex: '192.0.2.1')
            priority=None,      # Não se aplica a registros A
            port=None,          # Não se aplica a registros A
            ttl=3600,           # Tempo de vida em segundos (padrão seguro)
            weight=None,        # Não se aplica
            flags=None,         # Não se aplica
            tag=None            # Não se aplica
        )
        
        # Associa o registro ao domínio e cria na API
        record.domain = domain
        record.create_new()
        
        print(f"Sucesso! Registro A '{record_name}' criado para {ip_address}.")
        return True
        
    except digitalocean.Error as e:
        # Captura erros específicos da API, como duplicidade
        if 'already exists' in str(e):
            print(f"Aviso: O registro '{record_name}' já existe.")
        else:
            print(f"Erro ao criar registro: {e}")
        return False

# Exemplo de uso
criar_registro_a(token, domain_name, 'app', '203.0.113.50')

2.4 Atualizando um Registro Existente

Em cenários de CI/CD, onde o IP do servidor pode mudar (por exemplo, em instâncias EC2 ou Droplets com IPs dinâmicos), precisamos atualizar o registro existente em vez de criar um novo. O SDK permite carregar o registro atual e modificar seus atributos.

def atualizar_registro_a(token, domain_name, record_name, novo_ip):
    """
    Atualiza o IP de um registro DNS existente do tipo A.
    """
    try:
        # Busca os registros existentes no domínio
        records = domain.get_records()
        
        # Filtra o registro específico por nome e tipo
        alvo = None
        for rec in records:
            if rec.name == record_name and rec.type == 'A':
                alvo = rec
                break
        
        if not alvo:
            print(f"Registro '{record_name}' não encontrado para atualização.")
            return False
            
        # Modifica o campo 'data' (que contém o IP)
        alvo.data = novo_ip
        alvo.save()
        
        print(f"Sucesso! Registro '{record_name}' atualizado para {novo_ip}.")
        return True
        
    except digitalocean.Error as e:
        print(f"Erro ao atualizar registro: {e}")
        return False

3. Abordagem Alternativa: Requisições HTTP Diretas com Requests

Para ambientes onde a instalação de bibliotecas pesadas é restrita, ou para fins educacionais de como a API funciona internamente, podemos utilizar a biblioteca requests. Esta abordagem oferece maior controle sobre os cabeçalhos e o payload JSON.

3.1 Estrutura da Requisição

A API v2 do DigitalOcean opera em https://api.digitalocean.com/v2. Para criar um registro, enviamos uma requisição POST para /domains/{domain_id}/records.

import requests
import json

API_URL = "https://api.digitalocean.com/v2"

def criar_registro_http(domain_name, record_name, ip_address):
    """
    Cria um registro DNS usando requisições HTTP diretas.
    """
    token = os.environ.get('DIGITALOCEAN_TOKEN')
    
    # Cabeçalhos padrão para autenticação e formato JSON
    headers = {
        'Content-Type': 'application/json',
        'Authorization': f'Bearer {token}'
    }
    
    # Primeiro, precisamos do ID do domínio, pois a API prefere IDs em operações de criação
    url_busca = f"{API_URL}/domains/{domain_name}"
    response = requests.get(url_busca, headers=headers)
    
    if response.status_code != 200:
        print("Erro ao buscar domínio.")
        return False
        
    domain_data = response.json()
    domain_id = domain_data['domain']['id']
    
    # Payload JSON para criação do registro
    payload = {
        "type": "A",
        "name": record_name,
        "data": ip_address,
        "ttl": 3600
    }
    
    url_criacao = f"{API_URL}/domains/{domain_id}/records"
    response = requests.post(url_criacao, headers=headers, data=json.dumps(payload))
    
    if response.status_code == 201:
        print(f"Registro HTTP criado com sucesso para {record_name}.")
        return True
    else:
        print(f"Erro na criação HTTP: {response.text}")
        return False

A principal vantagem desta abordagem é a transparência. Ao inspecionar o objeto payload, você vê exatamente o que está sendo enviado ao servidor da DigitalOcean, o que facilita o debug de erros de validação JSON.

4. Validação e Boas Práticas em Produção

Apenas criar os registros não é suficiente. Em scripts de automação robustos, é necessário implementar verificações de integridade e tratamento de erros avançado.

4.1 Verificação de Propagação

Após a criação do registro DNS, o servidor DNS pode levar alguns segundos ou minutos para propagar a nova informação globalmente. Se seu script precisa testar a conectividade imediatamente após a criação, é recomendável implementar um loop de espera (retry logic) que verifica se o DNS resolveu corretamente antes de prosseguir.

import socket
import time

def verificar_dns_resolve(hostname, ip_esperado, tentativas=5, intervalo=10):
    """
    Verifica se o hostname resolve para o IP esperado.
    """
    for i in range(tentativas):
        try:
            # Tenta resolver o nome de host
            socket.gethostbyname(hostname)
            print(f"Tentativa {i+1}: DNS resolvido para {hostname}.")
            
            # Opcional: Verificar se o IP retornado corresponde ao esperado
            # Nota: gethostbyname retorna o primeiro IP, útil para registros A simples
            
            return True
        except socket.gaierror:
            print(f"Tentativa {i+1}: DNS ainda não propagado. Tentando novamente em {intervalo}s...")
            time.sleep(intervalo)
            
    print("Falha na verificação de propagação do DNS.")
    return False

4.2 Tratamento de Erros e Retries

A API da DigitalOcean possui limites de taxa (rate limits). Se seu script cria muitos registros rapidamente, você pode receber um erro 403 Too Many Requests. Utilize bibliotecas como tenacity para implementar retries automáticos com backoff exponencial.

from tenacity import retry, stop_after_attempt, wait_exponential

@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10))
def criar_registro_com_retry(token, domain_name, record_name, ip_address):
    # Lógica de criação aqui...
    pass

4.3 Segurança e Variáveis de Ambiente

Nunca hardcode a API Key no script. Utilize o módulo os.environ ou bibliotecas como python-dotenv para carregar credenciais de um arquivo .env local que não deve ser versionado no Git.

# Exemplo com python-dotenv
from dotenv import load_dotenv
import os

load_dotenv()
token = os.getenv('DIGITALOCEAN_TOKEN')

5. Conclusão e Próximos Passos

A automação de registros DNS via API do DigitalOcean é uma peça fundamental em pipelines de DevOps modernos. Ao utilizar Python, seja através do SDK oficial ou requisições HTTP diretas, ganhamos agilidade, consistência e a capacidade de integrar a gestão de infraestrutura diretamente em ferramentas de orquestração como Ansible, Terraform ou Jenkins.

Para ir além deste tutorial básico, considere os seguintes próximos passos:

  • Integração com Terraform: Utilize o provedor oficial do DigitalOcean no Terraform para gerenciar DNS de forma declarativa.
  • DNS Dinâmico (DDNS): Crie scripts que detectam mudanças de IP no seu servidor e atualizam os registros automaticamente via cron jobs.
  • Validação de Certificados SSL: Automatize a criação de registros TXT do tipo ACME Challenge para emissão automática de certificados Let's Encrypt.

Lembre-se sempre de revisar as permissões das suas chaves de API e monitorar o uso da API através do dashboard do DigitalOcean para garantir que sua automação está operando dentro dos limites esperados. A infraestrutura como código transforma a gestão de DNS de uma tarefa manual propensa a erros em um processo confiável e auditável.

Esse tutorial foi útil?

Comentários (0)

Seja o primeiro a comentar.

Deixe seu comentário

Seu comentário será analisado antes de ser publicado.

0/2000
WhatsApp