After personal Email server the next thing to setup is a Domain Name Server (DNS) server for costan․ro domain with DNSSEC, TLS and everything in between.
1. Authoritative DNS with NSD
I know Bind but this is also a bit old-fashion to me; more than that, I believe in SRP and since Bind is both caching and authoritative name server, I prefer NSD which is authoritative only server, lighter and newer.
1.1 Installation
Just install nsd and ldnsutils package.
pacman -S nsd ldnsutils
1.2 Configuration
Daemon
grep -v -e '^[[:space:]]*#' -e '^[[:space:]]*$' /etc/nsd/nsd.conf
server: verbosity: 2 username: "nsd" zonesdir: "//etc/nsd/zones" hide-version: yes statistics: 86400 refuse-any: yes verify: enable: yes remote-control: control-enable: yes zone: name: "costan.ro" zonefile: "costan.ro"
Non-default configuration is pretty basic:
- server: username, zonesdir for daemon to chdir into; verbosity, statistics to show in logs once a day; and last hide-version, refuse-any minimal security settings.
- verify: to verify each zone
- remote-control: to locally control the daemon via nsd-control tool.
- zone: zone for my personal domain
Zone
cat /etc/nsd/zones/costan.ro
;## NSD authoritative only DNS ;## FORWARD Zone - costan.ro $ORIGIN costan.ro. ; default zone domain $TTL 3600 ; default time to live - 1h @ IN SOA ns.costan.ro. admin.costan.ro. ( 2022072902 ; Serial number 3600 ; Refresh - 1h 600 ; Retry - 10m 1209600 ; Expire - 2w 3600 ; Min TTL - 1h ) @ IN NS ns.costan.ro. @ IN MX 5 smtp.costan.ro. @ IN A 86.124.145.184 ns IN A 86.124.145.184 smtp IN A 86.124.145.184 rig IN A 86.124.145.184 blog IN CNAME icostan.gitlab.io. deribase IN CNAME rig.costan.ro. gasherbrum2 IN CNAME dimensional-ant-eqt6rarkgtemacbmeid10exz.herokudns.com. huveragy IN CNAME icostan.github.io. imap IN CNAME rig.costan.ro. mail IN CNAME rig.costan.ro. www IN CNAME rig.costan.ro. @ IN TXT "v=spf1 a mx ip4:86.124.145.184 ~all" smtp IN TXT "v=spf1 a mx ip4:86.124.145.184 ~all" _dmarc IN TXT "v=DMARC1; p=quarantine; rua=mailto:postmaster@costan.ro; ruf=mailto:forensic@costan.ro; adkim=s; aspf=s; fo=1; pct=25" rig._domainkey IN TXT "v=DKIM1; k=rsa; s=email; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDQFlti46dceD5rk3+RGnoYStK6np+cIucrOrkMHbjoRLcOxNikOfi0ABgG2CxK/0X+VNmiL5PsaWWnXhYGOJWz82LM0zhDzoD1bQ0OIb/PWyPMz22udwnPa6FRypEEnjAdC6c8g7tX8fMovqX/09PHKKjLq4zX0X3CMT+t3QhXlQIDAQAB" _gitlab-pages-verification-code.blog IN TXT "gitlab-pages-verification-code=3b0fe70a3b7910b2760d02227529e240" _acme-challenge IN TXT "PDFYVU1v3Jlo6Yeo50-LHiokC8PwNeZl_-FCz13CKPs" ;## NSD authoritative only DNS ;## FORWARD Zone - costan.ro
1.3 Tuning
Enable TCP Fast Open as suggested by NSD daemon on start.
echo "net.ipv4.tcp_fastopen=2" > /etc/sysctl.d/30-nsd.conf
1.4 Service
Start/enable the daemon, allow port 53 in firewall and this is it.
systemctl start nsd
systemctl enable nsd
ufw limit "DNS"
1.5 Testing
The easiest test is to query the DNS server directly:
drill rig.costan.ro @ns.costan.ro
;; ->>HEADER<<- opcode: QUERY, rcode: NOERROR, id: 11080 ;; flags: qr rd ra ; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;; rig.costan.ro. IN A ;; ANSWER SECTION: rig.costan.ro. 3555 IN A 86.124.145.184 ;; AUTHORITY SECTION: ;; ADDITIONAL SECTION: ;; Query time: 1 msec ;; SERVER: 86.124.145.184 ;; WHEN: Sun Aug 28 11:25:50 2022 ;; MSG SIZE rcvd: 47
Or use these 3rd party tools to look deeper into DNS config and security.
2. Optional (highly recommended) DNS extensions
By default DNS protocol is old and pretty basic, just a giant hashmap that maps a domain name (e.g. costan.ro) to an IP address (e.g. 86.124.145.184) but there are other extensions for data integrity, privacy, etc.
2.1 DNSSEC for authentication and integrity
Generate ZSK keypair
This will be used to sign the zone file and for now I am OK with RSASHA256 standard algorithm, see RSA vs ECDSA in DNSSEC for details.
ldns-keygen -a RSASHA256 -b 1024 costan.ro
ls -l /etc/nsd/zones/Kcostan.ro*
-rw-r--r-- 1 nsd nsd 239 Jul 29 11:48 /etc/nsd/zones/Kcostan.ro.+008+03304.key -rw------- 1 root root 939 Jul 29 11:48 /etc/nsd/zones/Kcostan.ro.+008+03304.private
Generate KSK keypair
This will be used to sign the key file generated above, notice larger keysize for stronger security.
ldns-keygen -k -a RSASHA256 -b 2048 costan.ro
ls -l /etc/nsd/zones/Kcostan.ro*
-rw-r--r-- 1 nsd nsd 239 Jul 29 11:48 /etc/nsd/zones/Kcostan.ro.+008+03304.key -rw------- 1 root root 939 Jul 29 11:48 /etc/nsd/zones/Kcostan.ro.+008+03304.private -rw-r--r-- 1 nsd nsd 97 Jul 29 12:40 /etc/nsd/zones/Kcostan.ro.+008+19957.ds -rw-r--r-- 1 nsd nsd 412 Jul 29 11:49 /etc/nsd/zones/Kcostan.ro.+008+19957.key -rw------- 1 root root 1703 Jul 29 11:49 /etc/nsd/zones/Kcostan.ro.+008+19957.private
Sign zone
This will digitally sign each record in plaintext zone and output the signed zone file plus the public keys above.
ldns-signzone -n -p -i 20220729174119 -e 20230729174119 costan.ro Kcostan.ro.+008+03304 Kcostan.ro.+008+19957
ls -l /etc/nsd/zones/costan.ro*
-rw-r--r-- 1 nsd nsd 1773 Jul 29 20:39 /etc/nsd/zones/costan.ro -rw-r--r-- 1 nsd nsd 13869 Aug 28 11:08 /etc/nsd/zones/costan.ro.signed
Configuration
Update nsd.conf file to use the signed zone file instead of the plaintext one.
grep -B 2 costan.ro.signed /etc/nsd/nsd.conf
zone: name: "costan.ro" zonefile: "costan.ro.signed"
Service
Trigger daemon reconfig and reload zone file.
nsd-control reconfig
nsd-control reload costan.ro
Test DNSSEC keys
After reload we need to check if DNSKEY records are published and propagated. These are the public ZSK, KSK keys used by DNS resolvers to verify and authenticate the records.
drill -D costan.ro DNSKEY
;; ->>HEADER<<- opcode: QUERY, rcode: NOERROR, id: 3888 ;; flags: qr rd ra ad ; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;; costan.ro. IN DNSKEY ;; ANSWER SECTION: costan.ro. 3564 IN DNSKEY 257 3 8 AwEAAeM6ahMDg1TJ2enWZGaZxMarqrdZIqGm0xqnqR/4rr1LFYlY9M9cgHpLx++sqFPH6OWfbP/P5L8Y9k1GWHLp68HKRSuGljlVaKlStoauk+PCk83SNbp0btJQdFSqzuxNOPppMrhthd4yHsIGzTwy2h+qkyT/EYReV+IwAISvw9PJH3xj7XtG+3mvrs/WqrXqfXb4y1+jzbv3GJL2RCsDpUM3Cut3QTNrjqTJsc48wz/wu0HvXAnlCnyLTL2fJ69Bjf4hFJaiggvje2cTWxWixdUjiSPuBWRQcu/H5konkxtqV4eZR8DiLy7+mKZZUkKMPxTCUZ50qgtmNdLlRNs28zk= ;{id = 19957 (ksk), size = 2048b} costan.ro. 3564 IN DNSKEY 256 3 8 AwEAAckIPA6ENdhhPjlKEJo/57IC7MzfcuWRkS40wXKSKNh8nZyYVg9K92Kr5SgAD1kSAnaE4eFTOXZgYBE97eS6lBcljw0iWyPOkQZXaatSCduHCIrMbSg7xGjXeQzAiD8YOVbS4X0881h3Gi919zmiZ5tDTmNpHfxAKabEJXv6IfYL ;{id = 3304 (zsk), size = 1024b} costan.ro. 3564 IN RRSIG DNSKEY 8 2 3600 20230729174119 20220729174119 19957 costan.ro. gbwkTvxq94RLUrn+JyQeEXNvs1g5ucF3b+UHsCTJX71oFw/nend/tbRpSyYG/V61YBXo5Z4qT+LazY1wwyaCw5lxz0gnm/ZWyXMAZPYwX0k4yRLhVGIs5sDxL+Qy4MPcvgze4APF+WSmCZBF3hqfiB0J/6in0BnoBPyJmPWd/vM5QL4hue1EmJuu2fgDLvCloUNPcIfVoFau32WjRYgMwTVZyLK22FE6edAPAuPXNvHFnsx0hIJlexdTWyGEGF2IzMEd46DwCzPXnjbYywTV/WSmSJssZT2kQF+uVS1No9PvSQve75KIyusGcT+UFiGbZvV3tH8fIeE0oFCcvYh8wA== ;; AUTHORITY SECTION: ;; ADDITIONAL SECTION: ;; Query time: 0 msec ;; EDNS: version 0; flags: do ; udp: 1232 ;; SERVER: 127.0.0.1 ;; WHEN: Sun Aug 28 11:20:15 2022 ;; MSG SIZE rcvd: 759
Chain of trust
Generate DS (Delegation Signer) record for our signed zone that allows:
- DNS resolvers know that my domain is DNSSEC-enabled.
- transfer of trust from a trusted parent zone (ro.) and my domain (costan.)
ldns-key2ds costan.ro.signed
cat /etc/nsd/zones/Kcostan.ro.+008+19957.ds
costan.ro. 3600 IN DS 19957 8 2 083b1f4914402506d842029241041cc5869fd91c1887f41fb73a832fc78bbb8c
Update DS record in registrar (rotld.ro is the top-level registrar for ro. TLD zone).
drill costan.ro DS
;; ->>HEADER<<- opcode: QUERY, rcode: NOERROR, id: 57649 ;; flags: qr rd ra ; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;; costan.ro. IN DS ;; ANSWER SECTION: costan.ro. 35005 IN DS 19957 8 2 083b1f4914402506d842029241041cc5869fd91c1887f41fb73a832fc78bbb8c ;; AUTHORITY SECTION: ;; ADDITIONAL SECTION: ;; Query time: 0 msec ;; SERVER: 127.0.0.1 ;; WHEN: Sun Aug 28 11:27:22 2022 ;; MSG SIZE rcvd: 75
Test DNSSEC
Use excellent 3rd party tools:
2.2 DNS over TLS for privacy
TBD: once I solve one of the following issues:
- automate wildcard certificate issuance with Let's Encrypt
- figure out and configure DANE
3. References
Articles
- https://wiki.archlinux.org/title/NSD
- https://wiki.archlinux.org/title/DNSSEC
- https://www.digitalocean.com/community/tutorials/how-to-use-nsd-an-authoritative-only-dns-server-on-ubuntu-14-04
- https://www.digitalocean.com/community/tutorials/how-to-set-up-dnssec-on-an-nsd-nameserver-on-ubuntu-14-04
- https://www.icann.org/resources/pages/dnssec-what-is-it-why-important-2019-03-05-en
- https://www.cloudflare.com/dns/dnssec/how-dnssec-works/
- https://docs.infoblox.com/space/NAG8/22252229/RRSIG+Resource+Records
- https://en.wikipedia.org/wiki/Domain_Name_System
- https://en.wikipedia.org/wiki/Domain_Name_System_Security_Extensions
- https://en.wikipedia.org/wiki/DNS_over_TLS
- https://en.wikipedia.org/wiki/DNS-based_Authentication_of_Named_Entities
- https://tools.cisco.com/security/center/resources/dns_best_practices
- https://blog.cloudflare.com/dnssec-an-introduction/#kaminskysattack
Test tools
Test tools for DNSSEC resolvers
Updates
- 2022-07-29 - initial blog post
- 2022-08-28 - increase sig expiry time, use drill instead of dig
- 2022-12-19 - acme challenge as TXT record, resign zone