Hướng dẫn cấu hình DNS server với Bind9 trên Ubuntu/Debian

Network tutorial - IT technology blog
Network tutorial - IT technology blog

DNS mặc định của ISP — đủ dùng nhưng không kiểm soát được gì

Mình quản lý network cho office 50 người và một datacenter nhỏ. Mấy năm đầu cứ để 8.8.8.8 với 1.1.1.1 lo hết — router assign DHCP, client dùng DNS của ISP, “ai cũng làm vậy mà.” Rồi vấn đề tích tụ dần:

  • Internal hostname không resolve — mỗi lần SSH vào server phải gõ IP thay vì server1.office.local
  • Mỗi DNS query đi ra ngoài internet, trung bình 30–40ms. Local cache thì dưới 1ms.
  • Không làm được split-DNS — nội bộ và bên ngoài phải trả cùng một IP
  • Không có log. Khi mạng có vấn đề, mò mẫm không biết bắt đầu từ đâu

Đó là lúc mình quyết định dựng Bind9. Hơn hai thập kỷ chạy trên production server khắp thế giới, nó là DNS software lâu đời nhất vẫn còn được maintain tích cực — đủ để tin dùng cho production.

Trước khi cài: mấy khái niệm không thể bỏ qua

Bind9 làm được nhiều thứ: authoritative server (ông chủ của một domain), recursive resolver (hỏi hộ client), hoặc cả hai. Trong bài này mình chỉ dùng chế độ authoritative + forwarder — đủ cho office network, không cần phức tạp hơn.

Các khái niệm cốt lõi bạn cần nắm

  • Zone: Vùng quản lý DNS. Ví dụ: office.local hoặc example.com
  • A record: Map hostname → IPv4. Ví dụ: server1.office.local → 192.168.1.10
  • PTR record: Reverse DNS — map IP → hostname, ngược chiều với A record
  • NS record: Khai báo nameserver của zone
  • SOA record: Metadata của zone — serial, refresh, retry, expire
  • Forwarder: Khi Bind không biết trả lời, nó chuyển query sang DNS khác (8.8.8.8 chẳng hạn)

Mình sẽ dựng DNS nội bộ cho dải 192.168.1.0/24, domain office.local. Đủ để demo toàn bộ flow từ đầu đến cuối.

Cài đặt và cấu hình Bind9

Bước 1: Cài đặt Bind9

Trên Ubuntu/Debian:

sudo apt update
sudo apt install -y bind9 bind9utils bind9-doc dnsutils

Sau khi cài, service named tự start. Kiểm tra nhanh:

sudo systemctl status named

Bước 2: Cấu hình named.conf.options

File /etc/bind/named.conf.options — nơi khai báo forwarder và access control:

sudo nano /etc/bind/named.conf.options
acl "trusted" {
    192.168.1.0/24;   // Mạng nội bộ
    localhost;
    localnets;
};

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

    // Chỉ cho phép mạng trusted query
    allow-query { trusted; };
    allow-recursion { trusted; };

    // Forwarder — khi không biết thì hỏi Cloudflare
    forwarders {
        1.1.1.1;
        8.8.8.8;
    };
    forward only;

    dnssec-validation auto;
    listen-on { any; };
    listen-on-v6 { any; };
};

Dòng forward only quan trọng: Bind không tự recursive ra ngoài mà chuyển hẳn cho forwarder xử lý. Với office network thì cách này nhanh và đơn giản hơn. Muốn Bind tự resolve hoàn toàn, không phụ thuộc 8.8.8.8? Bỏ dòng đó đi.

Bước 3: Khai báo zone trong named.conf.local

sudo nano /etc/bind/named.conf.local
// Forward zone
zone "office.local" {
    type master;
    file "/etc/bind/zones/db.office.local";
};

// Reverse zone cho 192.168.1.0/24
zone "1.168.192.in-addr.arpa" {
    type master;
    file "/etc/bind/zones/db.192.168.1";
};

Bước 4: Tạo zone file forward

sudo mkdir /etc/bind/zones
sudo nano /etc/bind/zones/db.office.local
;
; Forward zone file cho office.local
;
$TTL    604800
@       IN      SOA     ns1.office.local. admin.office.local. (
                        2026022801  ; Serial (format: YYYYMMDDNN)
                        604800      ; Refresh (7 ngày)
                        86400       ; Retry (1 ngày)
                        2419200     ; Expire (28 ngày)
                        604800      ; Negative Cache TTL
                        )

; Nameserver
@       IN      NS      ns1.office.local.

; A records
ns1     IN      A       192.168.1.1
gateway IN      A       192.168.1.1
server1 IN      A       192.168.1.10
server2 IN      A       192.168.1.11
nas     IN      A       192.168.1.20
printer IN      A       192.168.1.30

Serial number theo format YYYYMMDDNN: ngày hôm nay + số thứ tự trong ngày. Hôm nay 28/02/2026, lần sửa đầu tiên → 2026022801. Bắt buộc tăng serial mỗi khi sửa zone — quên bước này, slave DNS âm thầm không nhận update mà không báo lỗi gì. Mình đã từng mất cả buổi sáng debug vì chuyện này.

Bước 5: Tạo Reverse zone file

sudo nano /etc/bind/zones/db.192.168.1
;
; Reverse zone file cho 192.168.1.0/24
;
$TTL    604800
@       IN      SOA     ns1.office.local. admin.office.local. (
                        2026022801
                        604800
                        86400
                        2419200
                        604800
                        )

@       IN      NS      ns1.office.local.

; PTR records (chỉ ghi phần host, không ghi full IP)
1       IN      PTR     ns1.office.local.
10      IN      PTR     server1.office.local.
11      IN      PTR     server2.office.local.
20      IN      PTR     nas.office.local.
30      IN      PTR     printer.office.local.

Bước 6: Validate config trước khi restart

Đừng bỏ qua bước này. Một lần mình restart named mà không check — typo trong zone file khiến DNS cả office chết gần 10 phút:

# Kiểm tra named.conf
sudo named-checkconf

# Kiểm tra từng zone file
sudo named-checkzone office.local /etc/bind/zones/db.office.local
sudo named-checkzone 1.168.192.in-addr.arpa /etc/bind/zones/db.192.168.1

Output OK thì restart:

sudo systemctl restart named
sudo systemctl enable named

Bước 7: Test từ client

Từ máy trong dải 192.168.1.0/24, trỏ DNS về 192.168.1.1 rồi test:

# Forward lookup
dig @192.168.1.1 server1.office.local
nslookup server1.office.local 192.168.1.1

# Reverse lookup
dig @192.168.1.1 -x 192.168.1.10

# Test forwarder — query domain bên ngoài
dig @192.168.1.1 google.com

Forward lookup thành công trả về:

;; ANSWER SECTION:
server1.office.local.   604800  IN  A   192.168.1.10

Query time thường dưới 1ms cho hostname nội bộ sau lần đầu tiên (đã vào cache), so với 30–40ms khi query thẳng ra 8.8.8.8.

Troubleshooting thường gặp

  • SERVFAIL: Zone file lỗi cú pháp — chạy lại named-checkzone
  • REFUSED: IP của client không có trong ACL trusted — kiểm tra lại allow-query
  • Timeout: Firewall block port 53 UDP/TCP — ufw allow 53
  • Slave không nhận zone mới: Serial chưa tăng — lỗi phổ biến nhất, và im lặng không báo gì
# Xem log real-time khi debug
sudo tail -f /var/log/syslog | grep named

Kết luận

Cài xong Bind9 hết khoảng 30 phút. Kết quả thực tế: hostname nội bộ resolve được, query time giảm từ 30–40ms xuống dưới 1ms cho domain nội bộ, và lần đầu tiên có log đàng hoàng để debug khi mạng có chuyện.

Bước tiếp theo của mình là split-DNS — server1.example.com trả IP private khi query từ nội bộ, IP public khi query từ ngoài. Sẽ viết trong bài sau.

Đang quản lý network cho team 10 người trở lên? Đừng chờ gặp vấn đề rồi mới làm. Internal DNS là thứ setup một lần, hưởng mãi — theo đúng nghĩa đen.

Share: