Technitium DNS (and DHCP)

Sorry for hijacking the thread…
I’ve thought about that, too. But I am not at all sure if I can manage the migration in such a way that I can guarantee an uninterrupted operation of my DNS zones and the server installations that depend on them.

I have not found a guide that I fully trust. So far I have only found this one and halfway understood it.

I have the impression that all these guides assume that you are familiar with the specialties with Cloudflare.

I noticed when I migrated my last domain to Cloudflare that they’d greatly streamlined the process–they query your current authoritative servers and duplicate all the records. So the process really is as simple as “register for Cloudflare, add the domain, (confirm that the records are transferred properly, making any desired changes), change nameservers at your registrar.” But until you take that last step, that of changing the nameservers at your domain registrar, you can mess around with the DNS records all you want, with no impact to the rest of the world.

You don’t need to use the Cloudflare CDN at all. You can, of course, and use it (to a degree) for no cost too, but you don’t need to use that feature at all.

1 Like

I have tested the first steps, but it doesn’t look so simple. But I think discussing that is not the real place here. I will open a new thread for this once.

1 Like

Overall it would be interesting to see Technitium installed Via docker

Docker is a supported installation method for Technitium, so that would be straightforward enough–just install docker-compose, edit docker-compose.yml, docker-compose up -d, and you’re up and running. The problem on Neth is that there’s something else already using port 53, so you’d need to either remove that (and break the rest of the system) or have Technitium listen on some other port (and thus be pretty much useless).

Edit: I wonder how this would work: Go ahead and set up Technitium, but rather than listening on port 53, have it listen on, say 5335. Then tell the system its DNS server is 127.0.0.1#5335. This is pretty much how you set up Unbound on a Pi-Hole…

First draft:

Install Technitium on Nethserver 7

Starting from a clean, up-to-date Nethserver 7 installation.

Install Docker and docker-compose

  • yum install nethserver-docker
  • curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
  • chmod +x /usr/local/bin/docker-compose

Install Technitium

  • mkdir /opt/technitium
  • cd /opt/technitium
  • nano docker-compose.yml
version: "3"
services:
  dns-server:
    container_name: dns-server
    hostname: dns-server
    image: technitium/dns-server:latest
    # Use "host" network mode for DHCP deployments
    # network_mode: "host"
    ports:
      - "5380:5380/tcp" #DNS web console
      - "5335:53/udp" #DNS service
      - "5335:53/tcp" #DNS service
      # - "67:67/udp" #DHCP service
      # - "853:853/tcp" #DNS-over-TLS service
      # - "443:443/tcp" #DNS-over-HTTPS service
      # - "80:80/tcp" #DNS-over-HTTPS service certbot certificate renewal
      # - "8053:8053/tcp" #DNS-over-HTTPS using reverse proxy
    environment:
      - DNS_SERVER_DOMAIN=dns-server #The primary domain name used by this DNS Server to identify itself.
      # - DNS_SERVER_ADMIN_PASSWORD=password #DNS web console admin user password.
      # - DNS_SERVER_ADMIN_PASSWORD_FILE=password.txt #The path to a file that contains a plain text password for the DNS web console admin user.
      # - DNS_SERVER_PREFER_IPV6=false #DNS Server will use IPv6 for querying whenever possible with this option enabled.
      # - DNS_SERVER_OPTIONAL_PROTOCOL_DNS_OVER_HTTP=false #Enables DNS server optional protocol DNS-over-HTTP on TCP port 8053 to be used with a TLS terminating reverse proxy like nginx.
      # - DNS_SERVER_RECURSION=AllowOnlyForPrivateNetworks #Recursion options: Allow, Deny, AllowOnlyForPrivateNetworks, UseSpecifiedNetworks.
      # - DNS_SERVER_RECURSION_DENIED_NETWORKS=1.1.1.0/24 #Comma separated list of IP addresses or network addresses to deny recursion. Valid only for `UseSpecifiedNetworks` recursion option.
      # - DNS_SERVER_RECURSION_ALLOWED_NETWORKS=127.0.0.1, 192.168.1.0/24 #Comma separated list of IP addresses or network addresses to allow recursion. Valid only for `UseSpecifiedNetworks` recursion option.
      # - DNS_SERVER_ENABLE_BLOCKING=false #Sets the DNS server to block domain names using Blocked Zone and Block List Zone.
      # - DNS_SERVER_ALLOW_TXT_BLOCKING_REPORT=false #Specifies if the DNS Server should respond with TXT records containing a blocked domain report for TXT type requests.
      # - DNS_SERVER_FORWARDERS=1.1.1.1, 8.8.8.8 #Comma separated list of forwarder addresses.
      # - DNS_SERVER_FORWARDER_PROTOCOL=Tcp #Forwarder protocol options: Udp, Tcp, Tls, Https, HttpsJson.
    volumes:
      - config:/etc/dns/config    
    restart: unless-stopped
volumes:
    config:
  • docker-compose up -d
  • docker network connect aqua dns-server

Make sure Technitium is up and running

  • Browse to http://your_ip:5380
  • Change/set the admin password

Create a test record

  • In the Technitium UI, create a zone of example.com
  • Create a TXT record for test.example.com with a random value of your choice
  • From another computer on your network, run dig @neth_ip -p 5335 txt test.example.com. You should get output like this:
dan@Dan-MacBook-Pro-2013  ~  dig @192.168.1.218 -p 5335 txt test.example.com

; <<>> DiG 9.10.6 <<>> @192.168.1.218 -p 5335 txt test.example.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 41308
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;test.example.com.		IN	TXT

;; ANSWER SECTION:
test.example.com.	3600	IN	TXT	"foo"

;; Query time: 74 msec
;; SERVER: 192.168.1.218#5335(192.168.1.218)
;; WHEN: Sat Oct 30 14:22:33 EDT 2021
;; MSG SIZE  rcvd: 50

Tell Neth to use Technitium

  • mkdir -p /etc/e-smith/templates-custom/etc/dnsmasq.conf
  • nano /etc/e-smith/templates-custom/etc/dnsmasq.conf/25NameServers
#
# 25NameServers
#

# Don't read /etc/resolv.conf. Get upstream servers only from the
# command line or the dnsmasq configuration file.
no-resolv

# Specify IP address of upstream servers directly. Setting this flag
# does not suppress reading of /etc/resolv.conf, use "no-resolv" to do
# that.
server=127.0.0.1#5335

# By  default,  dnsmasq  will  send queries to any of the upstream
# servers it knows about and tries to favour servers that are known
# to  be  up.  Uncommenting this forces dnsmasq to try each query
# with  each  server  strictly  in  the  order  they   appear   in
# /etc/resolv.conf
all-servers

  • signal-event nethserver-dnsmasq-update

Note

Unfortunately, it isn’t currently possible to set a custom port on the DNS server without creating a custom template fragment like this. I’d expected to have been able to set it with config setprop dns NameServers 127.0.0.1#5335, but that breaks the dnsmasq template expansion.

Test

Run the same test as before, but without specifying the port number: dig @192.168.1.218 txt test.example.com. The results should be the same as before:

 dan@Dan-MacBook-Pro-2013  ~  dig @192.168.1.218 txt test.example.com

; <<>> DiG 9.10.6 <<>> @192.168.1.218 txt test.example.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 16410
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;test.example.com.		IN	TXT

;; ANSWER SECTION:
test.example.com.	3600	IN	TXT	"foo"

;; Query time: 7 msec
;; SERVER: 192.168.1.218#53(192.168.1.218)
;; WHEN: Sat Oct 30 14:29:27 EDT 2021
;; MSG SIZE  rcvd: 50

Conclusion

Technitium is now up and running on your Neth server, and DNSMasq on the Neth server is configured to use Technitium as its upstream DNS provider. From here, you’re on your own.

5 Likes

does it mean, also the DNS records can be setup inside Nethserver, and work as before, or they wont work as before and will rely on technitium

Haven’t tested that yet, but let’s see. Log in to the Neth server manager. Create a DNS record like this:

Query that server:

 dan@Dan-MacBook-Pro-2013  ~  dig @192.168.1.218 a foo.familybrown.org

; <<>> DiG 9.10.6 <<>> @192.168.1.218 a foo.familybrown.org
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 25903
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;foo.familybrown.org.		IN	A

;; ANSWER SECTION:
foo.familybrown.org.	0	IN	A	12.34.56.78

;; Query time: 5 msec
;; SERVER: 192.168.1.218#53(192.168.1.218)
;; WHEN: Sat Oct 30 18:29:37 EDT 2021
;; MSG SIZE  rcvd: 64

I’d say it works.

3 Likes

Technitium does have a HTTP API, too. So let’s play with it a bit. In all the examples, I’m piping the output through jq just for the sake of readability. Here’s how you’d create a record. First, log in:

[root@neth-tech technitium]# curl -s http://localhost:5380/api/login\?user\=admin\&pass\=password | jq
{
  "token": "df43d4dc49f5665167ffc6bf311089bb0cf576843f3b8ec27e9df1804733cd20",
  "status": "ok"
}

You’ll now use that token in every following request. We’re going to create a record for example.com, so let’s make sure this server claims to be authoritative for example.com:

[root@neth-tech technitium]# curl -s "http://localhost:5380/api/listZones?token=df43d4dc49f5665167ffc6bf311089bb0cf576843f3b8ec27e9df1804733cd20" | jq
{
  "response": {
    "zones": [
(snip)
      {
        "name": "example.com",
        "type": "Primary",
        "internal": false,
        "disabled": false
      },
(snip)
    ]
  },
  "status": "ok"
}

Yep, it’s there. So now, create the record:

[root@neth-tech technitium]# curl -s "http://localhost:5380/api/addRecord?token=df43d4dc49f5665167ffc6bf311089bb0cf576843f3b8ec27e9df1804733cd20&domain=_acme-challenge.example.com&type=TXT&value=___validation_token_received_from_the_ca___" | jq
{
  "response": {},
  "status": "ok"
}

It says OK, so we should be good. Let’s confirm. Note here that I’m querying the Neth server itself, not the Technitium instance directly.

 dan@Dan-Hack-Mini  ~  dig @192.168.1.218 txt _acme-challenge.example.com

; <<>> DiG 9.10.6 <<>> @192.168.1.218 txt _acme-challenge.example.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 14089
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;_acme-challenge.example.com.	IN	TXT

;; ANSWER SECTION:
_acme-challenge.example.com. 3600 IN	TXT	"___validation_token_received_from_the_ca___"

;; Query time: 5 msec
;; SERVER: 192.168.1.218#53(192.168.1.218)
;; WHEN: Sun Oct 31 06:32:18 EDT 2021
;; MSG SIZE  rcvd: 101

OK, so (hypothetically) Let’s Encrypt was able to validate domain control, and we got the certificate. Let’s now delete the record, as it doesn’t serve any further purpose:

[root@neth-tech technitium]# curl -s "http://localhost:5380/api/deleteRecord?token=df43d4dc49f5665167ffc6bf311089bb0cf576843f3b8ec27e9df1804733cd20&domain=_acme-challenge.example.com&type=TXT&value=___validation_token_received_from_the_ca___" | jq
{
  "response": {},
  "status": "ok"
}

…and test:

 dan@Dan-Hack-Mini  ~  dig @192.168.1.218 txt _acme-challenge.example.com

; <<>> DiG 9.10.6 <<>> @192.168.1.218 txt _acme-challenge.example.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 57993
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 0

NXDOMAIN, just as it should be. OK, time to log out:

[root@neth-tech technitium]# curl -s "http://localhost:5380/api/logout?token=df43d4dc49f5665167ffc6bf311089bb0cf576843f3b8ec27e9df1804733cd20" | jq
{
  "status": "ok"
}

So given this, it’d be pretty simple for Nethserver (or any other application) to make any necessary changes to the DNS system–so, for example, Neth could automatically populate all the AD DNS records, or all the dozens of records that email can use. And certbot or acme.sh, with an appropriate hook script (acme.sh doesn’t include one, and I assume certbot doesn’t either), could automatically generate and remove the records used for domain validation.

A couple of major drawbacks, though:

  • AFAIK, there’s only one admin user, with full control. You need that user’s password to use the API–there are no API tokens.
  • As a result of the above, there are no limited privileges. Any application (or person) that has the admin password can do absolutely anything with it. Perhaps not the most secure arrangement.

Note also that the record types are case-sensitive. If you try to create a txt record, for example, rather than a TXT record, you’ll get an error like this:

{
  "status": "error",
  "errorMessage": "Requested value 'txt' was not found.",
  "stackTrace": "   at System.Enum.TryParseByName(RuntimeType enumType, String originalValueString, ReadOnlySpan`1 value, Boolean ignoreCase, Boolean throwOnFailure, UInt64& result)\n   at System.Enum.TryParseUInt32Enum(RuntimeType enumType, String originalValueString, ReadOnlySpan`1 value, UInt32 maxInclusive, Boolean ignoreCase, Boolean throwOnFailure, TypeCode type, UInt32& result)\n   at System.Enum.TryParse(Type enumType, String value, Boolean ignoreCase, Boolean throwOnFailure, Object& result)\n   at System.Enum.Parse(Type enumType, String value)\n   at DnsServerCore.WebServiceZonesApi.AddRecord(HttpListenerRequest request) in Z:\\Technitium\\Projects\\DnsServer\\DnsServerCore\\WebServiceZonesApi.cs:line 505\n   at DnsServerCore.DnsWebService.ProcessRequestAsync(HttpListenerRequest request, HttpListenerResponse response) in Z:\\Technitium\\Projects\\DnsServer\\DnsServerCore\\DnsWebService.cs:line 519"
}
5 Likes

Can Threatshield AND Pi-Hole used simultanoesly on Nethserver? Doesn’t Threatshield include the PI-Hole functionality?

AFAIK threatshield use the pihole FTL (daemon/backend)

When I did it I preferred to see the pihole UI

2 Likes

Hi, I try to add a secondary zone on my technitium second dns server. The problem is that this server is on a different subnet, and doesn’t resolve the SOA address correctly. To be more specific I put the io address of my first dns server (that ping it without problem) but when try to reach the soa record fail.
Someone has e the same problem?

Thanks