Split-Horizon DNS với BIND9: Ngăn chặn lỗi Hairpinning cho lưu lượng nội bộ

Networking tutorial - IT technology blog
Networking tutorial - IT technology blog

Vòng lặp định tuyến lúc 2 giờ 15 sáng

2:15 sáng: Điện thoại của tôi reo inh ỏi với các cảnh báo từ PagerDuty. Đội ngũ phát triển nội bộ không thể truy cập vào môi trường staging, và mọi yêu cầu đều gặp lỗi connection timeout. Sau năm phút tìm hiểu, tôi đã tìm ra thủ phạm: các máy chủ nội bộ đang cố gắng truy cập cổng thông tin staging thông qua địa chỉ IP công cộng của nó. Lưu lượng rời khỏi mạng cục bộ, đi đến ISP và cố gắng quay ngược trở lại (hairpin) qua tường lửa. Tường lửa, khi nhận thấy rủi ro bảo mật, đã ngay lập tức loại bỏ các gói tin này.

Vấn đề kiến trúc này rất phổ biến. Hãy tưởng tượng một dịch vụ như portal.example.com. Một khách hàng ở London sẽ thấy IP công cộng, chẳng hạn như 203.0.113.10. Tuy nhiên, một nhân viên trong văn phòng của bạn nên nhận được IP nội bộ, như 192.168.1.10. Việc ép lưu lượng nội bộ đi ra ngoài Internet công cộng chỉ để quay ngược trở lại là rất kém hiệu quả. Nó làm tăng thêm 40-60ms độ trễ không cần thiết và tạo ra một điểm lỗi duy nhất (single point of failure) tại router biên của bạn.

Split-Horizon DNS (hay Split-View DNS) giải quyết vấn đề này. Bằng cách sử dụng BIND9 trên Linux, bạn có thể cấu hình máy chủ DNS của mình để phát hiện nguồn gốc của truy vấn và đưa ra phản hồi phù hợp. Theo kinh nghiệm của tôi, việc triển khai này giúp giảm tải CPU của tường lửa tới 30% trong môi trường lưu lượng cao và giữ cho lưu lượng nội bộ luôn nằm trong mạng cục bộ.

Cài đặt BIND9 trên Linux

Hầu hết các máy chủ DNS trong môi trường sản xuất đều chạy trên Debian hoặc Ubuntu do các bản vá bảo mật ổn định. Các bước này là tiêu chuẩn, nhưng chúng đảm bảo môi trường của bạn sẵn sàng cho các logic tùy chỉnh.

# Cập nhật danh sách gói phần mềm
sudo apt update

# Cài đặt BIND9 và các tiện ích thông dụng
sudo apt install bind9 bind9utils bind9-doc -y

# Đảm bảo dịch vụ khởi động cùng hệ thống
sudo systemctl enable bind9
sudo systemctl start bind9

Chạy lệnh systemctl status bind9 để xác nhận dịch vụ đang hoạt động. Đừng bao giờ bỏ qua bước này; bạn cần một nền tảng sạch trước khi sửa đổi các tệp cấu hình.

Cấu hình: Triển khai Views

BIND9 sử dụng “Views” để phân chia không gian tên DNS. Có một quy tắc nghiêm ngặt: nếu bạn sử dụng views, mọi zone phải được định nghĩa bên trong một view. Bạn không thể trộn lẫn các zone toàn cục với các zone được phân chia theo view.

1. Định nghĩa Access Control Lists (ACLs)

Bắt đầu bằng cách xác định những ai được coi là “nội bộ”. Mở tệp /etc/bind/named.conf.options. Tôi thích định nghĩa các ACL tại đây để giữ cho việc thiết lập được tập trung. Chúng ta sẽ bao gồm địa chỉ loopback và các dải IP nội bộ (CIDR) của bạn.

acl "trusted" {
        127.0.0.0/8;
        192.168.1.0/24;
        10.0.0.0/8;
};

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

        recursion yes;
        allow-query { any; };

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

2. Thiết lập các View

Chỉnh sửa tệp /etc/bind/named.conf.local để định nghĩa các view Internal (Nội bộ) và External (Bên ngoài). BIND xử lý các view này từ trên xuống dưới. View đầu tiên khớp với IP nguồn của khách hàng sẽ được chọn.

# View Nội bộ
view "internal" {
    match-clients { "trusted"; };
    recursion yes;

    zone "example.com" {
        type master;
        file "/etc/bind/zones/db.example.com.internal";
    };

    include "/etc/bind/named.conf.default-zones";
};

# View Bên ngoài
view "external" {
    match-clients { any; };
    recursion no;

    zone "example.com" {
        type master;
        file "/etc/bind/zones/db.example.com.external";
    };
};

Người dùng nội bộ sẽ được phép recursion yes, biến máy chủ thành một DNS resolver đầy đủ để truy cập web. Đối với view bên ngoài, chúng ta tắt tính năng đệ quy. Điều này ngăn máy chủ của bạn bị lợi dụng trong các cuộc tấn công khuếch đại DNS (DNS amplification attacks).

3. Tạo các tệp Zone

Bạn cần hai phiên bản riêng biệt của tệp zone cho example.com. Tạo một thư mục riêng để quản lý dễ dàng hơn:

sudo mkdir /etc/bind/zones

Tệp Zone Nội bộ

Trong tệp /etc/bind/zones/db.example.com.internal, hãy ánh xạ các hostname của bạn tới IP nội bộ. Lưu ý chỉ số TTL thấp (600) nếu bạn dự kiến sẽ có thay đổi nội bộ thường xuyên.

$TTL    600
@       IN      SOA     ns1.example.com. admin.example.com. (
                              2023101001 ; Số Serial (Định dạng YYYYMMDDNN)
                         604800         ; Thời gian làm mới (Refresh)
                          86400         ; Thời gian thử lại (Retry)
                        2419200         ; Thời gian hết hạn (Expire)
                         604800 )       ; TTL cho bộ nhớ đệm âm (Negative Cache TTL)
;
@       IN      NS      ns1.example.com.
ns1     IN      A       192.168.1.5
portal  IN      A       192.168.1.10
web     IN      A       192.168.1.11

Tệp Zone Bên ngoài

Trong tệp /etc/bind/zones/db.example.com.external, hãy sử dụng các địa chỉ IP WAN công cộng của bạn.

$TTL    86400
@       IN      SOA     ns1.example.com. admin.example.com. (
                              2023101001 ; Số Serial
                         604800         ; Thời gian làm mới
                          86400         ; Thời gian thử lại
                        2419200         ; Thời gian hết hạn
                         604800 )       ; TTL cho bộ nhớ đệm âm
;
@       IN      NS      ns1.example.com.
ns1     IN      A       203.0.113.5
portal  IN      A       203.0.113.10
web     IN      A       203.0.113.11

Kiểm tra & Giám sát

DNS rất nhạy cảm. Một dấu chấm phẩy bị thiếu có thể làm sập toàn bộ khả năng phân giải tên miền của mạng. Luôn kiểm tra cú pháp trước khi khởi động lại dịch vụ.

# Kiểm tra cú pháp cấu hình BIND
sudo named-checkconf

# Kiểm tra các tệp zone cụ thể
sudo named-checkzone example.com /etc/bind/zones/db.example.com.internal
sudo named-checkzone example.com /etc/bind/zones/db.example.com.external

Nếu kết quả không có lỗi, hãy khởi động lại BIND: sudo systemctl restart bind9.

Kiểm tra logic hoạt động

Sử dụng lệnh dig để xác nhận các view đang hoạt động đúng. Nếu bạn chạy lệnh này từ máy chủ DNS (một IP tin cậy), bạn sẽ thấy địa chỉ 192.168.x.x.

dig @localhost portal.example.com

Để mô phỏng một yêu cầu từ bên ngoài, hãy chạy dig từ một VPS từ xa hoặc một điểm phát sóng di động. Bạn sẽ thấy địa chỉ 203.0.x.x thay thế. Nếu kết quả bị tráo đổi, hãy kiểm tra lại các dải ACL của bạn và đảm bảo view “internal” được liệt kê trước trong tệp cấu hình.

Giám sát nhật ký (Logs)

Nhật ký hệ thống là nơi tốt nhất để gỡ lỗi việc gán view. Sử dụng lệnh này để theo dõi các truy vấn trong thời gian thực:

journalctl -u bind9 -f

Tìm các dòng có chứa view internal: query. Điều này xác nhận BIND đang nhận diện chính xác người dùng nội bộ của bạn. Split-Horizon DNS không chỉ khắc phục lỗi định tuyến; nó còn thêm một lớp bảo mật bằng cách ẩn sơ đồ IP nội bộ của bạn khỏi công cộng. Đây là một giải pháp phía máy chủ gọn gàng cho một vấn đề mà thông thường đòi hỏi các quy tắc NAT phức tạp hoặc chỉnh sửa tệp hosts thủ công.

Share: