Vấn đề với VPN truyền thống và tại sao WireGuard ra đời
Hầu hết các sysadmin khi cần VPN đều nghĩ ngay đến OpenVPN — cài được, chạy được, nhưng cấu hình cực kỳ rườm rà. Chứng chỉ CA, file .ovpn dài hàng trăm dòng, daemon nặng nề. Và mỗi khi client đổi mạng — từ WiFi sang 4G chẳng hạn — kết nối đứt ngay, phải reconnect lại từ đầu.
Mình gặp đúng bài toán đó khi công ty cần cho team remote truy cập vào hệ thống nội bộ. Trên con server chạy CentOS 7 cũ, mình đã mày mò đủ kiểu để đạt performance chấp nhận được — và sau nhiều lần thử nghiệm, WireGuard là thứ duy nhất thực sự giải quyết được vấn đề mà không kéo theo đống headache mới.
Từ Linux 5.6, WireGuard được tích hợp thẳng vào kernel — không cần module bên thứ ba. Toàn bộ codebase chỉ khoảng 4.000 dòng, so với hàng trăm nghìn dòng của stack OpenVPN + OpenSSL. Ít code hơn đồng nghĩa với ít điểm tấn công hơn, dễ audit bảo mật hơn, và overhead xử lý packet thấp hơn rõ rệt.
Khái niệm cốt lõi trước khi cài đặt
Khác với OpenVPN phân biệt cứng server/client, WireGuard dùng mô hình peer-to-peer — mỗi máy trong mạng là một peer bình đẳng, có cặp key riêng (private key + public key).
- Interface: WireGuard tạo một network interface ảo (thường đặt tên
wg0), tương tự như interfaceeth0haytun0. - Endpoint: Địa chỉ IP:port thực tế để các peer kết nối đến nhau (thường chỉ server mới cần khai báo).
- AllowedIPs: Dải IP nào được phép đi qua tunnel. Đây là điểm khác biệt so với OpenVPN — WireGuard dùng chính trường này để định tuyến, không cần route table phức tạp.
- PersistentKeepalive: Gửi packet giữ kết nối mỗi N giây — quan trọng khi client đứng sau NAT.
Điểm mấu chốt: WireGuard giữ trạng thái kết nối qua thay đổi mạng nhờ cơ chế roaming — client đổi IP không làm tunnel đứt, miễn là hai peer còn trao đổi được packet.
Thực hành: Cài đặt WireGuard Server
Bước 1: Cài đặt WireGuard
Trên Ubuntu 20.04/22.04:
sudo apt update
sudo apt install -y wireguard wireguard-tools
Trên CentOS 7/8 hoặc RHEL (cần thêm repo EPEL):
# CentOS 7
yum install -y epel-release
yum install -y kmod-wireguard wireguard-tools
# CentOS 8 / Stream
dnf install -y epel-release elrepo-release
dnf install -y kmod-wireguard wireguard-tools
Kiểm tra kernel module đã load chưa:
modprobe wireguard
lsmod | grep wireguard
Bước 2: Tạo key pair cho server
WireGuard dùng Curve25519 cho key exchange — cùng thuật toán mà Signal Protocol sử dụng. Tạo key chỉ cần một lệnh:
cd /etc/wireguard
umask 077 # Quan trọng: đảm bảo private key không bị đọc bởi user khác
wg genkey | tee server_private.key | wg pubkey > server_public.key
cat server_private.key # Lưu lại giá trị này
cat server_public.key # Dùng cho client config
Lệnh umask 077 đảm bảo file key tạo ra có permission 600. Bỏ qua bước này, private key có thể bị đọc bởi bất kỳ user nào trên cùng server — và toàn bộ tunnel coi như bị compromise.
Bước 3: Tạo cấu hình server (/etc/wireguard/wg0.conf)
Tạo file cấu hình với nội dung sau:
cat > /etc/wireguard/wg0.conf << 'EOF'
[Interface]
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = <SERVER_PRIVATE_KEY>
# Bật IP forwarding và NAT để client ra được internet qua VPN
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
# Peer: Client 1
[Peer]
PublicKey = <CLIENT1_PUBLIC_KEY>
AllowedIPs = 10.0.0.2/32
EOF
Lưu ý: Thay eth0 bằng interface thực của server (kiểm tra bằng ip route | grep default). Thay các placeholder <...> bằng key thực tế.
Bật IP forwarding vĩnh viễn:
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
sysctl -p
Bước 4: Khởi động WireGuard và bật autostart
systemctl enable --now wg-quick@wg0
systemctl status wg-quick@wg0
# Xem trạng thái tunnel
wg show
Output của wg show sẽ hiển thị interface wg0, listening port, và danh sách peer đã kết nối.
Cấu hình phía Client
Tạo key pair cho client
Thực hiện trên máy client (Linux, macOS, Windows đều dùng cách tương tự):
wg genkey | tee client_private.key | wg pubkey > client_public.key
File cấu hình client (wg0.conf hoặc tunnel.conf)
[Interface]
Address = 10.0.0.2/24
PrivateKey = <CLIENT_PRIVATE_KEY>
DNS = 1.1.1.1 # Tùy chọn: dùng DNS qua tunnel
[Peer]
PublicKey = <SERVER_PUBLIC_KEY>
Endpoint = <SERVER_PUBLIC_IP>:51820
AllowedIPs = 0.0.0.0/0 # Route toàn bộ traffic qua VPN
# Hoặc chỉ route subnet nội bộ:
# AllowedIPs = 10.0.0.0/24, 192.168.1.0/24
PersistentKeepalive = 25
Khởi động tunnel trên client:
sudo wg-quick up ./tunnel.conf
# Kiểm tra kết nối
ping 10.0.0.1 # Ping đến server
curl ifconfig.me # Kiểm tra IP public (nên thấy IP của server VPN)
Thêm peer mới vào server mà không cần restart
Đây là một điểm mình rất thích ở WireGuard — thêm client mới không cần reload service:
# Thêm peer động (không ngắt kết nối hiện tại)
wg set wg0 peer <CLIENT2_PUBLIC_KEY> allowed-ips 10.0.0.3/32
# Lưu cấu hình hiện tại vào file
wg-quick save wg0
Mở firewall cho cổng WireGuard
Đừng quên mở port UDP 51820 trên firewall server:
# UFW (Ubuntu)
ufw allow 51820/udp
# firewalld (CentOS/RHEL)
firewall-cmd --permanent --add-port=51820/udp
firewall-cmd --reload
# iptables trực tiếp
iptables -A INPUT -p udp --dport 51820 -j ACCEPT
Nếu server đứng sau NAT (VPS cloud), nhớ mở port trong security group/network ACL của nhà cung cấp cloud.
Xử lý sự cố thường gặp
Một số vấn đề mình đã gặp và cách giải quyết:
- Client kết nối được nhưng không ra internet: Kiểm tra
ip_forwardđã bật chưa (sysctl net.ipv4.ip_forward), và rule NAT MASQUERADE đúng interface. - Tunnel up nhưng ping không thông: Chạy
wg show— nếu thấy dònglatest handshakethì đã bắt tay thành công. Không có dòng đó nghĩa là public key hoặc endpoint đang sai. - CentOS 7 không load được module: Kernel mặc định CentOS 7 (3.10.x) không hỗ trợ WireGuard native — phải dùng DKMS hoặc nâng kernel lên mainline qua ELRepo (
yum install -y kernel-mltừ repo ELRepo). - Kết nối bị drop sau vài phút: Thêm
PersistentKeepalive = 25vào peer config phía client.
Kết luận
Sau vài tuần dùng thực tế, cả team hết phàn nàn về VPN bị đứt. Đổi mạng từ văn phòng về nhà, từ WiFi sang 4G — tunnel vẫn giữ nguyên. Thêm thành viên mới vào cũng xong trong vài phút, không cần đụng đến service đang chạy.
WireGuard không phải là phiên bản cải tiến của OpenVPN — đây là cách tiếp cận khác hẳn. 4.000 dòng chạy trong kernel thay vì hàng trăm nghìn dòng chạy ở userspace. Khi cần mở rộng: đổi AllowedIPs thành dải subnet cụ thể để có split tunneling, hoặc khai báo thêm DNS trong [Interface] phía client để truy cập service nội bộ bằng domain thay vì gõ IP.

