Configurer un Dynamic DNS Cloudflare sous Debian avec systemd
Un DDNS maison sous Debian : script Bash, API Cloudflare et timer systemd pour garder un enregistrement A à jour malgré une IP publique dynamique.
Aussi disponible en English

Lorsqu’on utilise un ordinateur portable ou un serveur derrière une connexion Internet avec une adresse IP publique dynamique, l’adresse peut changer à tout moment.
L’objectif est donc simple :
arthur-perrot.example.com
↓
Adresse IPv4 publique actuelleLe PC vérifie régulièrement son IP publique et, lorsqu’elle change, met automatiquement à jour l’enregistrement DNS correspondant chez Cloudflare.
Dans ce guide, nous allons réaliser ce DDNS avec un simple script Bash, l’API Cloudflare et un timer systemd sous Debian.
Prérequis
Il faut :
- un domaine dont le DNS est géré par Cloudflare ;
- Debian ou une distribution utilisant systemd ;
curl;jq;- un token API Cloudflare.
Installation des dépendances :
sudo apt update
sudo apt install curl jqDans les exemples, nous utiliserons :
Zone DNS : example.com
Record : arthur-perrot.example.com1. Créer le token API Cloudflare
Depuis Cloudflare, créer un API Token ayant uniquement les permissions nécessaires sur la zone concernée.
Permissions recommandées :
Zone → DNS → Edit
Zone → Zone → ReadIl est préférable de limiter le token à une seule zone DNS plutôt que de lui donner accès à l’ensemble du compte.
On peut vérifier que le token fonctionne :
export CF_API_TOKEN='cfat_xxxxxxxxxxxxxxxxx'
curl -s \
-H "Authorization: Bearer $CF_API_TOKEN" \
"https://api.cloudflare.com/client/v4/zones?name=example.com" | jqCloudflare doit retourner notamment :
{
"success": true
}La réponse contient également l’identifiant de la zone :
"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"Conservez ce Zone ID, nous allons l’utiliser directement dans le script.
2. Créer l’enregistrement DNS
Créer dans Cloudflare un enregistrement A, par exemple :
Type : A
Name : arthur-perrot
IPv4 : 1.2.3.4
TTL : AutoL’adresse initiale importe peu : le script la remplacera lors de sa première exécution.
On pourra vérifier la résolution avec :
dig +short arthur-perrot.example.com @1.1.1.1Par exemple :
1.2.3.43. Créer le script DDNS
Créons notre script :
sudo nano /usr/local/bin/cloudflare-ddnsContenu :
#!/bin/bash
set -e
TOKEN="$CF_API_TOKEN"
ZONE_ID="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
RECORD="arthur-perrot.example.com"
# Récupération de l'IPv4 publique
IP=$(curl -fsS -4 https://api.ipify.org)
# Recherche de l'identifiant du record DNS
RECORD_ID=$(curl -fsS \
-H "Authorization: Bearer $TOKEN" \
"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?type=A&name=$RECORD" \
| jq -r '.result[0].id')
if [ "$RECORD_ID" = "null" ] || [ -z "$RECORD_ID" ]; then
echo "Record $RECORD introuvable"
exit 1
fi
# Récupération de l'adresse actuellement publiée
CURRENT=$(curl -fsS \
-H "Authorization: Bearer $TOKEN" \
"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$RECORD_ID" \
| jq -r '.result.content')
# Rien à faire si l'adresse n'a pas changé
if [ "$CURRENT" = "$IP" ]; then
echo "IP inchangée : $IP"
exit 0
fi
# Mise à jour du record Cloudflare
curl -fsS -X PATCH \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$RECORD_ID" \
--data "{\"content\":\"$IP\"}" \
| jq -e '.success' >/dev/null
echo "$RECORD : $CURRENT -> $IP"Rendre le script exécutable :
sudo chmod 700 /usr/local/bin/cloudflare-ddnsLe fonctionnement est volontairement simple :
Récupération de l'IP publique
↓
Lecture du record Cloudflare
↓
IP identique ?
↙ ↘
Oui Non
↓ ↓
Ne rien Mise à jour
faire Cloudflare4. Stocker le token séparément
Il est préférable de ne pas mettre le token Cloudflare directement dans le script.
Créer :
sudo nano /etc/cloudflare-ddns.envAvec :
CF_API_TOKEN=cfat_xxxxxxxxxxxxxxxxxRestreindre ensuite les permissions :
sudo chown root:root /etc/cloudflare-ddns.env
sudo chmod 600 /etc/cloudflare-ddns.envAinsi, seul root peut lire le token.
5. Créer le service systemd
Créer :
sudo nano /etc/systemd/system/cloudflare-ddns.serviceAvec :
[Unit]
Description=Cloudflare DDNS updater
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
EnvironmentFile=/etc/cloudflare-ddns.env
ExecStart=/usr/local/bin/cloudflare-ddnsPuis recharger systemd :
sudo systemctl daemon-reloadTestons le service manuellement :
sudo systemctl start cloudflare-ddnsPuis :
systemctl status cloudflare-ddnsUn résultat comme celui-ci indique que tout fonctionne :
Starting cloudflare-ddns.service - Cloudflare DDNS updater...
arthur-perrot.example.com : 1.2.3.4 -> 203.0.113.42
Deactivated successfully.
Finished cloudflare-ddns.service - Cloudflare DDNS updater.Lors des exécutions suivantes, si l’IP n’a pas changé :
IP inchangée : 203.0.113.42Il est normal que le service apparaisse ensuite comme :
inactive (dead)Il s’agit d’un service oneshot : il effectue son travail puis se termine.
6. Exécuter automatiquement le DDNS toutes les 5 minutes
Nous pouvons maintenant utiliser un timer systemd.
Créer :
sudo nano /etc/systemd/system/cloudflare-ddns.timerAvec :
[Unit]
Description=Update Cloudflare DDNS every 5 minutes
[Timer]
OnBootSec=30s
OnUnitActiveSec=5min
Persistent=true
[Install]
WantedBy=timers.targetActiver le timer :
sudo systemctl daemon-reload
sudo systemctl enable --now cloudflare-ddns.timerVérifier son état :
systemctl status cloudflare-ddns.timerOn doit obtenir :
Active: active (waiting)Pour connaître la prochaine exécution :
systemctl list-timers cloudflare-ddns.timerPar exemple :
NEXT LEFT UNIT
Tue 17:43:20 EDT 4min cloudflare-ddns.timer7. Vérifier les logs
Les exécutions du script sont enregistrées dans le journal systemd.
Afficher les dernières exécutions :
journalctl -u cloudflare-ddns.service -n 20Suivre les prochaines exécutions en direct :
journalctl -fu cloudflare-ddns.servicePour afficher uniquement les événements depuis le dernier démarrage :
journalctl -b -u cloudflare-ddns.service8. Vérifier après un reboot
Le timer étant activé avec :
sudo systemctl enable --now cloudflare-ddns.timeril redémarrera automatiquement avec Debian.
On peut vérifier :
systemctl is-enabled cloudflare-ddns.timer
systemctl is-active cloudflare-ddns.timerRésultat attendu :
enabled
activeEnviron 30 secondes après le démarrage, une première vérification sera effectuée. Ensuite, le script sera exécuté toutes les cinq minutes.
9. Vérifier le résultat
Afficher l’IPv4 publique :
curl -4 -s https://api.ipify.org
echoPuis vérifier le DNS :
dig +short arthur-perrot.example.com @1.1.1.1Les deux valeurs doivent correspondre :
203.0.113.42
203.0.113.42Le fonctionnement final est donc :
┌─────────────────┐
│ PC Debian │
└────────┬────────┘
│
toutes les 5 min
│
▼
IP publique actuelle
│
▼
Comparaison Cloudflare
│
┌────────┴────────┐
│ │
identique différente
│ │
▼ ▼
rien Cloudflare API
│
▼
arthur-perrot.example.com
│
▼
nouvelle IPv4 publiqueCette solution a l’avantage de n’utiliser aucun daemon DDNS supplémentaire : seulement curl, jq, quelques lignes de Bash et les mécanismes natifs de systemd.
Elle convient notamment à un laptop qui change régulièrement de réseau : domicile, partage de connexion, Wi-Fi extérieur, etc.
Il faut cependant garder à l’esprit que le DDNS ne contourne pas le NAT ou les restrictions du réseau. Le nom DNS pointera bien vers l’adresse IP publique utilisée par le laptop, mais cela ne garantit pas que les connexions entrantes vers celui-ci soient possibles, notamment derrière un CGNAT ou un réseau qui bloque les connexions entrantes.