DNS Server

A DNS server is not difficult to run, though can be complicated. Reasons to run your own DNS server include hosting your own domain(s), or for the edification. The domains could be ones delegated to you by some DNS provider, or local custom zones used only by your local systems. A DNS server can also provide blacklisting; for example, your DNS server could claim to be authoritative for bad sites such as twitter or x.com and reject all lookup requests for those domains. Anti-spam or blacklisting can also be done in a caching DNS server that is not authoritative for any zone, or in systems such as the pi-hole.

There are several implementations of DNS servers; some are authoritative only, caching only, or both. An authoritative server is required to host your own domains. Authoritative and caching roles probably should not be mixed, especially for a server exposed to the internet. In that case, the domains delegated to you probably should be hosted with an authoritative-only DNS server that no client system connects to directly. Clients should instead use some other caching-only DNS server. Multiple servers are generally run for redundancy, though if you are running your services on a single system or virtual host there is not much to be redundant about: if that system is down, so is gemini, SMTP, or any other services that you run in addition to DNS.

Authoritative Server

This guide concerns nsd(8) which ships with OpenBSD 7.3; different needs may need a different DNS server. nsd(8) is authoritative only, so is suitable for hosting domains: those delegated to you, or a custom local zone. Let's see how to setup a custom local zone.

One complication is that only one server can listen to port 53. This may conflict with caching DNS servers such as unwind(8) or unbound(8) that also listen on port 53. One solution is to simply disable those services on the authoritative DNS server. A virtual system or clunker laptop would be one option to avoid breaking the DNS configuration on a system that you actually need to work.

    $ netstat -ln | grep 53
    $ doas rcctl -f start nsd
    nsd(ok)
    $ netstat -ln | grep 53
    tcp          0      0  *.53               *.*                LISTEN
    udp          0      0  *.53               *.*
    tcp6         0      0  *.53               *.*                LISTEN
    udp6         0      0  *.53               *.*
    $ host example.org 127.0.0.1
    Using domain server:
    Name: 127.0.0.1
    Address: 127.0.0.1#53
    Aliases:

    Host example.org not found: 5(REFUSED)

Here we confirm nothing is using port 53, start nsd(8), and check that it can answer queries, even if it refuses them. That's a good sign at this point. A firewall might block queries, though localhost is typically not firewalled. The service has not yet been set to start at system boot.

Next up, configure /var/nsd/etc/nsd.conf to serve up an example zone. This will make the DNS server authoritative for the zone(s) in question.

    server:
            hide-version: no
            verbosity: 1
            database: "" # disable database

    remote-control:
            control-enable: yes
            control-interface: /var/run/nsd.sock

    zone:
            name: "example.org"
            zonefile: "master/example.org"

nsd.conf

A zone file is also necessary, /var/nsd/zones/master/example.com given the above and the OpenBSD defaults. The syntax is the same as for the BIND (Berkeley Internet Name Domain).

    $ORIGIN example.org
    $TTL 12345
    @       IN  SOA ns1.example.org.    root.example.org. (
    2023080200  ; serial (YYYYMMDDNN)
    3600        ; refresh (seconds)
    900     ; retry
    1814400     ; expire
    12345       ; TTL
    )
            IN  MX  mail.example.org.
            IN  NS  ns1.example.org.
            IN  A   127.0.0.1
    gem 42  IN  A   127.0.0.1
    mail        IN  A   127.0.0.1
    ns1     IN  A   127.0.0.1

example.txt

This configures the start of authority (SOA) record and a few other names. "root.example.org." is actually an email address; the first "." must be changed to an "@" so the address given is actually root@example.org. The trailing dot is pretty important to include on records. The serial number should only ever be increased. YYYYMMDDNN is one convention for the serial number, another is to start at 1 and increment by 1 with each zonefile change.

The TTL and other timeouts could be configured as you see fit. Some like lower TTL values than others. A low TTL value risks the record "going away" if clients cannot contact a DNS server for longer than the TTL. Github suffered an outage when someone took their upstream DNS servers offline long enough for the probably too low TTL to expire. High TTL values make it difficult to change a record that really does need to change in a hurry; with a high TTL clients may cache it for that long. How often and how quickly do you need to change IP addresses or other DNS records? Mostly I favor long TTL values, as mostly I run small sites where I do not want the site to vanish over a weekend if DNS should fall over, and I rarely if ever make significant IP address changes. If there is a migration, the TTL can be lowered in advance. Others will favor TTL that are too low.

For email on the internet various other records will be necessary, though that is not covered here (SPF, DKIM, etc). There are also CAA records and so forth if you want those complications.

    $ doas rcctl -f restart nsd
    nsd(ok)
    nsd(ok)
    $ host example.org 127.0.0.1
    Using domain server:
    Name: 127.0.0.1
    Address: 127.0.0.1#53
    Aliases:

    Host example.org not found: 2(SERVFAIL)

Whoops, had some typos in the zone file. Check /var/log/messages and if that does not help try increasing the verbosity of nsd.

    $ doas rcctl -f restart nsd
    ...
    $ host example.org 127.0.0.1
    Using domain server:
    Name: 127.0.0.1
    Address: 127.0.0.1#53
    Aliases:

    example.org has address 127.0.0.1
    example.org mail is handled by 10 ns1.example.org.

Mission accomplished! The server is now up, or almost up, test queries can be sent to it, and new records or TTL or whatnot can be tested with it.

    $ doas rcctl enable nsd

Well, almost. To actually use this server from a client system will require that a caching server is setup that forwards requests for suitable zones to this custom server. But that's a story for some other time... oh, and a public or even LAN facing DNS server probably needs to allow in requests for TCP/53 and UDP/53. But this is not a firewall guide.

A secondary DNS server might be good to setup; this provides redundancy and lets you practice zone transfers. However, these days configuration management could also put the right zone file onto all the DNS servers.

DNS Server Version

Mostly the server version information is turned off these days, as one may not want to give that information to attackers. The above nsd.conf enables it, which probably should not be done on a public server. Chaosnet is mostly a historical curiosity these days.

    $ grep CHAOS /etc/protocols
    chaos   16      CHAOS           # Chaos
    $ dig +short -c chaos -t txt @127.0.0.1 version.bind
    "NSD 4.6.1"
    $ dig +short -c chaos -t txt @1.1.1.1 version.bind
    $ dig +short -c chaos -t txt @8.8.8.8 version.bind
    $ dig +short -c chaos -t txt @192.168.0.1 version.bind
    "dnsmasq-2.89"

Zone File Updates

Be sure to increment the serial number. Maybe this should be handled automatically by having configuration management template the zone file and increment the number automatically, but that's more complicated, as is using a DNS server with a database backend, or using dynamic updates to change DNS records. Larger sites with more frequent changes need more automation; smaller sites can probably get by with editing a zone file.

example2.txt

    $ host -t SOA example.org 127.0.0.1 | grep SOA
    example.org has SOA record ns1.example.org. root.example.org. 2023080300 3600 900 1814400 12345
    $ host -t TXT example.org 127.0.0.1 | grep spf
    example.org descriptive text "v=spf1 mx -all"

There is a trick if you need to make the serial number wrap around to a lower value, but I've never had to do that. Hopefully you will not, either.

One place I worked put the zone file into webmin; this resulted in a HTML page that took hilariously long to render the resulting table with the thousands of entries in it (bloat browsers gonna bloat), and mass updates were pretty much impossible without writing a script to automate the web form—why not edit the file directly in that case? That was back in the mid-2000s, so my dislike of HTML and web-based solutions has roots pretty deep. I mean if you have five or six or maybe even 50 records it's probably okay, but webmin did not scale to thousands of records nor did it provide any means for mass updates where one needed to create or modify hundreds of records in one go.

Blacklists

Problematic domains can be blacklisted at the DNS level. This assumes that all your client systems use your DNS servers (they may not, and might instead use DNS over HTTPS) and also assumes all your users are okay with a domain being blacklisted. Here one creates an empty zone for the problematic domain, makes your DNS server authoritative for it, and then all requests to the DNS server will fail to lookup hosts in the problematic domain.

It may be more efficient to use a DNS server that directly supports blacklists so that memory need not be wasted on fake authoritative zones, especially as the number of blacklisted domains grows large. Probably the blacklists should also be done at the caching DNS server level, and not on the authoritative servers. (Using a DNS server will generally be much more efficient than putting the blacklist into the /etc/hosts file.)

There are services that provide lists of bad domains (spam, ads, malware, etc) that can be loaded into various DNS servers, or you could run a pi-hole. Check the documentation for those services for how to use them. Note that these services come with risk; they might block a domain that you or a customer do need access to, or might ship bad configuration that breaks your DNS services.

https://github.com/StevenBlack/hosts

https://github.com/cyclaero/void-zones-tools

I have not used any of the above, use at your own risk. There may be better options available?

Other Tools

Might be worth a look?

highly DNS-, DoS- and abuse-aware loadbalancer