dnsdist là một công cụ mạnh mẽ được sử dụng để quản lý và điều phối traffic DNS. Với
- Load Balancing, giúp phân phối tải đều và hiệu quả cho các máy chủ DNS, đảm bảo rằng traffic không bị tắc nghẽn và tối ưu hóa hiệu suất hệ thống.
- Failover cho phép dnsdist tự động chuyển hướng lưu lượng DNS đến các máy chủ dự phòng trong trường hợp máy chủ chính gặp sự cố hoặc không hoạt động. Điều này đảm bảo rằng dịch vụ DNS không bị gián đoạn và người dùng vẫn có thể truy cập phân giải một cách liên tục.
- DoS Protection giúp ngăn chặn các cuộc tấn công từ chối dịch vụ (DoS) bằng cách giới hạn số lượng lưu lượng truy cập đến hệ thống DNS. Điều này giúp bảo vệ hệ thống khỏi các cuộc tấn công gây ra sự cố và đảm bảo rằng dịch vụ DNS hoạt động ổn định và bảo mật.
Ngoài ra, dnsdist cũng hỗ trợ các giao thức DNS khác nhau như DNS over TLS (DoT) và DNS over HTTPS (DoH), giúp bảo mật và mã hóa traffic DNS và còn nhiều tính năng khác.
Với những tính năng đa dạng và linh hoạt như vậy, dnsdist là một giải pháp quản lý DNS mạnh mẽ, đảm bảo hiệu suất, an toàn và khả năng chịu tải cho hệ thống mạng.
1. Mô hình tổng quan
Ở phần 2 của chủ đề PowerDNS, chúng ta đã thiết lặp đồng bộ zone Master-Slave cho cặp DNS Authoritative Server.
Trong chủ đề này chúng ta sẽ dựng 2 Server dnsdist (Một Master và một Backup) đứng trước cặp DNS Authoritative để hứng và xử lý traffic. Sử dụng kết hợp KeepAlived cho 2 server dnsdist để nâng cao tính sẵn sàng cho hệ thống.
Lúc này chúng ta sẽ cần trỏ 2 record nameserver ns1.gocit.vn và ns2.gocit.vn về địa chỉ Virtual IP 10.10.40.30
Virtual IP sẽ được uplink mặc định trên Server Master, nếu Master ‘die‘ thì Virtual IP sẽ được uplink trên Server Backup.
Khi Client gửi yêu cầu truy vấn DNS, dnsdist sẽ thực hiện kiểm tra các Rule ACL trước. Nếu truy cập được cho phép, dnsdist sẽ tiếp tục kiểm tra cache. Nếu yêu cầu đã được hit cache, dnsdist sẽ trả kết quả ngay cho Client. Trường hợp miss cache, dnsdist sẽ gửi yêu cầu truy vấn đến DNS Authoritative. Nếu Client bị chặn bởi các Rule ACL, dnsdist sẽ gửi phản hồi từ chối trực tiếp cho Client.
Có nhiều nhiều Rule ACL và phương pháp phân phối traffic đến DNS Authoritative, và mình sẽ nói chi tiết hơn trong mục tiếp theo của bài viết này.
2. Chuẩn bị môi trường
- dnsdist server 1:
- CPU: 2 core; Ram 2 GB; Disk: 20 GB
- IP: 10.10.40.10
- Domain: dnsdist01.gocit.vn
- Master
- dnsdist server 2:
- CPU: 2 core; Ram 2 GB; Disk: 20 GB
- IP: 10.10.40.20
- Domain: dnsdist02.gocit.vn
- Slave
- Virtual IP: 10.10.40.30
- OS: Debian 12
Hai server cần được update và upgrade các package mới nhất. Hãy sử dụng hai lệnh sau:
apt install -y update
apt install -y upgrade
3. Cài đặt và cấu hình KeepAlived
Keepalived sử dụng VRRP (Giao thức dự phòng bộ định tuyến ảo) để xây dựng cấu hình dự phòng.
Cài đặt Keepalived trên cả 2 server bằng lệnh sau:
apt -y install keepalived
3.1. Cấu hình tại Server Master
cat << EOF > /etc/keepalived/keepalived.conf
global_defs {
router_id dnsdist01
}
vrrp_instance VRRP1 {
state MASTER
interface ens18
virtual_router_id 101
priority 200
advert_int 1
authentication {
auth_type PASS
auth_pass Q87dKPFU5d6ziLp
}
virtual_ipaddress {
10.10.40.30/24
}
notify_master "/etc/keepalived/notify.sh Master"
notify_backup "/etc/keepalived/notify.sh Backup"
notify_fault "/etc/keepalived/notify.sh Fault"
}
EOF
Cấu hình file script để ghi log
mkdir /var/log/keepalived
cat << EOF > /etc/keepalived/notify.sh
#!/bin/bash
keepalived_log='/var/log/keepalived/keepalived.state'
function check_state {
local state=$1
cat << EOF >> $keepalived_log
===================================
Date: $(date +'%d-%b-%Y %H:%M:%S')
[INFO] Now $state
EOF
if [[ "$state" == "Master" ]]; then
sudo systemctl restart lelastic
else
sudo systemctl stop lelastic
fi
}
function main {
local state=$1
case $state in
Master)
check_state Master;;
Backup)
check_state Backup;;
Fault)
check_state Fault;;
*)
echo "[ERR] Provided arguement is invalid"
esac
}
main $1
EOF
Restart lại KeepAlived để nhận cấu hình mới và kiểm tra Status
systemctl restart keepalived.service
systemctl status keepalived.service
[root@dnsdist01]# systemctl status keepalived.service
● keepalived.service - Keepalive Daemon (LVS and VRRP)
Loaded: loaded (/lib/systemd/system/keepalived.service; enabled; preset: enabled)
Active: active (running) since Fri 2024-05-03 06:22:33 EDT; 2m ago
Docs: man:keepalived(8)
man:keepalived.conf(5)
man:genhash(1)
https://keepalived.org
Main PID: 21721 (keepalived)
Tasks: 2 (limit: 4644)
Memory: 2.0M
CPU: 3.694s
CGroup: /system.slice/keepalived.service
├─21721 /usr/sbin/keepalived --dont-fork
└─21722 /usr/sbin/keepalived --dont-fork
May 04 01:57:40 dnsdist01 Keepalived_vrrp[26992]: (VRRP1) Entering MASTER STATE
3.2. Cấu hình tại Server Backup
cat << EOF > /etc/keepalived/keepalived.conf
global_defs {
router_id dnsdist02
}
vrrp_instance VI_1 {
state MASTER
interface ens18
virtual_router_id 101
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass Q87dKPFU5d6ziLp
}
virtual_ipaddress {
10.10.40.30/24
}
notify_master "/etc/keepalived/notify.sh Master"
notify_backup "/etc/keepalived/notify.sh Backup"
notify_fault "/etc/keepalived/notify.sh Fault"
}
EOF
Cấu hình file script để ghi log, tương tự như trên.
Restart lại KeepAlived để nhận cấu hình mới và kiểm tra Status
systemctl restart keepalived.service
systemctl status keepalived.service
[root@dnsdist01]# systemctl status keepalived.service
● keepalived.service - Keepalive Daemon (LVS and VRRP)
Loaded: loaded (/lib/systemd/system/keepalived.service; enabled; preset: enabled)
Active: active (running) since Fri 2024-05-03 06:25:33 EDT; 5s ago
Docs: man:keepalived(8)
man:keepalived.conf(5)
man:genhash(1)
https://keepalived.org
Main PID: 21721 (keepalived)
Tasks: 2 (limit: 4644)
Memory: 2.0M
CPU: 3.694s
CGroup: /system.slice/keepalived.service
├─32263 /usr/sbin/keepalived --dont-fork
└─32263 /usr/sbin/keepalived --dont-fork
May 04 01:57:40 dnsdist01 Keepalived_vrrp[26992]: (VRRP1) Entering BACKUP STATE
3.3. Kiểm tra hoạt động KeepAlived
Kiểm tra lại card mạng xem đã gán Virtual IP hay chưa và kiểm tra trạng thái được ghi trong file log
[root@dnsdist01]# ip a
1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host noprefixroute
valid_lft forever preferred_lft forever
2: ens18: mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether bc:24:11:9c:42:9a brd ff:ff:ff:ff:ff:ff
altname enp0s18
inet 10.10.40.10/24 brd 10.10.40.255 scope global ens18
valid_lft forever preferred_lft forever
inet 10.10.40.30/24 scope global secondary ens18
valid_lft forever preferred_lft forever
inet6 fe80::be24:11ff:fe9c:429a/64 scope link
valid_lft forever preferred_lft forever
[root@dnsdist01]# cat /var/log/keepalived/keepalived.state
===================================
Date: 03-May-2024 06:22:33
[INFO] Now Master
4. Cài đặt và cấu hình dnsdist
Cài đặt dnsdist trên cả hai server bằng lệnh sau:
apt -y install dnsdist
4.1 Cấu hình các Policy và rule ACL
Chỉnh sửa lại nội dung của file sau: /etc/dnsdist/dnsdist.conf trên cả 2 server dnsdist
---- dnsdist configuration file, an example can be found in /usr/share/doc/dnsdist/examples/
---- disable security status polling via DNS
setSecurityPollSuffix("")
---- Chỉ định địa chỉ IP và port của máy chủ web
webserver("10.10.40.30:8083")
---- Chỉ định địa chỉ IP cho phép truy cập web
setWebserverConfig({acl="192.168.2.10/32"})
---- Chỉ định địa chỉ IP, port và setKey của control socket
controlSocket('127.0.0.1:5199')
setKey("2ux3QDmpdDAzYjspjkhjkuyF8jXFU5qhd/BqXV8ag=")
---- Cấu hình ghi log
---- addAction(AllRule(), LogAction("/var/log/dnsdist.log", false, true, false,true))
---- Cấu hình lắng nghe truy vấn DNS qua giao thức UDP/53 and TCP/53
setLocal('0.0.0.0:53')
---- Cấu hình lắng nghe truy vấn DNS qua giao thức DoT (DNS over TLS) queries on TCP/853
addTLSLocal('0.0.0.0:853', {"/etc/dnsdist/ssl/vinahost.vn.cert.combined"}, {"/etc/dnsdist/ssl/vinahost.vn.key"})
---- Cấu hình lắng nghe truy vấn DNS qua giao thức DNS over HTTPS (DoH) queries on TCP/443
addDOHLocal("0.0.0.0:443", {"/etc/dnsdist/ssl/vinahost.vn.cert.combined"}, {"/etc/dnsdist/ssl/vinahost.vn.key"}, "/dns-query")
---- Định nghĩa các máy chủ back-end
newServer({address="10.10.10.1",name="nsmaster", weight=1, tcpFastOpen=true, healthCheckMode='lazy', checkInterval=1, lazyHealthCheckFailedInterval=30, rise=2, maxCheckFailures=3, lazyHealthCheckThreshold=30, lazyHealthCheckSampleSize=100, lazyHealthCheckMinSampleCount=10, lazyHealthCheckMode='TimeoutOnly'})
newServer({address="10.10.20.1",name="ns-slave", weight=2, tcpFastOpen=true, healthCheckMode='lazy', checkInterval=1, lazyHealthCheckFailedInterval=30, rise=2, maxCheckFailures=3, lazyHealthCheckThreshold=30, lazyHealthCheckSampleSize=100, lazyHealthCheckMinSampleCount=10, lazyHealthCheckMode='TimeoutOnly'})
---- Định nghĩa chính sách xử lý truy vấn
setACL({'0.0.0.0/0', '::/0'})
---- Cấu hình policy load balancing đến Backend
setServerPolicy(wrandom) -- weight random Khi có 3 yêu cầu đến thì ns-slave sẽ trả lời 2 query, nsmaster sẽ trả lời 1
---- Chặn và phản hồi các query có type là any
addAction(QTypeRule(DNSQType.ANY), TCAction())
---- Cấu hình Dynamic Blocking Rules (DBR)
local dbr = dynBlockRulesGroup()
---- Rate limit 30 query trong 10 giây, sẽ chặn trong 60 giây nếu đạt giới hạn
dbr:setQueryRate(30, 10, "Exceeded query rate", 60)
---- Rate limit 20 query với NXDOMAIN ( non-existent domain (tên miền không tồn tại)) trong 10 giây, sẽ chặn trong 60 giây nếu đạt giới hạn
dbr:setRCodeRate(DNSRCode.NXDOMAIN, 20, 10, "Exceeded NXD rate", 60)
---- Rate limit 20 query với SERVFAIL trong 10 giây, sẽ chặn trong 60 giây nếu đạt giới hạn
dbr:setRCodeRate(DNSRCode.SERVFAIL, 20, 10, "Exceeded ServFail rate", 60)
---- Rate limit 5 query với ANY (tất cả type) trong 10 giây, sẽ chặn trong 60 giây nếu đạt giới hạn
dbr:setQTypeRate(DNSQType.ANY, 5, 10, "Exceeded ANY rate", 60)
---- Rate limit BW 10000 byte query trong 10 giây, sẽ chặn trong 60 giây nếu đạt giới hạn
dbr:setResponseByteRate(10000, 10, "Exceeded resp BW rate", 60)
---- Định nghĩa hàm "maintenance"
function maintenance()
dbr:apply()
end
--- Định nghĩa để xóa bộ nhớ cache
function flushCache(dq)
errlog("Got notify for " .. dq.qname:toString())
a = getPool(""):getCache():toString()
getPool(""):getCache():expungeByName(dq.qname, DNSQType.ANY, true)
z = getPool(""):getCache():toString()
errlog("Went from " .. a .. " to " .. z)
return DNSAction.None
end
---- Sao chép Notify tới nhiều back-end
addAction(AndRule({OpcodeRule(DNSOpcode.Notify)}), TeeAction("10.10.10.1"))
addAction(AndRule({OpcodeRule(DNSOpcode.Notify)}), TeeAction("10.10.20.1"))
-- Xóa bộ nhớ cache nếu back-end trả về NOERROR
addResponseAction(AndRule({OpcodeRule(DNSOpcode.Notify), RCodeRule(DNSRCode.NOERROR)}), LuaResponseAction(flushCache))
---- Cấu hình bộ nhớ cache
pc = newPacketCache(1000000, {maxTTL=300, minTTL=0, staleTTL=60, dontAge=false})
getPool(""):setCache(pc)
Thực hiện lệnh sau để kiểm tra cấu hình
dnsdist --check-config
# Kết quả
Configuration '/etc/dnsdist/dnsdist.conf' OK!
Nếu kết quả là OK thì chúng ta tiến hành restart lại dnsdist trên cả 2 server
systemctl restart dnsdist.service
systemctl status dnsdist.service
4.2 Kiểm tra hoạt động
for i in {1..1000}; do echo $i:$(dig @10.10.40.30 gocit.vn -t A); done
for i in {1..1000}; do echo $i:$(dig @10.10.40.30 gocit.vn -t any); done
Truy cập vào web để xem thêm thông tin http://10.10.40.30:8083, có thể thấy địa chỉ IP truy cập query quá nhiều đã bị blocked, và server ns-slave sẽ nhận được nhiều query hơn (gấp đôi) so với nsmaster do có cấu hình weight bằng 2.
Kết luận
Bài hướng dẫn trên là do mình dựng máy ảo vào build lại mô phỏng dựa trên mô hình của công ty mình đang triển khai và sử dụng.
Nếu các bạn có câu hỏi hay ý kiến khác thì hãy để lại bình luận phía dưới nhé!