Vượt rào DPI: Cách tạo Tunnel cho WireGuard qua WebSockets với WSTunnel

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

Vấn đề với UDP trong các mạng bị hạn chế

WireGuard cực kỳ nhanh, nhưng nó có một điểm yếu chí mạng trong môi trường doanh nghiệp: nó chỉ sử dụng UDP. Hầu hết tường lửa doanh nghiệp và các hệ thống Deep Packet Inspection (DPI) đều được thiết lập chính sách “deny-all” (từ chối tất cả) đối với lưu lượng UDP. Chúng thường chặn mọi thứ trừ các truy vấn DNS trên cổng 53 để ngăn chặn các kết nối tunnel trái phép.

Nếu bạn từng thấy trạng thái VPN bị kẹt ở “Handshake did not complete” khi đang dùng Wi-Fi văn phòng, bạn đã gặp phải bức tường này. Trong thử nghiệm của tôi tại một địa điểm khách hàng sử dụng tường lửa Palo Alto Networks nghiêm ngặt, WireGuard tiêu chuẩn thất bại 100%. Để vượt qua điều này, chúng ta cần ngụy trang lưu lượng của mình thành thứ mà tường lửa đã tin tưởng: lưu lượng web HTTPS tiêu chuẩn.

Bằng cách bọc các gói tin WireGuard UDP bên trong một luồng WebSocket, chúng ta có thể đánh lừa tường lửa. Vì WebSockets khởi tạo thông qua một cái bắt tay (handshake) HTTP/1.1 hoặc HTTP/2 tiêu chuẩn, lưu lượng trông giống như một kết nối trình duyệt bình thường đến Slack, Discord hoặc một nền tảng giao dịch.

Hiểu về kiến trúc WSTunnel

Chúng ta sử dụng WSTunnel, một relay hiệu suất cao được viết bằng Rust, để xử lý các công việc nặng nhọc. Thay vì kết nối trực tiếp đến cổng UDP của máy chủ, client của bạn sẽ giao tiếp với một “cầu nối” (bridge) cục bộ để chuyển đổi lưu lượng thành WebSockets. Luồng hoạt động như sau:

  • Phía Client: WireGuard Client → WSTunnel cục bộ (UDP 51820) → WebSocket (TCP 443) → Internet.
  • Phía Server: Internet → WSTunnel Server (TCP 443) → WireGuard Server (Local UDP 51820).

Đối với một công cụ DPI, đây trông giống như một kết nối TLS được mã hóa. Nó thấy một header nâng cấp WebSocket tiêu chuẩn và cho qua. Điều này giúp ẩn lưu lượng VPN của bạn bên trong một đường hầm mà tường lửa thường buộc phải cho phép.

Bước 1: Cài đặt WSTunnel

WSTunnel là một tệp thực thi (binary) di động. Bạn cần nó trên cả máy chủ từ xa và máy cục bộ. Bạn có thể tải bản phát hành mới nhất từ GitHub.

# Tải tệp thực thi (ví dụ cho Linux x64) 
wget https://github.com/erebe/wstunnel/releases/download/v9.7.1/wstunnel-x64-linux.tar.gz
tar -xvf wstunnel-x64-linux.tar.gz
sudo mv wstunnel /usr/local/bin/

Xác minh cài đặt bằng cách chạy wstunnel --version. Nó sẽ trả về số phiên bản mà không có lỗi.

Bước 2: Cấu hình WireGuard Server

Cấu hình máy chủ của bạn là tiêu chuẩn, nhưng nó nên lắng nghe cục bộ. Giả sử máy chủ của bạn sử dụng IP nội bộ 10.0.0.1.

Chỉnh sửa /etc/wireguard/wg0.conf:

[Interface]
PrivateKey = <SERVER_PRIVATE_KEY>
Address = 10.0.0.1/24
ListenPort = 51820

[Peer]
PublicKey = <CLIENT_PUBLIC_KEY>
AllowedIPs = 10.0.0.2/32

Kích hoạt interface:

sudo wg-quick up wg0

Bước 3: Khởi chạy WSTunnel Server

Bây giờ, hãy khởi động WSTunnel server. Nó sẽ lắng nghe trên cổng TCP 443 và chuyển tiếp lưu lượng WebSocket đến cổng WireGuard của bạn.

wstunnel server --listen http://0.0.0.0:443 --restrict-to 127.0.0.1:51820

Lưu ý: Nếu bạn đã chạy một web server như Nginx trên cổng 443, bạn sẽ cần chạy WSTunnel trên một cổng khác (như 8443) hoặc sử dụng một reverse proxy để định tuyến lưu lượng dựa trên một đường dẫn URL cụ thể.

Bước 4: Thiết lập Client Bridge

Trên laptop hoặc máy cục bộ, bạn cần tạo đầu kia của đường hầm. Lệnh này yêu cầu WSTunnel lắng nghe trên một cổng UDP cục bộ và gửi mọi thứ đến máy chủ của bạn qua một WebSocket bảo mật.

wstunnel client --listen udp://127.0.0.1:51820 --to wss://your-server-ip:443

Bây giờ, bất kỳ dữ liệu nào được gửi đến 127.0.0.1:51820 trên máy cục bộ của bạn sẽ tự động được bọc trong TLS và gửi đến máy chủ.

Bước 5: Cấu hình WireGuard Client

Bước cuối cùng là trỏ WireGuard client vào bridge cục bộ. Đây là nơi phép màu xảy ra.

Chỉnh sửa tệp client.conf cục bộ của bạn:

[Interface]
PrivateKey = <CLIENT_PRIVATE_KEY>
Address = 10.0.0.2/24
# Quan trọng: Hạ MTU xuống 1300 để tránh phân mảnh
MTU = 1300

[Peer]
PublicKey = <SERVER_PUBLIC_KEY>
# Trỏ đến bridge WSTunnel cục bộ của bạn
Endpoint = 127.0.0.1:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25

Tại sao phải thay đổi MTU? Ethernet tiêu chuẩn sử dụng MTU là 1500 byte. WireGuard thường sử dụng 1420. Tuy nhiên, vì chúng ta đang thêm các header WebSocket, TCP và TLS, kích thước gói tin sẽ tăng lên. Nếu bạn để ở mức 1420, các gói tin sẽ quá lớn và bị loại bỏ hoặc phân mảnh. 1300 là một giá trị lý tưởng giúp đảm bảo kết nối của bạn ổn định.

Automating with Systemd

Để đảm bảo đường hầm tự động khởi chạy trên máy chủ, hãy tạo một tệp service tại /etc/systemd/system/wstunnel.service:

[Unit]
Description=Máy chủ WSTunnel
After=network.target

[Service]
ExecStart=/usr/local/bin/wstunnel server --listen http://0.0.0.0:443 --restrict-to 127.0.0.1:51820
Restart=always
User=root

[Install]
WantedBy=multi-user.target

Kích hoạt nó bằng sudo systemctl enable --now wstunnel.

Hiệu suất và những cạm bẫy tiềm tàng

Mặc dù phương pháp này rất tuyệt vời để vượt qua các rào cản, nó gây ra vấn đề “TCP-over-TCP”. WireGuard được thiết kế cho UDP, vốn xử lý việc mất gói tin một cách nhẹ nhàng. Khi bạn bọc nó trong TCP (thông qua WebSockets), cả hai lớp sẽ cố gắng truyền lại các gói tin bị mất cùng một lúc. Trên một kết nối có độ trễ cao, điều này có thể gây ra hiện tượng “TCP Meltdown” khiến tốc độ giảm đáng kể.

Để mọi thứ hoạt động trơn tru, hãy luôn sử dụng wss:// (mã hóa) thay vì ws://. WebSockets thông thường rất dễ bị các tường lửa hiện đại nhận diện và bóp băng thông. Mã hóa TLS làm cho lưu lượng của bạn không thể phân biệt được với một phiên HTTPS tiêu chuẩn.

Lời kết

Bằng cách kết hợp WireGuard với WSTunnel, bạn có được sự bảo mật của một VPN hiện đại cùng khả năng tiếp cận của lưu lượng web tiêu chuẩn. Đây là một thiết lập thiết yếu cho bất kỳ ai làm việc trong môi trường bị khóa chặt. Chỉ cần nhớ theo dõi cài đặt MTU và giữ cho PersistentKeepalive luôn hoạt động để ngăn tường lửa ngắt kết nối của bạn do quá hạn.

Share: