DNS dinámico propio con DigitalOcean

Recientemente tuve la necesidad de apuntar un dominio a la red de mi casa, pues muchas veces me conecto remotamente a mi servidor bastión y salto entre servidores dentro de mi red para tareas de mantenimiento y otras cosas.

Como mi proveedor de internet, Cableonda, ofrece internet (miserable por el precio) a través de IP dinámica, pensé en pedir una IP estática, pues de este modo apuntaba el dominio a la IP y no tenía que hacer más nada. Comentando esto a soporte de ellos, me indican que tiene un costo de 10$ mensuales adicionales a la facturación. Así que después de considerar un aumento de 120$ anuales + ITBMS, pensé que sería mejor utilizar un servicio de DNS dinámico, pues al fin y al cabo, existen varios gratuitos.

Investigando me topé un montón de guías, al final termine eligiendo un proceso mucho más simple. Un script bash.

Primero creamos el nombre de dominio que necesitamos en el tablero de DigitalOcean en Manage > Networking > el dominio que deseamos. Y añadimos el registro.

Añadiendo el registro

Si hacemos ping al dominio luego de crearlo con esos datos, tendremos una respuesta así:

1
2
3
4
5
usuario@host:~$ ping home.dominio.com
PING home.dominio.com (192.168.1.1) 56(84) bytes of data.
64 bytes from USG (192.168.1.1): icmp_seq=1 ttl=64 time=0.210 ms
64 bytes from USG (192.168.1.1): icmp_seq=2 ttl=64 time=0.280 ms
64 bytes from USG (192.168.1.1): icmp_seq=3 ttl=64 time=0.257 ms

Ahora necesitamos nuestro token personal de la APIv2 de DigitalOcean. Vamos a nuestro Dashboard y luego a Manage > API.

Panel de administración de la API de DigitalOcean
Creando nombre del token
Token creado

Una vez hemos hecho el proceso y copiado el token personal, estamos listos para el script. Podemos verlo aquí. Solo clonamos el repositorio.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#!/usr/bin/env bash
[ ! -f ./secrets ] && \
  echo 'secrets file is missing!' && \
  exit 1
source ./secrets
# Exit if the RECORD_IDS array has no elements
[ ${#RECORD_IDS[@]} -eq 0 ] && \
  echo 'RECORD_IDS are missing!' && \
  exit 1
public_ip=$(curl -s http://checkip.amazonaws.com/)
for ID in "${RECORD_IDS[@]}"; do
  local_ip=$(
    curl \
      --fail \
      --silent \
      -X GET \
      -H "Content-Type: application/json" \
      -H "Authorization: Bearer ${ACCESS_TOKEN}" \
      "https://api.digitalocean.com/v2/domains/${DOMAIN}/records/${ID}" | \
      grep -Eo '"data":".*?"' | \
      grep -Eo '\d+\.\d+\.\d+\.\d+'
  )
  # if the IPs are the same just exit
  [ "$local_ip" == "$public_ip" ] && exit 0
  # --fail silently on server errors
  curl \
    --fail \
    --silent \
    --output /dev/null \
    -X PUT \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer ${ACCESS_TOKEN}" \
    -d "{"data": "${public_ip}"}" \
    "https://api.digitalocean.com/v2/domains/${DOMAIN}/records/${ID}"
done

Añadimos nuestro token personal en el archivo secrets en la línea ACCESS_TOKEN y el dominio del cual vamos a editar el registro en DOMAIN. Le damos permiso de ejecución a los scripts.

1
2
$ chmod +x update-dns.sh
$ chmod +x get-dns.sh

Solo nos falta el RECORD_ID, el cual lo podemos obtener con el script get-dns.sh:

1
$ ./get-dns.sh

Obtendremos una respuesta parecida a esta:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
{
  "domain_records": [
    {
      "id": 28448429,
      "type": "NS",
      "name": "@",
      "data": "ns1.digitalocean.com",
      "priority": null,
      "port": null,
      "ttl": 1800,
      "weight": null,
      "flags": null,
      "tag": null
    },
    {
      "id": 28448430,
      "type": "NS",
      "name": "@",
      "data": "ns2.digitalocean.com",
      "priority": null,
      "port": null,
      "ttl": 1800,
      "weight": null,
      "flags": null,
      "tag": null
    },
    {
      "id": 28448431,
      "type": "NS",
      "name": "@",
      "data": "ns3.digitalocean.com",
      "priority": null,
      "port": null,
      "ttl": 1800,
      "weight": null,
      "flags": null,
      "tag": null
    },
    {
      "id": 28448432,
      "type": "A",
      "name": "home",
      "data": "192.168.1.1",
      "priority": null,
      "port": null,
      "ttl": 1800,
      "weight": null,
      "flags": null,
      "tag": null
    }
  ],
  "links": {
  },
  "meta": {
    "total": 4
  }
}

Ahora el registro que vamos a editar tiene como ID, 28448432 como se ve en la línea 40. Este es el dato que añadiremos al archivo secrets. Ahora podremos ejecutar el script:

1
$ ./update-dns.sh

Inmediatamente en el tablero de DigitalOcean, en el dominio veremos la actualización.

Dominio actualizado

Y si hacemos ping al dominio, veremos la respuesta siempre y cuando ya se haya propagado, cosa que podría tardar de algunos minutos a horas.

1
2
3
4
5
usuario@host:~$ ping home.dominio.com
PING home.dominio.com (190.219.162.100) 56(84) bytes of data.
64 bytes from 190.219.162.109 (190.219.162.100): icmp_seq=1 ttl=64 time=0.595 ms
64 bytes from 190.219.162.109 (190.219.162.100): icmp_seq=2 ttl=64 time=0.582 ms
64 bytes from 190.219.162.109 (190.219.162.100): icmp_seq=3 ttl=64 time=0.597 ms

Por último, tengo dos opciones. Añadirlo al USG y ejecutarlo con cron o utilizarlo en el bastión del mismo modo ejecutado por cron. Editamos nuestro crontab con crontab -e y añadimos esta línea.

1
0 * * * * /home/usuario/scripts/update-dns.sh > /dev/null 2>&1

De manera tal que el actualizador de DNS se ejecuta cada hora a la hora en punto.

Si deseamos saber si ha ejecutado la tarea, después de unas horas, verificamos el archivo /var/log/syslog

1
$ sudo cat /var/log/syslog | grep CRON

Y tendremos una salida como esta, dependiendo de cuantas veces se haya ejecutado la tarea:

1
2
3
4
Feb 20 13:00:01 host CRON[23912]: (user) CMD (/home/user/scripts/update-dns.sh > /dev/null 2>&1)
Feb 20 14:00:01 host CRON[30157]: (user) CMD (/home/user/scripts/update-dns.sh > /dev/null 2>&1)
Feb 20 15:00:01 host CRON[7474]: (user) CMD (/home/user/scripts/update-dns.sh > /dev/null 2>&1)
Feb 20 16:00:01 host CRON[6428]: (user) CMD (/home/user/scripts/update-dns.sh > /dev/null 2>&1)

En conclusión, tengo un subdominio que apunta a mi red en casa con su ip actualizada cuando hay un cambio y realiza comprobaciones cada hora. Muy útil para evitar pagar de más por una ip fija.


Moisés Serrano Samudio Médico de atención primaria, fotógrafo aficionado, apasionado de las tecnologías relacionadas con el EdTech y el eHealth y diseñador/desarrollador de sitios web de salud. Médico, apasionado del EdTech/eHealth y diseñador/desarrollador de sitios web de salud.
Moisés Serrano Samudio

@linkmoises

Médico de atención primaria, fotógrafo aficionado, apasionado de las tecnologías relacionadas con el EdTech y el eHealth.

Entradas relacionadas

Comentarios

Deja una respuesta

Su email no será publicado. Required fields are marked *