Xây dựng Transparent Proxy trên Linux với Redsocks và Iptables

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

Nỗi đau đầu từ các ứng dụng không hỗ trợ Proxy

Tôi thường gặp phải một sự ức chế cụ thể khi quản lý các máy chủ Linux từ xa hoặc môi trường IoT: các ứng dụng từ chối tuân thủ cài đặt proxy. Bạn thiết lập các biến môi trường HTTP_PROXYHTTPS_PROXY, nhưng ứng dụng vẫn phớt lờ chúng, cố gắng kết nối trực tiếp và thất bại vì bị chặn bởi tường lửa nghiêm ngặt. Trong nhiều trường hợp, bạn cần tìm cách vượt rào DPI để duy trì kết nối ổn định cho các tác vụ quản trị.

Điều này thường xuyên xảy ra với các phần mềm cũ (legacy), một some công cụ CLI và các thiết bị nhúng như Smart TV hoặc các dự án Raspberry Pi. Nguyên nhân gốc rễ là các ứng dụng này không “nhận diện proxy” (proxy-aware). Chúng không kiểm tra các biến hệ thống hoặc không có menu cấu hình để nhập chi tiết proxy. Chúng kỳ vọng một đường dẫn trực tiếp ra internet.

Giải pháp không phải là sửa đổi từng ứng dụng một. Thay vào đó, tôi chuyển logic xuống lớp mạng. Bằng cách xây dựng một Transparent Proxy (Proxy trong suốt), hệ điều hành sẽ chặn lưu lượng ở cấp độ kernel và chuyển hướng nó qua một proxy mà ứng dụng không hề hay biết. Để thực hiện điều này trên Linux, sự kết hợp giữa RedsocksIptables là lựa chọn hàng đầu của tôi.

So sánh các phương pháp định tuyến

Trước khi đi vào chi tiết triển khai, chúng ta cần hiểu vị trí của Transparent Proxy so với các phương pháp khác mà tôi đã từng sử dụng trong quá khứ.

  • Standard Proxy (SOCKS/HTTP): Yêu cầu cấu hình ở cấp độ ứng dụng. Nếu ứng dụng không hỗ trợ, bạn sẽ gặp bế tắc.
  • System-wide VPN (Tun/Tap): Điều hướng mọi thứ, nhưng có thể là quá mức cần thiết nếu bạn chỉ có sẵn tunnel SOCKS5 hoặc nếu giao thức VPN bị nhà cung cấp lớp trên chặn, hoặc đôi khi gặp lỗi ICMP Black Hole làm gián đoạn đường truyền.
  • Transparent Proxy (Redsocks + Iptables): Chuyển hướng các gói tin TCP/UDP cụ thể đến một daemon cục bộ (Redsocks), sau đó nó sẽ chuyển tiếp chúng đến proxy thực sự của bạn. Phương pháp này có độ chính xác cao và không yêu cầu driver VPN chuyên dụng ở phía máy khách.

Ưu và nhược điểm của Transparent Proxying

Ưu điểm

  • Cấu hình bằng không (Zero Configuration): Không cần chạm vào cài đặt ứng dụng. Nếu nó gửi một gói tin, gói tin đó sẽ được qua proxy.
  • Kiểm soát chi tiết: Bạn có thể chọn chỉ proxy các cổng cụ thể (ví dụ: chỉ 80 và 443) hoặc các IP đích cụ thể.
  • Khả năng tương thích: Hoạt động với mọi ngôn ngữ (Python, Go, Java, C++) vì việc chuyển hướng xảy ra trong kernel Linux (Netfilter).

Nhược điểm

  • Độ phức tạp: Thiết lập các quy tắc Iptables một cách chính xác là khá khó khăn. Một sai sót nhỏ có thể khiến bạn bị khóa khỏi máy chủ hoặc tạo ra một vòng lặp vô hạn.
  • Rò rỉ DNS (DNS Leaks): Các quy tắc Iptables tiêu chuẩn thường chỉ xử lý TCP. DNS (UDP 53) yêu cầu các bước bổ sung để đảm bảo ISP không theo dõi các truy vấn của bạn.

Thiết lập được đề xuất

Trong thiết lập này, tôi giả định bạn đã có một máy chủ proxy SOCKS5 hoặc HTTP đang hoạt động (có thể là một SSH tunnel hoặc từ nhà cung cấp thương mại). Chúng ta sẽ sử dụng một máy Linux (Ubuntu/Debian) làm gateway hoặc máy chủ cục bộ.

Tôi đã áp dụng phương pháp này trong môi trường production và kết quả đạt được rất ổn định, đặc biệt là khi bạn cần tối ưu hóa mạng Linux cho các bot thu thập dữ liệu tự động không hỗ trợ SOCKS5 nguyên bản.

[Ứng dụng] -> [Iptables (Chuyển hướng)] -> [Redsocks (Cổng cục bộ)] -> [Máy chủ Proxy từ xa] -> [Internet]

Từng bước triển khai

1. Cài đặt Redsocks

Đầu tiên, hãy cài đặt Redsocks daemon. Nó đóng vai trò là cầu nối giữa việc chuyển hướng của Iptables và máy chủ proxy thực sự của bạn.

sudo apt update
sudo apt install redsocks -y

2. Cấu hình Redsocks

Tệp cấu hình thường nằm tại /etc/redsocks.conf. Chúng ta cần cho Redsocks biết cổng cục bộ nào cần lắng nghe và máy chủ proxy lớp trên của bạn nằm ở đâu.

Chỉnh sửa tệp: sudo nano /etc/redsocks.conf. Tìm phần redsocks:

redsocks {
    /* local_ip là địa chỉ mà redsocks sẽ lắng nghe */
    local_ip = 127.0.0.1;
    local_port = 12345; // Đây là nơi Iptables sẽ gửi lưu lượng đến

    /* IP và cổng của proxy SOCKS5/HTTP thực tế của bạn */
    ip = 1.2.3.4;
    port = 1080;

    /* type có thể là: socks4, socks5, http-connect, http-relay */
    type = socks5;
}

Sau khi lưu, hãy khởi động lại dịch vụ:

sudo systemctl restart redsocks

3. Định tuyến lưu lượng với Iptables

Đây là phần quan trọng nhất. Chúng ta cần tạo một chain mới trong Iptables để xử lý việc chuyển hướng. Điều này giúp chúng ta tránh làm xáo trộn các chain mặc định như OUTPUT hoặc PREROUTING.

Tôi khuyên bạn nên tạo một script (ví dụ: proxy_on.sh) vì các quy tắc này sẽ bị mất khi khởi động lại trừ khi được lưu lại.

# Tạo một chain mới tên là REDSOCKS
sudo iptables -t nat -N REDSOCKS

# Bỏ qua lưu lượng đến máy chủ proxy của bạn (để tránh vòng lặp vô hạn)
sudo iptables -t nat -A REDSOCKS -d 1.2.3.4 -j RETURN

# Bỏ qua mạng LAN và các dải IP dự phòng (chúng ta không muốn proxy lưu lượng nội bộ)
sudo iptables -t nat -A REDSOCKS -d 0.0.0.0/8 -j RETURN
sudo iptables -t nat -A REDSOCKS -d 10.0.0.0/8 -j RETURN
sudo iptables -t nat -A REDSOCKS -d 127.0.0.0/8 -j RETURN
sudo iptables -t nat -A REDSOCKS -d 169.254.0.0/16 -j RETURN
sudo iptables -t nat -A REDSOCKS -d 172.16.0.0/12 -j RETURN
sudo iptables -t nat -A REDSOCKS -d 192.168.0.0/16 -j RETURN

# Chuyển hướng tất cả lưu lượng TCP còn lại đến cổng cục bộ của Redsocks
sudo iptables -t nat -A REDSOCKS -p tcp -j REDIRECT --to-ports 12345

# Áp dụng chain cho tất cả lưu lượng đi ra từ máy này
sudo iptables -t nat -A OUTPUT -p tcp -j REDSOCKS

Nếu bạn đang thiết lập cấu hình này trên một router/gateway để proxy lưu lượng cho các thiết bị khác trong mạng, bạn sẽ sử dụng chain PREROUTING thay vì OUTPUT:

sudo iptables -t nat -A PREROUTING -p tcp -s 192.168.1.0/24 -j REDSOCKS

4. Xử lý DNS

Theo mặc định, Redsocks chỉ xử lý TCP. Nếu các truy vấn DNS của bạn vẫn gửi đến ISP địa phương, bạn có thể đối mặt với tình trạng “Rò rỉ DNS” hoặc việc proxy có thể thất bại nếu DNS bị nhiễm độc. Tôi thường xử lý vấn đề này bằng cách ép buộc DNS qua TCP hoặc sử dụng một DNS forwarder chuyên dụng.

Một giải pháp nhanh chóng là sử dụng DNS công cộng như 1.1.1.1 và đảm bảo Redsocks được cấu hình để xử lý UDP (yêu cầu phần dnstc trong redsocks.conf), nhưng đối với hầu hết các trường hợp sử dụng cơ bản, việc đảm bảo ứng dụng sử dụng một IP DNS cố định là đủ để đi qua tunnel nếu proxy hỗ trợ.

Kiểm tra thiết lập

Để xác minh rằng transparent proxy đang hoạt động, tôi sử dụng một lệnh curl đơn giản mà không có bất kỳ cờ proxy nào:

curl ifconfig.me

Nếu IP trả về khớp với IP của máy chủ proxy chứ không phải IP ISP địa phương của bạn, việc chuyển hướng đã thành công. Bạn cũng có thể phân tích pcap chuyên sâu bằng tshark để xem các kết nối đang được xử lý chi tiết hơn:

sudo journalctl -u redsocks -f

Lưu cấu hình và dọn dẹp

Các quy tắc Iptables sẽ bị reset khi bạn khởi động lại máy. Để lưu chúng vĩnh viễn, tôi sử dụng iptables-persistent:

sudo apt install iptables-persistent
sudo netfilter-persistent save

Nếu bạn cần tắt proxy ngay lập tức mà không dừng dịch vụ, chỉ cần xóa quy tắc jump khỏi chain OUTPUT:

sudo iptables -t nat -D OUTPUT -p tcp -j REDSOCKS

Tổng kết

Thiết lập một transparent proxy với Redsocks và Iptables là một cách mạnh mẽ để bắt buộc các ứng dụng không tuân thủ quy tắc phải hoạt động theo ý muốn. Nó cung cấp một điểm kiểm soát tập trung cho lưu lượng của bạn, đảm bảo rằng ngay cả công cụ CLI cứng đầu nhất hoặc thiết bị IoT cũng nằm trong tunnel bảo mật của bạn. Mặc dù cấu hình Iptables ban đầu có thể gây nản lòng, nhưng sự ổn định và tính chất “thiết lập một lần rồi quên” của giải pháp này khiến nó trở thành một phần không thể thiếu trong bộ công cụ mạng của tôi.

Share: