Linux Router

Warning
To save yourself from headache just install OpenWRT or pfSense.

Table of Contents

To goal of this is to configure our Linux server as a router, LAN1 acting as a WAN and other interface like LAN2 to assign IP or WLAN as a hotspot.

DNS Server

Pi-hole

There are many option to run DNS server, but with my broke setup we’ll configure Pi-hole. I recommend Bind9 for advance functionality. Head over to their website for elaborate documentation.

 1services:
 2  pihole:
 3    container_name: pihole
 4    image: pihole/pihole:latest
 5    ports:
 6      # DNS Ports
 7      - "53:53/tcp"
 8      - "53:53/udp"
 9      # Default HTTP Port
10      - "80:80/tcp"
11      # Default HTTPs Port. FTL will generate a self-signed certificate
12      - "443:443/tcp"
13      # Uncomment the below if using Pi-hole as your DHCP Server
14      #- "67:67/udp"
15      # Uncomment the line below if you are using Pi-hole as your NTP server
16      #- "123:123/udp"
17    environment:
18      # Set the appropriate timezone for your location from
19      # https://en.wikipedia.org/wiki/List_of_tz_database_time_zones, e.g:
20      TZ: 'Europe/London'
21      # Set a password to access the web interface. Not setting one will result in a random password being assigned
22      FTLCONF_webserver_api_password: 'correct horse battery staple'
23      # If using Docker's default `bridge` network setting the dns listening mode should be set to 'ALL'
24      FTLCONF_dns_listeningMode: 'ALL'
25    # Volumes store your data between container upgrades
26    volumes:
27      # For persisting Pi-hole's databases and common configuration file
28      - './etc-pihole:/etc/pihole'
29      # Uncomment the below if you have custom dnsmasq config files that you want to persist. Not needed for most starting fresh with Pi-hole v6. If you're upgrading from v5 you and have used this directory before, you should keep it enabled for the first v6 container start to allow for a complete migration. It can be removed afterwards. Needs environment variable FTLCONF_misc_etc_dnsmasq_d: 'true'
30      #- './etc-dnsmasq.d:/etc/dnsmasq.d'
31    cap_add:
32      # See https://github.com/pi-hole/docker-pi-hole#note-on-capabilities
33      # Required if you are using Pi-hole as your DHCP server, else not needed
34      - NET_ADMIN
35      # Required if you are using Pi-hole as your NTP client to be able to set the host's system time
36      - SYS_TIME
37      # Optional, if Pi-hole should get some more processing time
38      - SYS_NICE
39    restart: unless-stopped

Run the container.

1docker compose up -d

Let assume your IP is 192.168.254.15. To check if the DNS server is running you can run this command.

Check from the server if port 53 is active

1$ netstat -tulp
2netstat -tulpn | grep 53
3(Not all processes could be identified, non-owned process info
4 will not be shown, you would have to be root to see it all.)
5tcp        0      0 127.0.0.1:53            0.0.0.0:*               LISTEN      -                   
6tcp        0      0 192.168.254.15:53       0.0.0.0:*               LISTEN      -   

Using dig or nslookup.

 1$ dig @192.168.254.15 -p 53 google.com
 2
 3; <<>> DiG 9.20.15-1~deb13u1-Debian <<>> @192.168.254.15 -p 53 google.com
 4; (1 server found)
 5;; global options: +cmd
 6;; Got answer:
 7;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 15974
 8;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
 9
10;; OPT PSEUDOSECTION:
11; EDNS: version: 0, flags:; udp: 1232
12;; QUESTION SECTION:
13;google.com.                    IN      A
14
15;; ANSWER SECTION:
16google.com.             149     IN      A       142.251.221.14
17
18;; Query time: 18 msec
19;; SERVER: 192.168.254.15#53(192.168.254.15) (UDP)
20;; WHEN: Mon Jan 26 15:27:39 PST 2026
21;; MSG SIZE  rcvd: 55
22
23
24$ nslookup google.com 192.168.254.15
25Server:         192.168.254.15
26Address:        192.168.254.15#53
27
28Non-authoritative answer:
29Name:   google.com
30Address: 142.251.221.46
31Name:   google.com
32Address: 2404:6800:4017:805::200e

dnsmasq as DNS Forwarder

With dnsmasq configured as a forwarder DNS can be resolved to your local IP (let the IP set to 192.168.254.15), as a example let our upstream DNS server to be 8.8.8.8.

First we need to install dnsmasq and start service.

1apt install dnsmasq
2systemctl enable --now dnsmasq

You will get error when you start the service, depending what network service you are using configure it to use dnsmasq.

networkd

1#  /etc/systemd/network/10-eth0.network
2Name=eth0
3
4[Network]
5Address=192.168.1.1/24
6DNS=192.168.254.15
7Domains=~.
8IPForward=yes

Network Manager

1# /etc/NetworkManager/NetworkManager.conf
2[main]
3dns=dnsmasq

Also edit your config to point it to your local IP.

Now let as configure dnsmasq.

1# /etc/dnsmasq.conf
2listen-address=127.0.0.1,192.168.254.15
3bind-interfaces
4
5interface=eth0
6dhcp-range=192.168.1.100,192.168.1.200,12h

Look at the next section as dnsmasq can also be configured as DHCP Server.

DHCP Server

WAN and LAN

Now let’s assume we have two ethernet and we want our Linux server behave like a gateway, eth0 as WAN and eth1 as LAN. First make sure IP forwarding is enabled, most probably enabled if you are using docker.

1echo "net.ipv4.ip_forward=1" | sudo tee /etc/sysctl.d/99-router.conf
2sudo sysctl --system

Set IP for ethernets using netplan, you can use other method. In here we let the server get WAN IP from the ISP, it can also be set to static. And the IP subnet propagated to the other ethernet is 192.168.10.0/24 as seen in the later section (IP range from 192.168.10.100 to .200).

/etc/netplan/01-router.yaml

 1network:
 2  version: 2
 3  renderer: networkd
 4
 5  ethernets:
 6    eth0:
 7      dhcp4: true
 8
 9    eth1:
10      dhcp4: no
11      addresses:
12        - 192.168.10.101/24

Apply configuration.

1netplan apply

Now we configure dnsmasq.

/etc/dnsmasq.conf

 1# Serve ONLY the LAN
 2interface=eth1
 3bind-interfaces
 4
 5# DHCP range
 6dhcp-range=192.168.10.100,192.168.10.200,12h
 7
 8# DHCP options
 9dhcp-option=option:router,192.168.10.1
10dhcp-option=option:dns-server,192.168.10.1
11
12# DNS forwarding
13no-resolv
14server=192.168.254.15
15server=8.8.8.8
16
17# Hygiene
18domain-needed
19bogus-priv

Start dnsmasq service

1systemctl enable --now dnsmasq

WAN/LAN and Hotspot

Unlike the previous example that has two ethernets, in here we assume we have one ethernet (eth0) acting as WAN/LAN and wireless nic (wlan0) as hotspot.

Like the previous config, we just change the interface to wlan0.

/etc/dnsmasq.conf

 1# Serve ONLY the LAN
 2interface=wlan0
 3bind-interfaces
 4
 5# DHCP range
 6dhcp-range=192.168.10.100,192.168.10.200,12h
 7
 8# DHCP options
 9dhcp-option=option:router,192.168.10.1
10dhcp-option=option:dns-server,192.168.10.1
11
12# DNS forwarding
13no-resolv
14server=192.168.254.15
15server=8.8.8.8
16
17# Hygiene
18domain-needed
19bogus-priv

Start dnsmasq.

1systemctl enable --now dnsmasq

Install hotspod.

1apt install hotspot

Edit hotspotd configuration.

/etc/hotspotd/hotspotd.conf

 1# Interface used for the hotspot
 2interface=wlan0
 3
 4# Hotspot IP configuration
 5address=192.168.10.1
 6netmask=255.255.255.0
 7
 8# Bridge (optional – only if you really use one)
 9# Comment this out if not bridging
10# bridge=br0
11
12# Wireless settings
13ssid=yourSSID
14country_code=PH
15driver=nl80211
16
17hw_mode=g
18channel=7
19ieee80211n=1
20wmm_enabled=1
21
22# Security: WPA2-PSK ONLY
23wpa=2
24wpa_key_mgmt=WPA-PSK
25rsn_pairwise=CCMP
26wpa_pairwise=CCMP
27wpa_passphrase=averysecurepassword

Start hotspotd service.

1systemctl enable --now hotspotd