Cum ziceam și în prima postare, mă uitam zilele astea peste soluții IaC pentru DNS, aflând inițial de octoDNS. Nu prea mi-a plăcut așa mult așa că am mai căutat și am dat de dnscontrol, dezvoltat aparent de cei de la Stack Exchange, care arată mai bine și avea o documentație mai bună.
Setup
În documentația lor au 4 variante de instalare, dar eu am mers direct pe varianta cu Docker pentru că voiam ceva simplu pe care îl puteam integra cu un pipeline de CI în Gitlab.
M-am uitat puțin peste ce provideri suportă și am aflat că suportă chiar și registrari, dar din păcate niciunul de acolo nu vinde domenii .ro
. M-am dus după pe pagina aferentă Cloudflare și am creat întâi creds.json
pentru credențiale (care probabil va fi un secret în pipeline-ul de CI):
{
"cloudflare": {
"TYPE": "CLOUDFLAREAPI",
"accountid": "your-cloudflare-account-id",
"apitoken": "your-cloudflare-api-token"
}
}
Am creat un API token destul de simplu (aveau și unele permisiuni opționale pt Page Rules sau Single Redirect, features pe care nu le folosesc):

Apoi am făcut un fișier dnsconfig.js
de test, să văd că sunt bune credențialele:
var REG_NONE = NewRegistrar("none");
var DSP_CLOUDFLARE = NewDnsProvider("cloudflare");
D("chitzu.ro", REG_NONE, DnsProvider(DSP_CLOUDFLARE),
A("test","1.2.3.13"),
);
Și am rulat imaginea de docker să văd cum arată:
$ docker run --rm -it -v "$(pwd):/dns" ghcr.io/stackexchange/dnscontrol preview
CONCURRENTLY gathering 1 zone(s)
SERIALLY gathering 0 zone(s)
Waiting for concurrent gathering(s) to complete...DONE
******************** Domain: chitzu.ro
11 corrections (cloudflare)
#1: - DELETE chitzu.ro A 158.180.26.241 proxy=false ttl=3600 id=8c08ff55b5f3f00a6fb64ec2789a58a3
#2: - DELETE chitzu.ro MX 10 mail.chitzu.ro. ttl=300 id=57acbe94735b077f1db559bb9bb2afe3
#3: - DELETE chitzu.ro TXT "v=spf1 mx a:mail.chitzu.ro -all" ttl=1 id=4ab36ab9df3cb898dc450a7b6a995a44
#4: - DELETE _dmarc.chitzu.ro TXT "v=DMARC1; p=reject; rua=mailto:dmarc@chitzu.ro; fo=1" ttl=1 id=e30ae8ae75bb1a29914dbc57314d0864
#5: - DELETE mail._domainkey.chitzu.ro TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAh8Y/U03AYdCDEOkfHWPYqrH6a/JG9vhOGGFjHGTyIJaPW8lg10URpYdSJs2tVSBP4B/d/tNDocjTJMRABgKksk5OmVBFWSxg3RrV8GRTFkkrl3wMARXGT4LQy7n7K401S+qAYhZvPSww1S9cEmwO5EzNGcycJLEetCze2IqLDBrxjqmr57p8kmaFhdETo5H7B" "uKc2GYUE89YC26f2oz4MV85p9vLQZoXko2i3BdFHHRbFuB+6vbiDzjNLTHsatSww6UqxC6zUNr7y0eAy/x/1ZPGwiT2kQvnjSqSkcq+l8o0iB0AkqEDes6LAlEDY3C+tV3Jptkk2gmbAjZ5SW6VdwIDAQAB" ttl=1 id=d5ef5457dc6d48ee6a8c691e1fc1b98d
#6: - DELETE _minecraft._tcp.chitzu.ro SRV 0 0 25565 mc.chitzu.ro. ttl=1 id=94c5f43b377086c6045270d6f81fcdf5
#7: - DELETE blog.chitzu.ro A 158.180.26.241 proxy=false ttl=1 id=eb8f899f0fc01a8f74350133356f0c2c
#8: - DELETE chat.chitzu.ro A 158.180.26.241 proxy=false ttl=1 id=e24b086ea98a3f74efef4680b05ba1c2
#9: - DELETE gitlab.chitzu.ro A 158.180.26.241 proxy=false ttl=1 id=837f26e08d901c5cf15b1c464cc1aca8
#10: - DELETE mail.chitzu.ro A 89.168.112.28 proxy=false ttl=1 id=18f0de442853a57301e9dcc12660c819
#11: + CREATE test.chitzu.ro A 1.2.3.13 proxy=false ttl=300
Done. 11 corrections.
Deci totul merge ok, mai rămâne doar să convertesc ce am în Cloudflare în JSON pentru dnsconfig.js
. După câteva minute bune de troubleshooting și scris de mână, am ajuns la asta:
var REG_NONE = NewRegistrar("none");
var DSP_CLOUDFLARE = NewDnsProvider("cloudflare");
D("chitzu.ro", REG_NONE, DnsProvider(DSP_CLOUDFLARE),
A("@", "158.180.26.241", TTL(3600)),
MX("@", 10, "mail", TTL(300)),
TXT("@", "v=spf1 mx a:mail.chitzu.ro -all", TTL(1)),
TXT("_dmarc", "v=DMARC1; p=reject; rua=mailto:dmarc@chitzu.ro; fo=1", TTL(1)),
TXT("mail._domainkey", ["v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAh8Y/U03AYdCDEOkfHWPYqrH6a/JG9vhOGGFjHGTyIJaPW8lg10URpYdSJs2tVSBP4B/d/tNDocjTJMRABgKksk5OmVBFWSxg3RrV8GRTFkkrl3wMARXGT4LQy7n7K401S+qAYhZvPSww1S9cEmwO5EzNGcycJLEetCze2IqLDBrxjqmr57p8kmaFhdETo5H7B", "uKc2GYUE89YC26f2oz4MV85p9vLQZoXko2i3BdFHHRbFuB+6vbiDzjNLTHsatSww6UqxC6zUNr7y0eAy/x/1ZPGwiT2kQvnjSqSkcq+l8o0iB0AkqEDes6LAlEDY3C+tV3Jptkk2gmbAjZ5SW6VdwIDAQAB"], TTL(1)),
SRV("_minecraft._tcp", 0, 0, 25565, "mc.chitzu.ro.", TTL(1)),
A("blog", "158.180.26.241", TTL(1)),
A("chat", "158.180.26.241", TTL(1)),
A("gitlab", "158.180.26.241", TTL(1)),
A("mail", "89.168.112.28", TTL(1))
);
Am rulat din nou preview și e totul bine:
$ docker run --rm -it -v "$(pwd):/dns" ghcr.io/stackexchange/dnscontrol preview
CONCURRENTLY gathering 1 zone(s)
SERIALLY gathering 0 zone(s)
Waiting for concurrent gathering(s) to complete...DONE
******************** Domain: chitzu.ro
Done. 0 corrections.
Acum vine partea de CI. În documentație au o parte foarte bine scrisă despre CI/CD pentru Gitlab după care m-am luat și eu, cu mici diferențe. Am ales să nu pun creds.json
în repository, în schimb adăugându-l ca variabilă în Gitlab. Inițial voiam să adaug ca file variable, doar că nu permite modul Visibility de "Masked and hidden" din cauza formatului JSON.

Așa că am encodat JSON-ul în base64 și îl voi decoda în pipeline. Urmărind în continuare documentația, au pus un exemplu foarte bun de pipeline pe care l-am folosit și eu cu mici modificări:
.dnscontrol:
image:
name: 'stackexchange/dnscontrol'
entrypoint: ['']
before_script:
- '/usr/local/bin/dnscontrol version'
- 'echo $DNSCONTROL_CREDS | base64 -d > creds.json'
dnscontrol-preview:
extends: '.dnscontrol'
stage: 'test'
script:
- '/usr/local/bin/dnscontrol check'
- '/usr/local/bin/dnscontrol preview'
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
changes:
- 'dnsconfig.js'
dnscontrol-push:
extends: '.dnscontrol'
stage: 'deploy'
script:
- '/usr/local/bin/dnscontrol push'
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
Ce am făcut diferit e să creez fișierul creds.json
și să nu folosesc && $CI_PIPELINE_SOURCE == "web"
, pentru că dacă trece PR-ul și ceva nu e bine, pot oricând să dau revert. Am dat push (direct în main, încă nu e protected, voiam să testez întâi) și a mers cum mă așteptam.

Acum ar trebui să fie mult mai simplu de editat, urmărit și reparat DNS-ul din Cloudflare.