Sáu tháng trước, tôi cảm thấy không còn thoải mái khi các truy vấn DNS liên tục bị rò rỉ tới các upstream thương mại như Google hay Cloudflare. Các tiện ích chặn quảng cáo thông thường chỉ loại bỏ banner trên trình duyệt, nhưng vẫn chuyển tiếp các truy vấn tên miền chưa mã hóa về phía downstream. Việc kết hợp AdGuard Home với Unbound sẽ giải quyết triệt để vấn đề này từ gốc rễ.
AdGuard đóng vai trò như một bộ lọc tiền tuyến, vô hiệu hóa (sinkhole) quảng cáo, dữ liệu đo lường từ xa (telemetry) và các tên miền theo dõi trên toàn bộ mạng LAN. Unbound đứng ngay phía sau như một recursive resolver có xác thực DNSSEC, giao tiếp trực tiếp với các root nameserver có thẩm quyền. Sau khi ghi nhận hơn 120.000 truy vấn mỗi ngày từ hơn 40 thiết bị trong nhà, cụm 2 container này đã mang lại tốc độ phản hồi cực kỳ ổn định chỉ 1ms đối với dữ liệu đệm (cache) và hoàn toàn không bị upstream thu thập hồ sơ người dùng.
Bắt đầu nhanh (5 phút)
Bạn có thể khởi chạy cụm dịch vụ này trên bất kỳ máy Linux nào (Debian, Ubuntu hoặc Raspberry Pi OS) đã cài đặt sẵn Docker và Docker Compose.
1. Chuẩn bị cấu trúc thư mục trên máy chủ
mkdir -p ~/homelab-dns/{adguard/work,adguard/conf,unbound}
cd ~/homelab-dns
2. Cấu hình Unbound
Tạo tệp cấu hình cho Unbound tại unbound/unbound.conf. Thiết lập này kích hoạt quá trình đệ quy lặp (iterative recursion) từ máy chủ gốc, xác thực DNSSEC nghiêm ngặt và gán cổng lắng nghe nội bộ tại 5335:
cat <<'EOF' > unbound/unbound.conf
server:
verbosity: 1
interface: 0.0.0.0
port: 5335
do-ip4: yes
do-udp: yes
do-tcp: yes
do-ip6: no
prefer-ip6: no
# Kiểm soát truy cập
access-control: 127.0.0.1/32 allow
access-control: 192.168.0.0/16 allow
access-control: 172.16.0.0/12 allow
access-control: 10.0.0.0/8 allow
# Bảo mật & Hiệu năng
hide-identity: yes
hide-version: yes
harden-glue: yes
harden-dnssec-stripped: yes
use-caps-for-id: no
edns-buffer-size: 1232
prefetch: yes
prefetch-key: yes
num-threads: 1
# Bộ nhớ đệm (Caching)
msg-cache-size: 64m
rrset-cache-size: 128m
infra-cache-numhosts: 10000
EOF
3. Tạo tệp Docker Compose
Lưu cấu hình sau thành tệp docker-compose.yml trong thư mục làm việc của bạn:
version: "3.8"
networks:
dns_net:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/24
services:
unbound:
image: mvance/unbound:latest
container_name: unbound-resolver
restart: unless-stopped
networks:
dns_net:
ipv4_address: 172.20.0.2
volumes:
- ./unbound/unbound.conf:/opt/unbound/etc/unbound/unbound.conf:ro
adguardhome:
image: adguard/adguardhome:latest
container_name: adguard-home
restart: unless-stopped
depends_on:
- unbound
networks:
dns_net:
ipv4_address: 172.20.0.3
ports:
- "53:53/tcp"
- "53:53/udp"
- "3000:3000/tcp" # Trình hướng dẫn thiết lập web ban đầu
- "8080:80/tcp" # Giao diện web quản trị
volumes:
- ./adguard/work:/opt/adguardhome/work
- ./adguard/conf:/opt/adguardhome/conf
4. Giải phóng cổng 53 và khởi chạy các container
Mặc định trên Ubuntu và Debian, dịch vụ systemd-resolved sẽ lắng nghe trên cổng 127.0.0.53:53. Xung đột cổng này sẽ khiến AdGuard không thể khởi động. Hãy vô hiệu hóa stub listener trước khi chạy container:
sudo sed -r -i.orig 's/#?DNSStubListener=yes/DNSStubListener=no/g' /etc/systemd/resolved.conf
sudo systemctl restart systemd-resolved
# Khởi chạy cụm dịch vụ
docker compose up -d
Mở http://<YOUR_SERVER_IP>:3000 trên trình duyệt để hoàn tất trình hướng dẫn cài đặt ban đầu của AdGuard Home. Đặt giao diện web quản trị lắng nghe trên cổng 80 (truy cập từ máy chủ qua cổng 8080) và DNS lắng nghe trên tất cả các interface tại cổng 53.
Phân tích chuyên sâu
Hiểu rõ cách thức các truy vấn luân chuyển qua kiến trúc hai tầng này sẽ giúp việc chẩn đoán và khắc phục sự cố trở nên đơn giản, trực quan hơn.
Cơ chế phân giải truy vấn hoạt động như thế nào
Mọi yêu cầu truy vấn từ mạng của bạn đều đi qua một quy trình 5 bước liền mạch:
- Truy vấn từ Client: Một thiết bị trong mạng LAN gửi yêu cầu DNS tiêu chuẩn đến IP máy chủ của bạn qua cổng 53 (UDP/TCP).
- Lọc qua AdGuard: AdGuard đối chiếu tên miền với các danh sách chặn (blocklist) đang kích hoạt. Nếu khớp với một tracker hoặc máy chủ quảng cáo, AdGuard sẽ phản hồi ngay lập tức địa chỉ
0.0.0.0trong chưa đầy 1ms. - Phân giải đệ quy lặp trong Unbound: Các truy vấn hợp lệ được chuyển tiếp nội bộ đến Unbound tại
172.20.0.2:5335. Thay vì phụ thuộc vào một dịch vụ DNS đệ quy bên thứ ba, Unbound sẽ truy vấn các máy chủ gốc Root zone (.), đi dần xuống các máy chủ tên miền cấp cao nhất TLD (như.com), và cuối cùng lấy bản ghi chính xác từ các authoritative nameserver. - Xác thực DNSSEC: Unbound xác thực chữ ký mã hóa dọc theo chuỗi tin cậy (trust chain). Nếu chữ ký bị giả mạo hoặc bị tước bỏ, Unbound sẽ hủy bản ghi và trả về lỗi
SERVFAILđể ngăn chặn tấn công đầu độc bộ nhớ đệm (cache poisoning). - Lưu đệm & Phản hồi: AdGuard lưu kết quả vào bộ nhớ cục bộ và trả về IP cuối cùng cho thiết bị client.
Mặc dù các truy vấn đệ quy khi bộ nhớ đệm còn trống (cold lookup) mất khoảng 45–90ms qua chuỗi root hints, cơ chế lưu đệm tích cực sẽ giúp độ trễ thực tế duy trì ở mức rất thấp. Trong quá trình vận hành hàng ngày, từ 85% đến 92% truy vấn được xử lý ngay trên RAM cục bộ, trả về kết quả trong chưa đầy 2ms mà không cần chạm tới mạng WAN bên ngoài.
Kết nối AdGuard với Unbound
Mở Settings > DNS settings trong bảng điều khiển web của AdGuard Home và cấu hình các tham số sau:
- Upstream DNS servers:
172.20.0.2:5335 - Bootstrap DNS servers:
1.1.1.1:53(chỉ dùng duy nhất để phân giải URL của các danh sách chặn ban đầu khi khởi động container) - Upstream mode: Parallel requests hoặc Load balancing
Nhấn Test upstreams. Biểu tượng xác nhận màu xanh lá sẽ xuất hiện để thông báo Unbound đang phân giải các bản ghi đúng như mong đợi.
Cấu hình nâng cao
Khi quá trình phân giải cơ bản đã hoạt động trơn tru, bạn có thể tùy chỉnh hệ thống để phục vụ định tuyến nội bộ và tự động hóa bảo trì.
1. Split-Horizon DNS cho các tên miền nội bộ
Bạn đang chạy các dịch vụ nội bộ phía sau Traefik hoặc Nginx Proxy Manager? Hãy tránh hiện tượng hairpin NAT bằng cách định tuyến các tên miền riêng tư thẳng tới reverse proxy của bạn (ví dụ: 192.168.1.50). Trong AdGuard Home, điều hướng tới Filters > Custom filtering rules và thêm:
# Định tuyến các tên miền nội bộ về reverse proxy cục bộ
||home.lab^$dnsrewrite=192.168.1.50
||*.home.lab^$dnsrewrite=192.168.1.50
2. Lựa chọn danh sách chặn và tối ưu hiệu năng
Việc nạp hàng triệu quy tắc dư thừa sẽ làm giảm hiệu năng mà không mang lại thêm khả năng bảo vệ đáng kể nào. Đối với mạng gia đình cân bằng tối ưu với khoảng 350.000 quy tắc, hãy kích hoạt các danh sách sau tại mục Filters > DNS blocklists:
- AdGuard DNS filter: Lớp bảo vệ cơ bản xuất sắc chống lại quảng cáo web và các trình theo dõi phổ biến.
- OISD (Basic / Full): Độ bao phủ rộng khắp internet được tuyển chọn kỹ lưỡng nhằm giảm thiểu tối đa tình trạng nhận diện nhầm.
- HaGeZi Multi Pro: Lá chắn toàn diện ngăn chặn đo lường từ xa xâm lấn, theo dõi tiếp thị liên kết và các tên miền mã độc.
3. Tự động cập nhật Root Hints
Unbound tham chiếu địa chỉ IP của các máy chủ gốc thông qua tệp IANA Root Hints. Mặc dù các địa chỉ này rất hiếm khi thay đổi, việc làm mới tệp này hai lần một năm là một thói quen vận hành tốt. Hãy thêm một tác vụ cron job đơn giản chạy hàng tháng trên máy chủ của bạn:
# Tải root hints mới vào ngày mùng 1 hàng tháng và khởi động lại Unbound
0 3 1 * * curl -s -o ~/homelab-dns/unbound/root.hints https://www.internic.net/domain/named.root && docker restart unbound-resolver
Kinh nghiệm thực tế
Tự vận hành một DNS resolver đệ quy có thể phát sinh một số trường hợp ngoại lệ tinh vi. Dưới đây là 4 đề xuất đã qua thử nghiệm thực tế giúp mạng của bạn luôn nhanh và ổn định:
- Bật Optimistic Caching: Trong mục DNS settings của AdGuard, hãy đặt kích thước bộ nhớ đệm thành
67108864(64MB) và bật tùy chọn Optimistic caching. AdGuard sẽ trả lời client ngay lập tức bằng các bản ghi đã hết hạn trong bộ nhớ đệm, đồng thời âm thầm làm mới chúng dưới nền, giúp việc lướt web luôn tức thì. - Giữ nguyên vô hiệu hóa
use-caps-for-id: Cơ chế ngẫu nhiên hóa bit 0x20 sẽ thay đổi chữ hoa/thường (ví dụ:eXaMpLe.CoM) để chống giả mạo. Tuy nhiên, nhiều mạng phân phối nội dung (CDN) lớn và authoritative nameserver hiện vẫn không xử lý đúng các truy vấn có chữ hoa chữ thường lẫn lộn, dẫn đến lỗi timeout truy vấn chập chờn. - Thiết lập quy tắc lọc theo từng Client: Sử dụng mục Client settings của AdGuard để phân nhóm theo subnet IP. Bạn có thể áp dụng chính sách chặn quảng cáo và mã độc nghiêm ngặt đối với thiết bị IoT hay Smart TV, trong khi miễn trừ các bộ lọc phân tích nghiêm ngặt cho máy tính làm việc của lập trình viên.
- Lên phương án dự phòng khi máy chủ khởi động lại: Nếu máy chủ Docker khởi động lại để cập nhật kernel, toàn bộ gia đình bạn sẽ mất kết nối DNS. Hãy cấu hình một IP phụ dự phòng trong tùy chọn DHCP của router hoặc chạy song song một node phụ trên Raspberry Pi để tránh sự cố gián đoạn mạng ngoài ý muốn.

