By Manny Fernandez

April 18, 2026

Deploying Bind9 as DNS Server on Ubuntu Server

In the process of deploying my lab, I needed to set up DNS services for it.  I decided to use Bind 9 on my Ubuntu Server.  I am going to use infosecmonkey.org as the domain for my lab.  The .lab did not work out as I expected before.  The idea is to allow anyone to query this server from 10.1.105.0/24, 10.1.106.0/24 and 10.240.0.0/15.

1. Assumptions and Planning

We will assume:

– Ubuntu Server 22.04/24.04, fresh or lightly used.
– Server IP (example): 10.1.106.10 (adjust to your real IP).
– Domain: infosecmonkey.org hosted authoritatively on this box.
– Networks allowed recursion:
10.1.106.0/24
10.1.105.0/24
10.240.0.0/15

Files we will touch (Ubuntu/Debian layout):

/etc/bind/named.conf.options
/etc/bind/named.conf.local
– Zone file: /etc/bind/db.infosecmonkey.org

2. Install BIND9 and Base Packages

Run these commands as root or with sudo.

sudo apt update
sudo apt install bind9 bind9-utils bind9-dnsutils -y

Enable and start the service:

sudo systemctl enable bind9
sudo systemctl start bind9
sudo systemctl status bind9

You should see active (running) in the status output.

3. Define ACLs and Recursive Options

We will configure:

– An ACL trusted for your three networks.
– BIND to listen on all interfaces.
– Recursion allowed only for trusted and localhost.

Edit /etc/bind/named.conf.options:

sudo vi /etc/bind/named.conf.options

Replace the entire file contents with this (adjust comments if you like):

acl "trusted" {
127.0.0.1; // localhost
10.1.106.0/24; // allowed subnet
10.1.105.0/24; // allowed subnet
10.240.0.0/15; // allowed subnet
};

options {
directory "/var/cache/bind";

// Listen on all IPv4/IPv6 interfaces
listen-on port 53 { any; };
listen-on-v6 port 53 { any; };

// Allow queries (authoritative + recursive) only from trusted ACL
allow-query { trusted; };
allow-recursion { trusted; };

// Disable zone transfers by default (unless you add slaves later)
allow-transfer { none; };

// Enable recursion
recursion yes;

// DNSSEC validation (recommended on modern BIND/Ubuntu)
dnssec-validation auto;
auth-nxdomain no; // conform to RFC1035
empty-zones-enable yes;

// Some sensible performance/security tuning
max-cache-size 512M;
max-cache-ttl 604800; // 7 days
max-ncache-ttl 3600; // 1 hour
minimal-responses yes;
qname-minimization relaxed;

// Hide software version
version "DNS Server";

// Forwarders (optional) - remove or change if you want pure recursion
// forwarders {
// 1.1.1.1;
// 8.8.8.8;
// };
};

This configuration ensures recursive queries are only served to your specified networks plus localhost.

4. Define the infosecmonkey.org Zone

We now make BIND authoritative for `infosecmonkey.org` by defining a master zone and its zone file.[^1_6][^1_7][^1_2][^1_1]

4.1 Add Zone to named.conf.local

Edit /etc/bind/named.conf.local:

sudo vi /etc/bind/named.conf.local

Append the following block:

zone "infosecmonkey.org" {
type master;
file "/etc/bind/db.infosecmonkey.org";
};

This tells BIND to load the zone data from /etc/bind/db.infosecmonkey.org.

5. Create Zone File for infosecmonkey.org

We will create a basic but functional zone:

– SOA
– NS records
– A record for the nameserver itself
– A couple of sample A records (adjust to your environment).

Create the zone file:

sudo vi /etc/bind/db.infosecmonkey.org

Paste and adjust as needed:

$TTL 86400
@ IN SOA ns1.infosecmonkey.org. admin.infosecmonkey.org. (
2026041701 ; Serial (YYYYMMDDnn)
3600 ; Refresh (1 hour)
1800 ; Retry (30 minutes)
1209600 ; Expire (14 days)
86400 ) ; Minimum TTL (1 day)

; Authoritative name servers
IN NS ns1.infosecmonkey.org.

; Nameserver A record (use your actual IP)
ns1 IN A 10.1.106.10

; Example host records
@ IN A 10.1.106.10
www IN A 10.1.106.10
api IN A 10.1.105.20
vpn IN A 10.240.0.10

Key points:

– Update the serial each time you edit the file (e.g., 2026041702).
– Replace IPs with your real host addresses.

Set proper permissions (usually already fine, but explicit is safe):

sudo chown root:bind /etc/bind/db.infosecmonkey.org
sudo chmod 640 /etc/bind/db.infosecmonkey.org

6. Check Configuration and Reload BIND

Use BIND’s built-in checkers:

sudo named-checkconf

If no output, syntax is OK.

Check the zone file syntax:

sudo named-checkzone infosecmonkey.org /etc/bind/db.infosecmonkey.org

You should see something like:

zone infosecmonkey.org/IN: loaded serial 2026041701

If all is good, reload BIND:

sudo systemctl reload bind9

Or restart if you prefer
sudo systemctl restart bind9

7. Allow DNS Through Ubuntu Firewall (ufw)

If you use ufw, allow port 53 (UDP/TCP).

sudo ufw allow 53/tcp
sudo ufw allow 53/udp
sudo ufw reload

Your ACL still restricts who can use recursion; firewall only governs basic reachability.

8. Test Authoritative and Recursive Queries

From a host in 10.1.106.0/24, 10.1.105.0/24, or 10.240.0.0/15, or from the server itself:

8.1 Test Zone Resolution

dig @10.1.106.10 infosecmonkey.org
dig @10.1.106.10 www.infosecmonkey.org
dig @10.1.106.10 ns1.infosecmonkey.org

You should receive NOERROR responses with the configured A records.

8.2 Test Recursion (External Domain)

dig @10.1.106.10 google.com
dig @10.1.106.10 www.cloudflare.com

You should see status: NOERROR and IP addresses in the ANSWER section, plus the ra (recursion available) flag.

8.3 Test Recursion from a Disallowed IP

From a non-allowed subnet (or simulate with a VM on another network) you should see queries refused or timed out:

dig @10.1.106.10 google.com

Expected: status: REFUSED (or similar), indicating ACLs are working.

9. Optional: Logging for Troubleshooting

To get more visibility, you can add logging channels and categories.

Edit /etc/bind/named.conf.local and add before your zone:

logging {
channel default_syslog {
syslog daemon;
severity info;
};

channel query_log {
file "/var/log/named/queries.log" versions 3 size 10m;
severity info;
print-time yes;
`};

category queries { query_log; };
category default { default_syslog; };
};

Create log directory and adjust permissions:

sudo mkdir -p /var/log/named
sudo chown bind:bind /var/log/named
sudo chmod 750 /var/log/named
sudo systemctl restart bind9

You can then tail query logs:

sudo tail -f /var/log/named/queries.log

10. Quick Health Checklist

systemctl status bind9 shows running.
named-checkconf and named-checkzone both OK.
dig for infosecmonkey.org returns your internal IPs.
dig google.com via your server works from allowed networks and is refused from others

Recent posts

  • If you've spent any time configuring user authentication on... Full Story

  • DNS is one of those technologies that quietly underpins... Full Story

  • BGP issues on FortiGate firewalls usually trace back to... Full Story

  • Every time your laptop talks to your router, a... Full Story

  • If you've spent any time configuring NAT on a... Full Story

  • If you have spent any time configuring firewall policies... Full Story

  • High availability on FortiGate is one of those features... Full Story

  • If you've configured SD-WAN on a FortiGate, you've almost... Full Story

  • FortiLink is the management protocol that turns a FortiSwitch... Full Story

  • FortiSwitches are pretty rock solid from Mean Time Between... Full Story

  • This is a quicky tip.  Have you ever gone... Full Story

  • DNS is one of those quiet pieces of internet... Full Story

  • This article is an updated version of the previous... Full Story

  • You will add ns2 as a secondary (slave) BIND9... Full Story

  • In the process of deploying my lab, I needed... Full Story

  • RFC 8805, used to be known as Self-Correcting IP... Full Story

  • Years back, I wrote an article about certificate pinning. ... Full Story

  • FortiGates have the ability to send alerts to Microsoft... Full Story

  • In this post, I am going to walk through... Full Story

  • Troubleshooting VoIP on a FortiGate can feel like trying... Full Story

  • Prior to FortiOS 7.0, there were three commands to... Full Story

  • In this post, I am going to go over... Full Story

  • What we are going to do:  We are going... Full Story

  • Choosing between FGCP (FortiGate Clustering Protocol) and FGSP (FortiGate... Full Story

  • Creating a VLAN on macOS (The "Pro" Move) A... Full Story

  • This blog post explores the logic behind how macOS... Full Story

  • Pretty Fly for a Wi-Fi Tell My Wi-Fi Love... Full Story

  • Part of my daily gig is creating BoMs (Bill-of-Materials)... Full Story

  • ICMP introduces several security risks, but careful filtering, rate... Full Story

  • The command diag debug application dhcps -1 enables full... Full Story

  • In the world of FortiOS, execute tac report is... Full Story

  • LLDP; What is it The Link Layer Discovery Protocol... Full Story

  • What it actually does When you run diagnose fdsm... Full Story

  • Monkey Bites are bite-sized, high-impact security insights designed for... Full Story

  • I have run macOS in macOS with Parallels but... Full Story

  • Don't be confused with my other FortiNAC posts where... Full Story

  • This is the third session in a multi-part article... Full Story

  • Today I was configuring key-based authentication on a FortiGate... Full Story

  • Netcat, often called the "Swiss Army knife" of networking,... Full Story

  • At its core, IEEE 802.1X is a network layer... Full Story

  • In case you did not see the previous FortiNAC... Full Story

  • This is our 5th session where we are going... Full Story

  • Now that we have Wireshark installed and somewhat configured,... Full Story

  • The Philosophy of Packet Analysis Troubleshooting isn't about looking... Full Story

  • DISCLAIMER:  Please see link above for the official Fortinet... Full Story

  • The underlying assumption that Let’s Encrypt requires you to... Full Story

  • FortiGate ships with thousands of built-in application signatures, but... Full Story