Ba lựa chọn DNS server phổ biến cho mạng nội bộ
Sau gần nửa năm chạy hệ thống DNS nội bộ trên môi trường production với khoảng 50 máy chủ, mình đã thử qua ba giải pháp khác nhau trước khi quyết định ở lại với BIND9. Đây là những gì mình rút ra được từ thực tế.
Ba lựa chọn phổ biến nhất cho DNS server nội bộ trên CentOS Stream 9:
- BIND9 (Berkeley Internet Name Domain) — giải pháp DNS truyền thống, đầy đủ tính năng nhất
- dnsmasq — nhẹ, dễ cấu hình, phù hợp cho mạng nhỏ
- Unbound — tập trung vào recursive DNS, bảo mật cao, ít tính năng authoritative
Phân tích ưu nhược điểm từng giải pháp
dnsmasq — Đơn giản nhưng có giới hạn
Mình bắt đầu với dnsmasq vì cấu hình chỉ mất 15 phút. Nhưng khi số lượng host tăng lên và cần quản lý nhiều zone khác nhau (lab.company.local, db.company.local…), dnsmasq bắt đầu lộ điểm yếu. Không có zone file thực sự, không support AXFR để sync giữa primary và secondary DNS, và khi debug sự cố thì khá khó chịu vì log ít chi tiết.
Phù hợp khi: Lab cá nhân, mạng gia đình, dưới 20 host, không cần primary-secondary replication.
Unbound — Bảo mật tốt nhưng thiếu tính năng authoritative
Unbound cực kỳ tốt cho recursive DNS — tức là forward query ra internet. Nhưng nó không được thiết kế để làm authoritative DNS server (loại server phân giải tên miền nội bộ của chính mạng bạn). Dùng Unbound để resolve server01.company.local là không đúng use case, cấu hình sẽ trở thành workaround phức tạp.
Phù hợp khi: Chỉ cần recursive resolver, ưu tiên bảo mật DNSSEC validation.
BIND9 — Phức tạp hơn nhưng xứng đáng
BIND9 là lựa chọn mình cuối cùng đi về. Cấu hình phức tạp hơn, nhưng bù lại: zone file rõ ràng, hỗ trợ cả forward và reverse lookup, có thể cấu hình primary-secondary replication, và tài liệu chính thức đầy đủ. Trên CentOS Stream 9 với SELinux enforcing, BIND9 cũng được kiểm thử kỹ hơn — policy SELinux cho named đã được định nghĩa sẵn trong hệ thống.
Phù hợp khi: Mạng doanh nghiệp, nhiều zone, cần authoritative DNS, cần primary-secondary setup để có high availability.
Chọn BIND9 — và lý do thực tế
Công ty mình vẫn còn vài con server chạy CentOS 7, và việc migrate sang AlmaLinux là bài toán mình đã xử lý. Trong giai đoạn chuyển đổi, cần một DNS server đủ mạnh để handle cả hai môi trường cũ lẫn mới song song — BIND9 với khả năng cấu hình nhiều zone riêng biệt là lựa chọn tự nhiên nhất. Hơn nữa, khi toàn bộ hạ tầng chạy SELinux enforcing (mặc định trên CentOS Stream 9), BIND9 gần như không phát sinh vấn đề nếu bạn đặt file đúng chỗ.
Triển khai BIND9 trên CentOS Stream 9
Bước 1: Cài đặt packages
sudo dnf install -y bind bind-utils
sudo systemctl enable --now named
sudo systemctl status named
Bước 2: Cấu hình named.conf
File cấu hình chính tại /etc/named.conf. Mình cấu hình BIND9 làm authoritative DNS cho zone nội bộ company.local và forward các query còn lại ra DNS public:
sudo nano /etc/named.conf
options {
listen-on port 53 { 127.0.0.1; 192.168.1.10; }; # IP của DNS server
listen-on-v6 port 53 { none; };
directory "/var/named";
dump-file "/var/named/data/cache_dump.db";
statistics-file "/var/named/data/named_stats.txt";
allow-query { localhost; 192.168.1.0/24; }; # Chỉ cho phép subnet nội bộ
# Forward DNS queries ra ngoài internet
forwarders {
8.8.8.8;
8.8.4.4;
};
forward only;
recursion yes;
dnssec-validation yes;
};
logging {
channel default_debug {
file "data/named.run";
severity dynamic;
};
};
# Zone nội bộ (forward)
zone "company.local" IN {
type master;
file "company.local.zone";
allow-update { none; };
};
# Reverse zone cho subnet 192.168.1.0/24
zone "1.168.192.in-addr.arpa" IN {
type master;
file "company.local.rev";
allow-update { none; };
};
include "/etc/named.rfc1912.zones";
include "/etc/named.root.key";
Bước 3: Tạo Forward Zone file
sudo nano /var/named/company.local.zone
$TTL 86400
@ IN SOA ns1.company.local. admin.company.local. (
2024010101 ; Serial (YYYYMMDDNN — tăng mỗi khi sửa)
3600 ; Refresh
1800 ; Retry
604800 ; Expire
86400 ) ; Minimum TTL
; Name servers
@ IN NS ns1.company.local.
; A records
ns1 IN A 192.168.1.10
gateway IN A 192.168.1.1
server01 IN A 192.168.1.20
server02 IN A 192.168.1.21
db01 IN A 192.168.1.30
webserver IN A 192.168.1.40
; CNAME
www IN CNAME webserver.company.local.
Serial number là điểm quan trọng hay bị quên: phải tăng mỗi khi chỉnh sửa zone file, BIND9 dùng giá trị này để quyết định có reload zone hay không.
Bước 4: Tạo Reverse Zone file
sudo nano /var/named/company.local.rev
$TTL 86400
@ IN SOA ns1.company.local. admin.company.local. (
2024010101
3600
1800
604800
86400 )
@ IN NS ns1.company.local.
; PTR records (reverse lookup)
10 IN PTR ns1.company.local.
1 IN PTR gateway.company.local.
20 IN PTR server01.company.local.
21 IN PTR server02.company.local.
30 IN PTR db01.company.local.
40 IN PTR webserver.company.local.
Bước 5: Kiểm tra cú pháp trước khi restart
# Kiểm tra named.conf
sudo named-checkconf
# Kiểm tra zone forward
sudo named-checkzone company.local /var/named/company.local.zone
# Kiểm tra zone reverse
sudo named-checkzone 1.168.192.in-addr.arpa /var/named/company.local.rev
# Nếu không có lỗi, restart
sudo systemctl restart named
Xử lý SELinux — phần hay bị bỏ qua nhất
Đây là chỗ mình mất nhiều thời gian nhất khi setup lần đầu. SELinux enforcing sẽ block BIND9 nếu zone file không có context đúng. Zone file phải đặt trong /var/named/ — không được để ở /etc/ hay thư mục tự tạo.
Kiểm tra SELinux context của zone file:
ls -lZ /var/named/company.local.zone
Output đúng phải có named_zone_t:
-rw-r--r--. 1 root named system_u:object_r:named_zone_t:s0 /var/named/company.local.zone
Nếu context sai (ví dụ copy file từ chỗ khác vào), fix bằng:
sudo restorecon -Rv /var/named/
sudo chown root:named /var/named/company.local.zone /var/named/company.local.rev
Kiểm tra SELinux có đang block named không:
sudo ausearch -c 'named' -m avc --raw 2>/dev/null | head -20
sudo tail -f /var/log/audit/audit.log | grep named
Cấu hình firewalld
Mở port DNS (53/udp và 53/tcp) cho subnet nội bộ:
# Cách 1: Mở service dns toàn bộ
sudo firewall-cmd --permanent --add-service=dns
sudo firewall-cmd --reload
# Cách 2: Restrict chỉ từ subnet nội bộ (khuyến nghị)
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" service name="dns" accept'
sudo firewall-cmd --reload
# Kiểm tra
sudo firewall-cmd --list-all
Test DNS server
# Test forward lookup
dig @192.168.1.10 server01.company.local
# Test reverse lookup
dig @192.168.1.10 -x 192.168.1.20
# Test forward DNS ra internet
dig @192.168.1.10 google.com
# Từ client trong mạng nội bộ
nslookup server01.company.local 192.168.1.10
Cập nhật DNS trên client dùng NetworkManager:
sudo nmcli con mod "Wired connection 1" ipv4.dns "192.168.1.10"
sudo nmcli con mod "Wired connection 1" ipv4.dns-search "company.local"
sudo nmcli con up "Wired connection 1"
Các lỗi thường gặp và cách xử lý nhanh
- named không start: Check SELinux context trước, sau đó
journalctl -u named -n 50 - Query timeout từ client: 99% là firewalld chưa mở port —
firewall-cmd --list-all | grep dns - Zone file không reload: Serial number chưa tăng — chỉnh serial rồi chạy
sudo rndc reload company.local - Permission denied trong log: File zone chưa thuộc group named —
chown root:named /var/named/*.zone
Bước tiếp theo
Setup này đã chạy ổn định trên production suốt 6 tháng qua. Phần khó nhất thực sự chỉ là lần đầu — đặc biệt xử lý SELinux context và firewalld. Một khi BIND9 đã chạy ngon, nó gần như không cần can thiệp thêm.
Nếu muốn nâng cao hơn, bước tiếp theo là thiết lập secondary DNS server với type slave và AXFR replication — để khi DNS chính gặp sự cố, mạng nội bộ vẫn phân giải được tên miền bình thường.
