From 344710e831db597e29a354f6aa9662e2a623c55c Mon Sep 17 00:00:00 2001 From: Tobias Reisinger Date: Wed, 25 Sep 2024 01:10:59 +0200 Subject: [PATCH] Migrate dns part --- .env.example | 3 ++ .envrc | 1 + .gitignore | 4 ++ Makefile | 31 +++++++++++ README.md | 10 ++++ creds.json | 12 +++++ dns/default_records.js | 40 ++++++++++++++ dns/dkim.json | 7 +++ dns/functions.js | 64 ++++++++++++++++++++++ dns/services.json | 79 ++++++++++++++++++++++++++++ dnsconfig.js | 117 +++++++++++++++++++++++++++++++++++++++++ shell.nix | 9 ++++ 12 files changed, 377 insertions(+) create mode 100755 .env.example create mode 100644 .envrc create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.md create mode 100644 creds.json create mode 100644 dns/default_records.js create mode 100644 dns/dkim.json create mode 100644 dns/functions.js create mode 100644 dns/services.json create mode 100644 dnsconfig.js create mode 100644 shell.nix diff --git a/.env.example b/.env.example new file mode 100755 index 0000000..b8a233d --- /dev/null +++ b/.env.example @@ -0,0 +1,3 @@ +OVH_APP_KEY= +OVH_APP_SECRET_KEY= +OVH_CONSUMER_KEY= diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..1d953f4 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use nix diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..19594fd --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.env + +types-dnscontrol.d.ts +dns/hosts.json diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5747dd4 --- /dev/null +++ b/Makefile @@ -0,0 +1,31 @@ +SHELL := /bin/bash + +include .env +export + +DNS_OUTPUT = "dns/hosts.js" +SERVICES_OUTPUT = "services/inventory/group_vars/all/opentofu.yaml" + +$(DNS_OUTPUT): + cd opentofu && \ + tofu output --json \ + | jq 'with_entries(.value |= .value).hosts' \ + > ../dns/hosts.json + +$(SERVICES_OUTPUT): + cd opentofu && \ + tofu output --json \ + | yq -y '{opentofu: with_entries(.value |= .value)}' \ + > ../services/inventory/group_vars/all/opentofu.yaml + +outputs: $(DNS_OUTPUT) $(SERVICES_OUTPUT) + + +./types-dnscontrol.d.ts: + dnscontrol write-types + +dns: $(DNS_OUTPUT) ./types-dnscontrol.d.ts + dnscontrol push + +dns-check: $(DNS_OUTPUT) ./types-dnscontrol.d.ts + dnscontrol check-creds ovh diff --git a/README.md b/README.md new file mode 100644 index 0000000..3a3beea --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +## DNS records for serguzim.net + +### Get started + +- install [DNSControl](https://dnscontrol.org/) (direnv and shell.nix should help) +- create credentials for ovh: [Control panel - OVHcloud](https://www.ovh.com/auth/api/createToken?GET=/domain/zone/*&POST=/domain/zone/*&PUT=/domain/zone/*&DELETE=/domain/zone/*&GET=/domain/*&POST=/domain/*&PUT=/domain/*&POST=/domain/*/nameServers/update) + - alternative: follow instructions on dnscontrol: [OVH | DNSControl](https://docs.dnscontrol.org/provider/ovh#activation) +- enter credentials to ovh in .env file (copy from .env.example) +- check credentials with `make dns-check` +- run `make dns` diff --git a/creds.json b/creds.json new file mode 100644 index 0000000..42eff23 --- /dev/null +++ b/creds.json @@ -0,0 +1,12 @@ +{ + "bind": { + "TYPE": "BIND" + }, + "ovh": { + "TYPE": "OVH", + "app-key": "$OVH_APP_KEY", + "app-secret-key": "$OVH_APP_SECRET_KEY", + "consumer-key": "$OVH_CONSUMER_KEY", + "endpoint": "eu" + } +} diff --git a/dns/default_records.js b/dns/default_records.js new file mode 100644 index 0000000..e2af7b0 --- /dev/null +++ b/dns/default_records.js @@ -0,0 +1,40 @@ + +function mx_default(dkim) { + return [ + CNAME("autoconfig", "mail.serguzim.me."), + CNAME("autodiscover", "mail.serguzim.me."), + SRV("_autodiscover._tcp", 1, 1, 443, "mail.serguzim.me."), + + TXT("@", "v=spf1 mx -all"), + TXT("mail-ses", "v=spf1 include:amazonses.com -all"), + + TXT("_dmarc", "v=DMARC1; p=quarantine; rua=mailto:dmarcreports@serguzim.me; ruf=mailto:dmarcreports@serguzim.me; rf=afrf; sp=quarantine; fo=1; pct=100; ri=604800; adkim=r; aspf=r"), + TXT("dkim._domainkey", "v=DKIM1; k=rsa; t=s; s=email; p=" + dkim), + + TLSA("_25._tcp", 3, 1, 1, "e66a608a3ec459bda7fb1f2d500b8abeb78f2910f26641204b6bc454b8aa2a49"), + + MX("@", 10, "mail.serguzim.me."), + MX("*", 10, "mail.serguzim.me."), + MX("mail-ses", 10, "feedback-smtp.eu-north-1.amazonses.com.") + ]; +} + +function pgp_verify() { + return TXT("@", "openpgp4fpr:723B78C0BF8D8C721D2C4EEF41E544A54E2533B2"); +} + +function all_defaults(domain, add_pgp) { + var result = [ + collect_services(domain), + ]; + + if (add_pgp) { + result.push(pgp_verify()); + } + + if (dkim[domain]) { + result.push(mx_default(dkim[domain])); + } + + return result; +} diff --git a/dns/dkim.json b/dns/dkim.json new file mode 100644 index 0000000..2ee5c2a --- /dev/null +++ b/dns/dkim.json @@ -0,0 +1,7 @@ +{ + "serguzim.me": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDZGmMeVFSFu9fIpp22JYMtYGlSdvZQXZOhQGL4beHiOm2uoor7wL/2vrwVBuE87xNFD1Rd/wPOOPUrejAf5RvQUOptOtL+yJPlu/LJPsa3RAEeerXjWaIYPgD47DEUW1ibFHgP66j8e5wh0dB8fzvcMpl/yCCBoO7G+4eowmGJcwIDAQAB", + "msrg.cc": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVhsx0fUgWdV9q/93cmnWZCkvcyT/qLLgbUaFof1VrjIANyCNMFo0FgBQNJ60AOxh8SMfJcybhR5ArtriUC1cxnWhk428SmKqgDNzR+CDP1/9/lF3TlLVzzgALu+8XFzlnvrVFZtSORgvYW9bvyT2RbGY+2qYlUWqtxeqC3QlrqQIDAQAB", + "msvg.cc": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtrqMTg7H2Y54TjTsPV0HaysIZ6yRrnk1rBoIQ6x+MS+WqFdjVFyPdemdQh8T7D5dSM7uoSOicxYA8a9XuMJvYfQgHm7JX3WUQkotOMMGhjDmtc7om+cpQPB2seYZr0weT9ImHKPrL+3d987GCq8ia2Zj/fxmAoB5tAA6Mme1/63+ARHfM5yEFPefr3brDojN5QFLivtp9FXfkEjZfn6OIfS15lak/JqdbaF98GCRR/GEYn1UWfpH8nmMSVEhq/IueGsDc0Q+2hG6ey4HLGHBjdYRvr+qtvviYMB87iy+NA77kd7KddnmLqiOktdMJL7X/gEQjnd0+qPPTtqsLcn+TQIDAQAB", + + "reitanlage-oranienburg.de": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDHZIA9DOxg5vLg5RZG6b6G6MI2mv4tRiHfRd8lDJZg9gI6DdYGibznw9ljYktcJruWPwTHPz7dr/YgKuzzqOTWBsdfsi4yVQywGsbEfbx/kSbHMMUsxhrDBC1wMAf1G+c2DbmixBLhmFyyY74ekOsWjPRMd/CpPMKQhOyzj/TSMwIDAQAB" +} diff --git a/dns/functions.js b/dns/functions.js new file mode 100644 index 0000000..8d9f906 --- /dev/null +++ b/dns/functions.js @@ -0,0 +1,64 @@ +function service(target, domain, host, alias) { + return { + target: target, + domain: domain, + host: hosts[host], + alias: alias, + record: function() { + return my_host_record(this.target, this.resolve_host()); + }, + resolve_host: function() { + if (this.alias) { + return services[this.alias].resolve_host(); + } + return this.host; + } + }; +} + +function collect_services(domain) { + var result = []; + for (var key in services) { + var s = services[key]; + if (s.domain == domain) { + result.push(s.record()); + } + } + return result; +} + +function my_host_record(target, host) { + return [ + A(target, host.ipv4_address), + AAAA(target, host.ipv6_address) + ]; +} + +function verify_amazon_ses(dkims) { + var result = []; + for (var i in dkims) { + var my_dkim = dkims[i] + "._domainkey"; + var ses_dkim = dkims[i] + ".dkim.amazonses.com."; + result.push(CNAME(my_dkim, ses_dkim)); + } + return result; +} + +function acme_challenge(subd, target) { + var final_subd = "_acme-challenge"; + if (subd) { + final_subd += "." + subd; + } + + final_target = target + ".acme.serguzim.me."; + + return CNAME(final_subd, final_target); +} + +function verify_dmarc_reports(domains) { + var result = []; + for (d in domains) { + result.push(TXT(d + "._report._dmarc", "v=DMARC1")); + } + return result; +} diff --git a/dns/services.json b/dns/services.json new file mode 100644 index 0000000..d5a0d92 --- /dev/null +++ b/dns/services.json @@ -0,0 +1,79 @@ +{ + "*": { + "target": "*", + "domain": "serguzim.me", + "host": "node002" + }, + "coder": { + "target": "coder", + "domain": "serguzim.me", + "host": "node002" + }, + "coder-wildcard": { + "target": "*.coder", + "domain": "serguzim.me", + "alias": "coder" + }, + "faas": { + "target": "faas", + "domain": "serguzim.me", + "host": "node002" + }, + "mail": { + "target": "mail", + "domain": "serguzim.me", + "host": "node003" + }, + "matrix": { + "target": "matrix", + "domain": "serguzim.me", + "host": "node002" + }, + "registry": { + "target": "registry", + "domain": "serguzim.me", + "host": "node002" + }, + "s3": { + "target": "s3", + "domain": "serguzim.me", + "host": "node002" + }, + "s3-console": { + "target": "console.s3", + "domain": "serguzim.me", + "alias": "s3" + }, + "serguzim.me": { + "target": "@", + "domain": "serguzim.me", + "alias": "faas" + }, + + "matrix_msrg": { + "target": "matrix", + "domain": "msrg.cc", + "alias": "matrix" + }, + "link": { + "target": "@", + "domain": "msrg.cc", + "host": "node002" + }, + "link_msvg": { + "target": "@", + "domain": "msvg.cc", + "alias": "link" + }, + + "reitanlage": { + "target": "@", + "domain": "reitanlage-oranienburg.de", + "host": "node002" + }, + "reitanlage_www": { + "target": "www", + "domain": "reitanlage-oranienburg.de", + "alias": "reitanlage" + } +} diff --git a/dnsconfig.js b/dnsconfig.js new file mode 100644 index 0000000..b6ae90c --- /dev/null +++ b/dnsconfig.js @@ -0,0 +1,117 @@ +// @ts-check +/// + +require('dns/default_records.js'); +require('dns/functions.js'); +var dkim = require('dns/dkim.json'); +var hosts = require('dns/hosts.json'); +var services_json = require('dns/services.json'); + +var REG_OVH = NewRegistrar("ovh"); +var DSP_OVH = NewDnsProvider("ovh"); + +var services = {}; +for (var key in services_json) { + var s = services_json[key]; + services[key] = service(s.target, s.domain, s.host, s.alias); +} + + +// _ __ ___ _____ ____ _ ___ ___ +// | '_ ` _ \/ __\ \ / / _` | / __/ __| +// | | | | | \__ \\ V / (_| || (_| (__ +// |_| |_| |_|___/ \_/ \__, (_)___\___| +// |___/ +D("msvg.cc", REG_OVH, DnsProvider(DSP_OVH), + all_defaults("msvg.cc", true) +); + + +// _ __ ___ ___ _ __ __ _ ___ ___ +// | '_ ` _ \/ __| '__/ _` | / __/ __| +// | | | | | \__ \ | | (_| || (_| (__ +// |_| |_| |_|___/_| \__, (_)___\___| +// |___/ +D("msrg.cc", REG_OVH, DnsProvider(DSP_OVH), + all_defaults("msrg.cc", true), + + acme_challenge(null, "11974523-79f9-4bd2-a776-33c26ded361f"), + + // Matrix + SRV("_matrix._tcp", 1, 1, 8448, "matrix.msrg.cc."), + + // XMPP + SRV("_xmpp-server._tcp", 0, 1, 5269, "wiuwiu.de."), + SRV("_xmpp-client._tcp", 0, 1, 5222, "wiuwiu.de."), + SRV("_xmpps-client._tcp", 0, 10, 5223, "xmpps.wiuwiu.de."), + SRV("_xmpps-client._tcp", 0, 20, 443, "xmpps.wiuwiu.de."), + TXT("xmppconnect", "_xmpp-client-xbosh=https://wiuwiu.de:443/http-bind/"), + + verify_amazon_ses([ + "rg6sgmw6fr73pucbqz62h25wq2q75iet", + "rjczvv7ab3twf6kfjjzmz5fkhpmyc2j5", + "bxau47qbno4igiwug2xrmwozzk6vwdyv" + ]), + + // SendGrid DKIM + CNAME("em2339.holitime", "u26197282.wl033.sendgrid.net."), + CNAME("s1._domainkey.holitime", "s1.domainkey.u26197282.wl033.sendgrid.net."), + CNAME("s2._domainkey.holitime", "s2.domainkey.u26197282.wl033.sendgrid.net.") +); + + +// _ +// ___ ___ _ __ __ _ _ _ ___(_)_ __ ___ _ __ ___ ___ +// / __|/ _ \ '__/ _` | | | |_ / | '_ ` _ \ | '_ ` _ \ / _ \ +// \__ \ __/ | | (_| | |_| |/ /| | | | | | |_| | | | | | __/ +// |___/\___|_| \__, |\__,_/___|_|_| |_| |_(_)_| |_| |_|\___| +// |___/ +D("serguzim.me", REG_OVH, DnsProvider(DSP_OVH), + all_defaults("serguzim.me", true), + + TLSA("_25._tcp.mail", 3, 1, 1, "e66a608a3ec459bda7fb1f2d500b8abeb78f2910f26641204b6bc454b8aa2a49"), + + acme_challenge("coder", "92924f7c-0859-4941-9e3d-2ecedfb21c1b"), + acme_challenge("db", "ca2c86c0-ff3d-458a-89e0-11bcfd2543e4"), + acme_challenge("registry", "18a42983-3d19-4c17-8213-fc275a8be721"), + + verify_amazon_ses([ + "dd4g333vxgahaf3rh3dafdx6g7kq7t7z", + "tbqt7mluvomvsomaj7nuhvs2xl7hd6hg", + "tl2n3zn4jxjodumvqj4jqdavfxznivvd" + ]), + + verify_dmarc_reports([ + "msrg.cc", + "reitanlage-oranienburg.de" + ]), + + NS("acme", "node002.serguzim.net."), + + CNAME("db", "node002.vpn.serguzim.net."), + + // Other records + A("loetlabor", "141.23.124.187"), + A("ls", "62.141.37.39"), + A("test", "62.141.37.39"), + CNAME("cloud", "nx45221.your-storageshare.de.") +); + + +// _ _ _ _ _ _ +// _ __ ___(_) |_ __ _ _ __ | | __ _ __ _ ___ ___ _ __ __ _ _ __ (_) ___ _ __ | |__ _ _ _ __ __ _ __| | ___ +// | '__/ _ \ | __/ _` | '_ \| |/ _` |/ _` |/ _ \_____ / _ \| '__/ _` | '_ \| |/ _ \ '_ \| '_ \| | | | '__/ _` | / _` |/ _ \ +// | | | __/ | || (_| | | | | | (_| | (_| | __/_____| (_) | | | (_| | | | | | __/ | | | |_) | |_| | | | (_| || (_| | __/ +// |_| \___|_|\__\__,_|_| |_|_|\__,_|\__, |\___| \___/|_| \__,_|_| |_|_|\___|_| |_|_.__/ \__,_|_| \__, (_)__,_|\___| +// |___/ |___/ +D("reitanlage-oranienburg.de", REG_OVH, DnsProvider(DSP_OVH), + all_defaults("reitanlage-oranienburg.de", false), + + verify_amazon_ses([ + "kseozkz37py4ukzg2h2kx7bua5r4yv2v", + "py2qx6nrsfn7r5j4uwwfpc7tgi63u7sn", + "hzmi7t2qcycinuy5edmo5uphohgtkefa" + ]), + + TXT("default._bimi", "v=BIMI1; l=https://www.reitanlage-oranienburg.de/user/themes/reitanlage-oranienburg/images/bimi.svg") +); diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..30d9ee0 --- /dev/null +++ b/shell.nix @@ -0,0 +1,9 @@ +with import {}; +mkShell { + nativeBuildInputs = [ + ansible + ansible-lint + dnscontrol + opentofu + ]; +}