Home Network DNS

  1. Introduction
    1. Basic DNS Setup
    2. Basic Configuration
    3. Exposing to WAN
      1. Using acme.sh

Introduction

Domain Name System (DNS) is a a system that associates domain names (strings) to internet protocol (IP) addresses.
Devices on a local network likely do not have an entry in global DNS. It can be tedious to enter an IP address to access these devices.
This post is an attempt to setup a home network to support local domain names using DNSMasq, as well as exposing a webserver externally with SSL.

I will be using a home installation of dnsmasq on a mini server running debian that is always connected and available in my network.
In my router, I have ensured that this server is assigned a fixed IP address (192.168.1.175).

Basic DNS Setup

Install dnsmasq on the server using sudo apt install dnsmasq.

A simple DNS server has now been created. We can test this by running a dns lookup pointed to localhost: dig udia.ca @localhost

; <<>> DiG 9.18.16-1~deb12u1-Debian <<>> udia.ca @localhost
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 39987
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;udia.ca. IN A

;; ANSWER SECTION:
udia.ca. 244 IN A 104.21.56.159
udia.ca. 244 IN A 172.67.187.41

;; Query time: 0 msec
;; SERVER: ::1#53(localhost) (UDP)
;; WHEN: Sat Sep 02 12:54:49 MDT 2023
;; MSG SIZE rcvd: 68

Basic Configuration

Let’s configure dnsmasq by modifying the file within /etc/dnsmasq.conf.
The following is a basic setup with comments explaining what each line does:

# DNS domain for the DHCP server
local=/home/
# never forward A or AAAA queries for plain names
domain-needed
# all reverse lookups for private IP ranges not found in /etc/hosts or DHCP
# should be answered with "no such domain"
bogus-priv
# use Cloudflare as upstream servers
server=1.1.1.1
server=1.0.0.1

The mini server I am running also has a local nginx web server running.
To expose this, I will modify the /etc/hosts file to include:

192.168.1.175   dns.home home

After saving the hosts file, run sudo systemctl restart dnsmasq to restart the service.

Now, local network requests to http://dns.home and http://home should resolve to 192.168.1.175.

Exposing to WAN

To have the webserver here available to the external internet, I enabled dynamic dns on my router.
Within my DNS provider (Cloudflare), I add a new A record to point to my currently assigned WAN.
Using awwong1/unifi-ddns, a DynDNS like API is exposed to my Ubiquiti router.
Within the router, I setup port forwarding to allow 80 and 443 to 192.168.1.175.

Using acme.sh

Create a new Cloudflare API token at https://dash.cloudflare.com/profile/api-tokens with permissions:

Modify the /etc/environment file to contain the cloudflare API token and account ID as environment variables.

CF_Token="<token>"
CF_Account_ID="<id>"

For SSL certificate provisioning, I use acmesh-official/acme.sh.
This diverges slightly from the official documentation, which recommends installing and using acme.sh as the root user.

sudo adduser --system --group --home /home/acmesh acmesh
sudo vim /etc/sudoers.d/acmesh
# acmesh ALL=(ALL:ALL) NOPASSWD:ALL

Switch users to acmesh and install acme.sh.

sudo su -s /bin/bash acmesh
wget -O - https://get.acme.sh | sh -s [email protected]

Because this is a system user, the installation script informs us that we don’t have a profile.
Invocations of the script will require us to specify the path to the executable: /home/acmesh/.acmesh/acme.sh.

Create a ZeroSSL account and generate EAB credentials within app.zerossl.com/developer.
Register the EAB credentials within acme.sh:

/home/acmesh/.acmesh/acme.sh \
--register-account --server zerossl \
--eab-kid xxxxxxxxxxxx \
--eab-hmac-key xxxxxxxxx

Issue a certificate for home.udia.ca.

/home/acmesh/.acme.sh/acme.sh --issue --dns dns_cf -d home.udia.ca

Once this certificate is issued, it must be installed.
Let’s create a folder to store our certs and ensure the acmesh user can modify it.

sudo mkdir -p /etc/nginx/ssl/home.udia.ca
sudo chown root:acmesh /etc/nginx/ssl -R
sudo chmod g+w /etc/nginx/ssl -R
/home/acmesh/.acme.sh/acme.sh --install-cert -d home.udia.ca \
--key-file /etc/nginx/ssl/home.udia.ca/privkey.pem \
--fullchain-file /etc/nginx/ssl/home.udia.ca/fullchain.pem \
--reloadcmd "sudo systemctl reload nginx"

A basic nginx configuration that supports the previous acme.sh certificate installation follows:
cat /etc/nginx/sites-available/home

server {
listen 80;
listen [::]:80;

server_name home dns.home home.udia.ca;

root /var/www/html;
index index.html index.htm index.nginx-debian.html;

location / {
try_files $uri $uri/ =404;
}
}

server {
listen 443 ssl;
server_name home.udia.ca;

root /var/www/html;
index index.html index.htm index.nginx-debian.html;

ssl_certificate /etc/nginx/ssl/home.udia.ca/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/home.udia.ca/privkey.pem;

location / {
try_files $uri $uri/ = 404;
}
}