Triển khai Pi-hole với DNS-over-HTTPS qua Cloudflared: Chặn mã độc và bảo vệ quyền riêng tư mạng

Security tutorial - IT technology blog
Security tutorial - IT technology blog

Vấn đề: DNS không mã hóa và nguy cơ rò rỉ dữ liệu telemetry âm thầm

Mỗi khi một thiết bị trong mạng nội bộ kết nối tới một dịch vụ web, quá trình luôn bắt đầu bằng một truy vấn DNS không mã hóa qua cổng UDP 53. Smart TV liên tục gửi dữ liệu chẩn đoán telemetry, các thiết bị IoT âm thầm kết nối về các máy chủ không xác định, còn hệ điều hành thì phát đi các truy vấn telemetry ngầm sau mỗi vài giây. Do DNS tiêu chuẩn hoàn toàn không được mã hóa, nhà cung cấp dịch vụ Internet (ISP) hoặc bất kỳ ai có khả năng nghe lén thụ động đều có thể ghi lại tường tận từng tên miền mà các thiết bị trong nhà bạn truy cập.

Các trình chặn quảng cáo thông thường chỉ hoạt động bên trong phạm vi trình duyệt, khiến cho ứng dụng di động, các thiết bị phần cứng kết nối mạng và những tiến trình daemon chạy ngầm hoàn toàn không được bảo vệ. Khi các tên miền độc hại phát tán phần mềm độc hại (malware) hoặc tải về mã điều khiển từ xa (C2), các thiết bị đầu cuối thông thường vẫn phân giải những địa chỉ IP đó mà không gặp phải bất kỳ rào cản nào.

Nguyên nhân gốc rễ: Phân giải bản rõ và sự thiếu vắng của DNS Sinkhole

Đặc tả giao thức DNS truyền thống được thiết kế từ nhiều thập kỷ trước mà không đi kèm bất kỳ cơ chế đảm bảo quyền riêng tư nào. Các truy vấn dạng văn bản rõ (plaintext) rất dễ bị can thiệp bởi các cuộc tấn công man-in-the-middle, giả mạo DNS (DNS spoofing) và thu thập siêu dữ liệu (metadata). Thậm chí, việc chuyển hướng máy chủ phân giải thượng nguồn (upstream resolver) còn cho phép các nhà mạng chèn trang quảng cáo hoặc cảnh báo khi nhận được phản hồi NXDOMAIN.

Để giải quyết triệt để cả vấn đề theo dõi lẫn giám sát, bạn cần kết hợp hai lớp bảo vệ song hành: một DNS sinkhole cục bộ giúp kiểm tra các truy vấn tên miền dựa trên danh sách chặn bảo mật (blocklist), và một lớp vỏ bọc truyền tải được mã hóa (DNS-over-HTTPS) nhằm che chắn các truy vấn gửi lên upstream khỏi sự dòm ngó từ bên ngoài.

Bắt đầu nhanh (5 phút)

Bạn có thể khởi chạy Pi-hole và Cloudflared trong Docker chỉ trong vòng năm phút. Sau khi máy chủ của tôi từng bị tấn công brute-force SSH vào lúc nửa đêm, tôi luôn ưu tiên bảo mật ngay từ bước thiết lập ban đầu; vì vậy hãy đảm bảo tường lửa trên máy chủ chỉ mở cổng 53 cho các mạng con (subnet) nội bộ đáng tin cậy.

Lưu cấu hình sau thành tệp docker-compose.yml trên máy chủ Linux của bạn:

version: "3.8"

services:
  cloudflared:
    image: cloudflare/cloudflared:latest
    container_name: cloudflared
    restart: unless-stopped
    command: proxy-dns --port 5053 --address 0.0.0.0 --upstream https://1.1.1.1/dns-query --upstream https://1.0.0.1/dns-query
    networks:
      pihole_net:
        ipv4_address: 172.20.0.2

  pihole:
    image: pihole/pihole:latest
    container_name: pihole
    restart: unless-stopped
    ports:
      - "53:53/tcp"
      - "53:53/udp"
      - "8080:80/tcp"
    environment:
      TZ: "UTC"
      WEBPASSWORD: "ChangeThisSecurePassword123!"
      PIHOLE_DNS_: "172.20.0.2#5053"
      DNSSEC: "true"
    volumes:
      - ./etc-pihole:/etc/pihole
      - ./etc-dnsmasq.d:/etc/dnsmasq.d
    networks:
      pihole_net:
        ipv4_address: 172.20.0.3
    depends_on:
      - cloudflared

networks:
  pihole_net:
    driver: bridge
    ipam:
      config:
        - subnet: 172.20.0.0/24

Khởi chạy hệ thống và kiểm tra xem các container đã hoạt động ổn định chưa:

docker compose up -d
docker compose ps

Kiểm tra quá trình phân giải DNS qua sinkhole từ terminal cục bộ của bạn:

dig @127.0.0.1 -p 53 itfromzero.com

Truy vấn sẽ trả về địa chỉ IP đã phân giải, được định tuyến an toàn qua Cloudflared bằng kết nối HTTPS mã hóa. Truy cập bảng điều khiển quản trị Pi-hole tại http://<your-server-ip>:8080/admin bằng mật khẩu đã cấu hình trong tệp compose.

Đi sâu vào chi tiết: Kiến trúc và vòng đời của truy vấn

Hiểu rõ lộ trình chính xác mà một gói tin mạng đi qua sẽ giúp bạn dễ dàng chẩn đoán các sự cố về độ trễ cũng như việc gói tin bị chặn bởi chính sách.

1. Đánh chặn và lọc truy vấn từ máy khách

Khi một máy khách trong mạng nội bộ (máy trạm, điện thoại di động hoặc bộ điều nhiệt thông minh) gửi yêu cầu tra cứu cho tên miền tracker.adnetwork.com, nó sẽ truyền một truy vấn UDP/TCP tiêu chuẩn đến địa chỉ IP của Pi-hole trên cổng 53. Pi-hole sẽ đối chiếu truy vấn này với FTL engine đang thường trú trên bộ nhớ RAM (Faster Than Light DNS, được xây dựng dựa trên dnsmasq).

Nếu tên miền có trong danh sách chặn (blocklist) hoặc khớp với một biểu thức chính quy (regex), Pi-hole sẽ lập tức ngắt ngắn chu trình yêu cầu và trả về địa chỉ 0.0.0.0 (hoặc NXDOMAIN). Kết nối sẽ không bao giờ thoát ra khỏi mạng nội bộ của bạn, từ đó cắt đứt dữ liệu theo dõi telemetry và ngăn chặn việc tải về các payload độc hại.

2. Chuyển tiếp truy vấn tới Cloudflared

Nếu tên miền hợp lệ (ví dụ: itfromzero.com), Pi-hole sẽ kiểm tra bộ nhớ đệm (cache) cục bộ. Trong trường hợp cache miss (chưa có trong bộ nhớ đệm), nó sẽ chuyển tiếp truy vấn lên upstream tại địa chỉ 172.20.0.2#5053, nơi Cloudflared đang lắng nghe.

3. Đóng gói DoH và bắt tay TLS

Cloudflared tiếp nhận truy vấn định dạng mạng RFC 1035 tiêu chuẩn, đóng gói vào payload HTTP/2 hoặc HTTP/3 (RFC 8484) và thiết lập đường hầm TLS 1.3 gửi đi qua cổng TCP 443 tiêu chuẩn tới các endpoint anycast của Cloudflare (https://1.1.1.1/dns-query). Dưới góc nhìn của các hệ thống giám sát mạng upstream và nhà mạng (ISP), gói tin này hoàn toàn giống với lưu lượng duyệt web HTTPS thông thường, khiến cho việc kiểm tra gói tin sâu (DPI) và ghi lại siêu dữ liệu trở nên vô hiệu.

Cài đặt trực tiếp trên bare-metal (Phương án thay thế cho Docker)

Nếu bạn muốn chạy trực tiếp để giảm tải overhead của container trên Raspberry Pi cấu hình thấp hoặc máy chủ Debian minimal, hãy cài đặt Cloudflared theo phương thức native:

sudo mkdir -p --mode=0755 /usr/share/keyrings
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/cloudflared.list
sudo apt-get update && sudo apt-get install -y cloudflared

Tạo tệp cấu hình dịch vụ riêng trong /etc/default/cloudflared:

CLOUDFLARED_OPTS="--port 5053 --address 127.0.0.1 --upstream https://1.1.1.1/dns-query --upstream https://1.0.0.1/dns-query"

Cấu hình systemd để quản lý tiến trình daemon:

sudo useradd -s /usr/sbin/nologin -r -M cloudflared
sudo tee /etc/systemd/system/cloudflared.service <<EOF
[Unit]
Description=Proxy DNS-over-HTTPS Cloudflared
After=syslog.target network-online.target

[Service]
Type=simple
User=cloudflared
EnvironmentFile=/etc/default/cloudflared
ExecStart=/usr/local/bin/cloudflared proxy-dns $CLOUDFLARED_OPTS
Restart=on-failure
RestartSec=10
LimitNOFILE=1048576

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now cloudflared

Kiểm tra proxy cục bộ trực tiếp trên cổng 5053:

dig @127.0.0.1 -p 5053 cloudflare.com +short

Cấu hình nâng cao

Cấu hình Upstream dự phòng và cân bằng tải

Việc phụ thuộc vào một nhà cung cấp DoH duy nhất có thể tạo ra điểm lỗi đơn lẻ (single point of failure). Cloudflared hỗ trợ đồng thời nhiều endpoint upstream và tự động cân bằng tải giữa các endpoint này thông qua cơ chế kiểm tra tình trạng (health check):

cloudflared proxy-dns \
  --port 5053 \
  --address 127.0.0.1 \
  --upstream https://1.1.1.1/dns-query \
  --upstream https://1.0.0.1/dns-query \
  --upstream https://dns.quad9.net/dns-query

Việc bổ sung Quad9 mang lại một lớp kiểm chứng thứ hai trước các hạ tầng liên quan đến mã độc, tăng cường tính năng phòng thủ theo chiều sâu (defense-in-depth) ngay từ giai đoạn phân giải tên miền.

Xác thực DNSSEC nghiêm ngặt

Kích hoạt DNSSEC trong bảng quản trị của Pi-hole tại Settings > DNS > Use DNSSEC, hoặc thiết lập DNSSEC=true trong biến môi trường của container. Khi đó, Pi-hole sẽ xác thực chữ ký mật mã trên các bản ghi DNS được trả về từ các vùng (zone) đã ký. Nếu một bản ghi bị can thiệp hoặc giả mạo trên đường truyền, Pi-hole sẽ loại bỏ kết quả tra cứu và gắn cờ phản hồi là BOGUS, giúp ngăn chặn triệt để các cuộc tấn công đầu độc bộ nhớ đệm (cache-poisoning).

Bản ghi tên miền nội bộ và Conditional Forwarding

Để phân giải các máy chủ nội bộ (chẳng hạn như nas.home.local hoặc proxmox.lan) trong khi vẫn duy trì cơ chế sinkhole cho lưu lượng ra ngoài, hãy thêm trực tiếp các bản ghi tùy chỉnh vào /etc/pihole/custom.list:

192.168.1.10 nas.home.local
192.168.1.20 hypervisor.home.local

Ngoài ra, bạn có thể điều hướng tới Settings > DNS > Conditional Forwarding và trỏ các truy vấn tra cứu ngược (reverse lookup) của mạng con nội bộ về router chính hoặc máy chủ DHCP.

Kinh nghiệm thực tế

Ngăn chặn thiết bị IoT bỏ qua DNS bằng IP cấu hình cứng

Nhiều thiết bị thông minh (như Chromecast, camera an ninh và smart TV) thường bỏ qua máy chủ DNS do DHCP cấp phát và gắn cứng (hardcode) các máy chủ DNS công cộng như 8.8.8.8 hoặc 1.1.1.1. Để ngăn chặn các thiết bị này vượt qua sinkhole, hãy thêm các quy tắc tường lửa trên router của bạn (chẳng hạn như pfSense, OPNsense hoặc OpenWrt) để đánh chặn hoặc chặn hoàn toàn lưu lượng trái phép trên cổng 53:

# Ví dụ quy tắc iptables để chuyển hướng toàn bộ lưu lượng cổng 53 đi ra ngoài về Pi-hole
iptables -t nat -A PREROUTING -i eth1 -p udp ! -d 192.168.1.5 --dport 53 -j DNAT --to-destination 192.168.1.5:53
iptables -t nat -A PREROUTING -i eth1 -p tcp ! -d 192.168.1.5 --dport 53 -j DNAT --to-destination 192.168.1.5:53

Thay thế 192.168.1.5 bằng địa chỉ IP tĩnh của máy chủ Pi-hole của bạn.

Tinh chỉnh kích thước bộ nhớ đệm để đạt hiệu năng cao

Theo mặc định, Pi-hole lưu vào bộ nhớ đệm 10.000 bản ghi. Đối với những môi trường có nhiều container hoạt động hoặc hàng chục microservice tạo ra khối lượng truy vấn lớn, hãy tinh chỉnh kích thước cache để giảm mức sử dụng CPU và tránh các yêu cầu gửi lên upstream không cần thiết. Tạo tệp cấu hình tùy chỉnh tại /etc/dnsmasq.d/02-custom-cache.conf:

cache-size=25000
min-cache-ttl=300

Khởi động lại dịch vụ phân giải DNS để áp dụng các giới hạn mới:

pihole restartdns

Tự động hóa cập nhật Blocklist và sao lưu

Pi-hole tự động cập nhật danh sách gravity hàng tuần thông qua cron, nhưng việc duy trì các blacklist và whitelist tùy chỉnh đòi hỏi bạn phải có thói quen sao lưu định kỳ. Hãy sử dụng tính năng Pi-hole Teleporter để tự động trích xuất các tệp cấu hình bằng tác vụ cron chạy hàng đêm:

pihole -a -t /var/backups/pihole/pihole-backup-$(date +\%F).tar.gz

Kết hợp lệnh sao lưu này với tập lệnh backup bên ngoài để đảm bảo khả năng phục hồi nhanh chóng nếu bộ nhớ máy chủ gặp sự cố.

Share: