1. Cài đặt Wireguard.
WireGuard là một công nghệ mạng ảo (VPN) mới, được thiết kế để cung cấp kết nối mạng an toàn, nhanh chóng và đơn giản hơn so với các công nghệ VPN truyền thống như OpenVPN hay IPsec.
WireGuard được xây dựng với mục tiêu đơn giản hóa các phương thức mã hóa và xác thực, giảm thiểu kích thước mã nguồn và tăng tốc độ kết nối. Với chỉ khoảng 4.000 dòng mã nguồn, WireGuard có kích thước nhỏ hơn rất nhiều so với các giải pháp VPN khác, giúp cho việc triển khai và bảo trì trở nên dễ dàng hơn.
WireGuard sử dụng một giao thức mới gọi là “Noise Protocol Framework” để cung cấp mã hóa và xác thực, và sử dụng “Curve25519” để thực hiện việc trao đổi khóa. Các tính năng bảo mật này được cho là rất tốt, giúp WireGuard trở thành một lựa chọn hấp dẫn cho các công ty và tổ chức có nhu cầu tăng cường bảo mật cho kết nối mạng của mình.
Nay mình sẽ hướng dẫn các bạn triển khai Wireguard bằng Docker Compose, bạn có thể theo dõi sơ đồ sau để dễ hình dung.
Đầu tiên mình tạo thư mục chứa config và file docker-compose.yaml.
mkdir -p /opt/wireguard-server/
Tiếp theo mình tạo file docker-compose.yaml với nội dung như dưới.
cat > /opt/wireguard-server/docker-compose.yaml << 'OEF'
version: "2.1"
services:
wireguard:
image: linuxserver/wireguard
container_name: wireguard
restart: always
cap_add:
- NET_ADMIN
- SYS_MODULE
environment:
- PUID=1000
- PGID=1000
- TZ=Asia/Ho_Chi_Minh
- SERVERURL=115.79.213.185
- SERVERPORT=51820
- PEERS=1
- PEERDNS=auto
- INTERNAL_SUBNET=10.10.100.0
volumes:
- /opt/wireguard-server/config:/config
- /lib/modules:/lib/modules
ports:
- 51820:51820/udp
sysctls:
- net.ipv4.conf.all.src_valid_mark=1
OEF
Đoạn yaml trên là một file docker-compose, định nghĩa một container Docker có tên “wireguard” sử dụng image “linuxserver/wireguard”. Container này cung cấp dịch vụ máy chủ VPN WireGuard.
Các thuộc tính được định nghĩa trong phần “environment” của file docker-compose giúp cấu hình container của WireGuard. Các thuộc tính bao gồm:
- PUID: User ID (UID) của người dùng được sử dụng để chạy container.
Để biết giá trị PUID và PGID của người dùng của bạn, bạn có thể chạy lệnh sau trên terminal của máy tính của bạn:
id -u username
Trong đó, “username” là tên người dùng của bạn. Lệnh này sẽ trả về giá trị PUID của bạn.
- PGID: Group ID (GID) của nhóm được sử dụng để chạy container. Bạn có thể lấy giá trị PGID bằng cách chạy lệnh sau:
id -g username
Lệnh này sẽ trả về giá trị PGID của bạn.
Nếu bạn không muốn sử dụng giá trị PUID và PGID của người dùng của bạn, bạn có thể thay đổi giá trị này thành bất kỳ giá trị nào khác phù hợp với nhu cầu của bạn.
- TZ: Múi giờ được sử dụng cho container.
- SERVERURL: Tùy chọn, địa chỉ IP hoặc tên miền của máy chủ chạy WireGuard.
- SERVERPORT: Tùy chọn, cổng được sử dụng cho máy chủ WireGuard.
- PEERS: Tùy chọn, số lượng peer được phép kết nối đến máy chủ.
- PEERDNS: Tùy chọn, cấu hình phân giải tên miền cho các peer. Các giá trị có thể là “auto” hoặc “no”.
- INTERNAL_SUBNET: Tùy chọn, mạng con nội bộ được sử dụng bởi các peer. GIá trị này không được trùng với bất kỳ subnet nào đã tồn tại trong mạng local của bạn. Nếu bạn không cung cấp giá trị này cho container thì sẽ sử dụng mặc định là 10.10.0.0/24.
- “/opt/wireguard-server/config” được sử dụng để lưu trữ cấu hình cho container.
- “/lib/modules” được sử dụng để chia sẻ module kernel với container.
Container được chạy trên cổng UDP 51820 và sử dụng sysctl để đặt giá trị của net.ipv4.conf.all.src_valid_mark thành 1. Nếu container bị dừng, nó sẽ được tự động khởi động lại trừ khi được chỉ định để dừng.
Chúng ta tạo và khởi động container bằng command sau:
cd /opt/wireguard-server
docker-compose up -d
Đây là các thư mục và file khi chúng ta khởi tạo thành công container.
/opt/wireguard-server
├── config
│ ├── coredns
│ │ └── Corefile
│ ├── peer1
│ │ ├── peer1.conf
│ │ ├── peer1.png
│ │ ├── presharedkey-peer1
│ │ ├── privatekey-peer1
│ │ └── publickey-peer1
│ ├── server
│ │ ├── privatekey-server
│ │ └── publickey-server
│ ├── templates
│ │ ├── peer.conf
│ │ └── server.conf
│ └── wg0.conf
└── docker-compose.yaml
Như bạn thấy ở cây thư mục trên, khi bạn chạy thành công container thì tất cả file config được lưu trữ tại /opt/wireguard-server/config
ở tại máy host. Chúng sẽ sử dụng nội dung trong file peer1/peer1.conf
để kết nối đến VPN Server.
Nội dung của file peer1.conf
sẽ dạng như sau:
[Interface]
Address = 10.10.100.2
PrivateKey = aCNWHpFRnNewK7aUjJy1gJhQvKMaAoizcGa6q3rZD18=
ListenPort = 51820
DNS = 10.10.100.1
[Peer]
PublicKey = B555r9P1dBAiGpu6BaIa5qQFn6DGtJUkC/IdiDOG3yE=
PresharedKey = CY5Bk3GPU1jkP+CcNKodLPMJWsqhiN2qUqlk2NVkP10=
Endpoint = 192.168.13.219:51820
AllowedIPs = 0.0.0.0/0, ::/0
Nếu muốn kết nối với QR code chúng ta dùng command sau để in command lên terminal.
docker exec -it wireguard /app/show-peer <peer-number>
Ví dụ:
docker exec -it wireguard /app/show-peer 1
Kết quả.
Để thêm nhiều config cho client thì trong file docker-compose ta điều chỉnh lại thông số PEERS=2 và restart lại container.
docker-compose up -d --force-recreate
Kết quả khi bạn tạo thêm peer thứ 2, bạn sẽ có một thư mục mới tên là peer2 được sinh ra trong thư mục config.
/opt/wireguard-server
├── config
│ ├── coredns
│ │ └── Corefile
│ ├── peer1
│ │ ├── peer1.conf
│ │ ├── peer1.png
│ │ ├── presharedkey-peer1
│ │ ├── privatekey-peer1
│ │ └── publickey-peer1
│ ├── peer2
│ │ ├── peer2.conf
│ │ ├── peer2.png
│ │ ├── presharedkey-peer2
│ │ ├── privatekey-peer2
│ │ └── publickey-peer2
│ ├── server
│ │ ├── privatekey-server
│ │ └── publickey-server
│ ├── templates
│ │ ├── peer.conf
│ │ └── server.conf
│ └── wg0.conf
└── docker-compose.yaml
Kết quả khi dùng lệnh wg để show các peer connection, kết quả như dưới cho thấy mình đang có 2 peer.
$ docker exec -it wireguard wg
interface: wg0
public key: B555r9P1dBAiGpu6BaIa5qQFn6DGtJUkC/IdiDOG3yE=
private key: (hidden)
listening port: 51820
peer: QKjuUZVUC0wSxSUsOaE80WvwM6V74QgicqpsHXTv4kI=
preshared key: (hidden)
endpoint: 192.168.12.20:51820
allowed ips: 10.10.100.3/32
latest handshake: 1 minute, 21 seconds ago
transfer: 13.30 MiB received, 20.75 MiB sent
peer: sJUIbKcnCu+KD3HSyza/o5Lo6c12yRD6jSDJhoMn3C8=
preshared key: (hidden)
endpoint: 192.168.12.20:51820
allowed ips: 10.10.100.2/32
latest handshake: 35 minutes, 29 seconds ago
transfer: 2.59 MiB received, 20.03 MiB sent
Vậy là xong! Bạn đã có 1 máy chủi VPN cho riêng mình bằng Wireguard rồi.
2. Thiết lập danh sách địa chỉ được phép truy cập của từng peer.
2.1. Cài đặt và làm quen Iptables.
Sau khi kết nối thành công VPN thì mặc định client sẽ được kết nối đến tất cả các mạng có trong LAN và Internet vì mình đang để AllowedIPs = 0.0.0.0/0. Để chặn tất cả các địa chỉ IP ngoại trừ danh sách địa chỉ trên cho từng peer, bạn có thể sử dụng tường lửa trên máy chủ của mình để chỉ cho phép các kết nối đến các địa chỉ IP đó. Và mình sẽ sử dụng Iptables để kiểm soát 1 cách triệt để hơn.
Để kiểm tra xem iptables đã được cài đặt trên hệ thống của bạn hay chưa, bạn có thể mở Terminal và nhập lệnh sau:
sudo iptables -L
Nếu iptables đã được cài đặt, bạn sẽ thấy một danh sách các quy tắc trong bảng mặc định của nó. Nếu thông báo lỗi xuất hiện, có thể bạn cần cài đặt iptables trước bằng lệnh:
sudo apt-get update
sudo apt-get install iptables -y
Nếu bạn sử dụng một bản phân phối khác, ví dụ như CentOS hoặc Fedora, bạn có thể sử dụng lệnh tương tự để kiểm tra và cài đặt iptables.
Sau khi cài đặt xong bạn có thể sử dụng lệnh iptables -L
để hiển thị danh sách các rule trong iptables. Để hiển thị chi tiết hơn về các rule trong iptables, bạn có thể sử dụng các tùy chọn sau:
-v
: hiển thị chi tiết về số gói tin và kích thước của chúng.-n
: hiển thị địa chỉ IP và cổng dưới dạng số thay vì tên miền và tên dịch vụ.-x
: hiển thị chi tiết về số byte dữ liệu được truyền qua các rule.
Ví dụ, để hiển thị danh sách các rule với thông tin chi tiết về số gói tin và kích thước của chúng, bạn có thể sử dụng lệnh sau:
iptables -L -v
Để hiển thị số thứ tự của các rule, bạn có thể chạy lệnh iptables -L --line-numbers
(bạn . Ví dụ:
sudo iptables -L --line-numbers
Kết quả sẽ hiển thị tên các chain và số thứ tự của các rule trong chain đó. Sau đó, bạn có thể sử dụng số thứ tự này để xoá rule bằng lệnh iptables -D
. Ví dụ:
sudo iptables -D INPUT 3
Lệnh trên sẽ xoá rule có số thứ tự là 3 trong chain INPUT. Hoặc để xoá trắng các rule sử dụng lệnh sau:
sudo iptables -F
Lưu ý nếu bạn xoá trắng rule bạn sẽ mất hết kết nối và trong đó có ssh, hãy đảm bảo bạn đang ở gần máy chủ vậy lý hoặc có thể console vào kiểm soát được server.
Bạn có thể sử dụng lệnh sau để dừng và vô hiệu hóa iptables:
sudo systemctl stop iptables
sudo systemctl disable iptables
Lệnh systemctl stop
sẽ dừng chương trình iptables ngay lập tức và lệnh systemctl disable
sẽ vô hiệu hóa iptables để nó không chạy khi khởi động hệ thống.
2.2. Thiết lập phân quyền truy cập các peer.
Bước 1: Nếu bạn chạy container thì bạn có thể bỏ qua bước này, nếu bạn chạy Wireguard bằng gói thì bạn nên thêm các rule sau vào iptables để mở port ssh và udp 51820 cho tất cả các địa chỉ IP để tránh mất kết nối SSH và port Wireguard khi config Iptables:
iptables -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
iptables -A INPUT -p udp -m udp --dport 51820 -j ACCEPT
Lưu ý rằng rule cho phép kết nối đến SSH và UDP 51820 của tất cả các IP truy cập nên được đặt trước các rule liên quan đến từng peer để đảm bảo các kết nối này được thiết lập thành công.
Ví dụ, nếu bạn đang sử dụng Iptables trên Ubuntu, bạn có thể sử dụng các quy tắc như sau:
iptables -A FORWARD -s 10.10.100.2 -d 172.16.1.1 -j ACCEPT
iptables -A FORWARD -s 10.10.100.3 -d 172.16.1.254 -j ACCEPT
iptables -A FORWARD -d 172.16.0.0/16 -j DROP
Đây là các rule iptables để quy định luồng chuyển tiếp (forward) các gói tin trong mạng. Cụ thể:
- Rule đầu tiên cho phép chuyển tiếp các gói tin từ địa chỉ source 10.10.100.2 đến địa chỉ đích 172.16.1.1.
- Rule thứ hai cho phép chuyển tiếp các gói tin từ địa chỉ source 10.10.100.3 đến địa chỉ đích 172.16.1.254.
- Rule cuối cùng sẽ loại bỏ (DROP) các gói tin có địa chỉ đích thuộc mạng 172.16.0.0/16, tức là các gói tin không được phép truy cập vào mạng này.
Tổng quan, rule trên không bao gồm chặn 0.0.0.0/0 nên mặc định nó sẽ kết nối đến tất cả ngoại trừ 172.16.0.0/16 và 2 IP trong LAN 172.16.1.1, 172.16.1.254.
Lưu ý rằng để sử dụng tường lửa để giới hạn truy cập của các peer trên container, bạn cần cài đặt và cấu hình Iptables trên container Wireguard.
$ iptables -L --line-numbers -n
Chain INPUT (policy ACCEPT)
num target prot opt source destination
Chain FORWARD (policy ACCEPT)
num target prot opt source destination
1 ACCEPT all -- 10.10.100.2 172.16.1.1
2 ACCEPT all -- 10.10.100.3 172.16.1.254
3 DROP all -- 0.0.0.0/0 172.16.0.0/16
Chain OUTPUT (policy ACCEPT)
num target prot opt source destination
3. Cấu hình VPN cho client.
Phần này mình sẽ sử dụng máy tính sử dụng hệ điều hành MacOS để demo. Bạn hãy vào Appstore tìm kiếm Wireguard và tải nó về máy nhé.
Sau khi tải xong hãy mở nó lên và bạn để ở trên thanh Bar đã xuất hiện biểu tượng của Wireguard, hãy bấm vào nó (1) để mở bảng Manage WireGuard Tunnels và sau đó bấm vào dấu + (2) để tiến hành cấu hình peer. Bấm chọn Add Empty Tunnel… (3).
Bạn hãy copy nội dung trong file config của peer1.
root@wireguard-13:~# cat /opt/wireguard-server/config/peer1/peer1.conf
[Interface]
Address = 10.10.100.2
PrivateKey = aCNWHpFRnNewK7aUjJy1gJhQvKMaAoizcGa6q3rZD18=
ListenPort = 51820
DNS = 10.10.100.1
[Peer]
PublicKey = B555r9P1dBAiGpu6BaIa5qQFn6DGtJUkC/IdiDOG3yE=
PresharedKey = CY5Bk3GPU1jkP+CcNKodLPMJWsqhiN2qUqlk2NVkP10=
Endpoint = 192.168.13.219:51820
AllowedIPs = 0.0.0.0/0, ::/0
Trong phần config của Client bạn hãy xoá những gì đang có trong khung config như dưới.
Hãy dán đoạn config vừa copy của peer.conf vào đây và đặt 1 tên bất kỳ cho nó sau đó lưu lại.
Nhớ bấm Allow để chấp nhận thêm network này.
Bạn sẽ có 1 peer đầu tiên.
Do mình chỉ có 1 máy tính nên mình sẽ demo 2 peer trên 1 máy tính. Mình sẽ thêm peer2 giống với các thao tác như trên.
Bạn sẽ có kết quả khi kết nối vào peer1 thì bạn không thể ping được 172.16.1.254 trong khi đó bạn vẫn có thể ping tới 172.16.1.1 và internet.
Tương tự mình sẽ ngắt peer1 và chuyển sang kích hoạt peer2, bạn sẽ thấy kết quả.
Kết quả cho thấy khi kết nối vào peer2 thì bạn không thể ping được 172.16.1.1 trong khi đó bạn vẫn có thể ping tới 172.16.1.254 và internet.
Cách sử dụng terminal để kết nối đối với MacOS, với cách này có thể áp dụng cho các MacOS version cũ. Đầu tiên hãy tải Wireguard về máy.
brew install wireguard-tools
Bạn có thể verify lại kết quả cài đặt.
$ sudo wg --version
wireguard-tools v1.0.20210914 - https://git.zx2c4.com/wireguard-tools/
Bạn hãy tạo thư mục chứa file peer.conf.
mkdir /usr/local/etc/wireguard/
Tiếp theo hãy tạo file peer.conf, dán nội dung file peer đã copy vào và lưu lại.
cat > /usr/local/etc/wireguard/wg0.conf << 'OEF'
[Interface]
Address = 10.10.100.2
PrivateKey = aCNWHpFRnNewK7aUjJy1gJhQvKMaAoizcGa6q3rZD18=
ListenPort = 51820
DNS = 10.10.100.1
[Peer]
PublicKey = B555r9P1dBAiGpu6BaIa5qQFn6DGtJUkC/IdiDOG3yE=
PresharedKey = CY5Bk3GPU1jkP+CcNKodLPMJWsqhiN2qUqlk2NVkP10=
Endpoint = 192.168.13.219:51820
AllowedIPs = 0.0.0.0/0, ::/0
OEF
Tiếp theo hãy start WireGuard VPN bằng lệnh sudo wg-quick up wg0
.
$ sudo wg-quick up wg0
Warning: `/usr/local/etc/wireguard/wg0.conf' is world accessible
[#] wireguard-go utun
[+] Interface for wg0 is utun2
[#] wg setconf utun2 /dev/fd/63
[#] ifconfig utun2 inet 10.10.100.2 10.10.100.2 alias
[#] ifconfig utun2 up
[#] route -q -n add -inet6 ::/1 -interface utun2
route: writing to routing socket: Network is unreachable
[#] route -q -n add -inet6 8000::/1 -interface utun2
route: writing to routing socket: Network is unreachable
[#] route -q -n add -inet 0.0.0.0/1 -interface utun2
[#] route -q -n add -inet 128.0.0.0/1 -interface utun2
[#] route -q -n add -inet 192.168.13.219 -gateway 172.16.3.254
[#] networksetup -getdnsservers Built-in Serial Port (1)
[#] networksetup -getsearchdomains Built-in Serial Port (1)
[#] networksetup -getdnsservers Ethernet
[#] networksetup -getsearchdomains Ethernet
[#] networksetup -getdnsservers Ethernet 2
[#] networksetup -getsearchdomains Ethernet 2
[#] networksetup -getdnsservers Bluetooth PAN
[#] networksetup -getsearchdomains Bluetooth PAN
[#] networksetup -getdnsservers NTJ
[#] networksetup -getsearchdomains NTJ
[#] networksetup -setdnsservers Ethernet 2 10.10.100.1
[#] networksetup -setsearchdomains Ethernet 2 Empty
[#] networksetup -setdnsservers Bluetooth PAN 10.10.100.1
[#] networksetup -setsearchdomains Bluetooth PAN Empty
[#] networksetup -setdnsservers NTJ 10.10.100.1
[#] networksetup -setsearchdomains NTJ Empty
[#] networksetup -setdnsservers Built-in Serial Port (1) 10.10.100.1
[#] networksetup -setsearchdomains Built-in Serial Port (1) Empty
[#] networksetup -setdnsservers Ethernet 10.10.100.1
[#] networksetup -setsearchdomains Ethernet Empty
[+] Backgrounding route monitor
Hãy dùng lệnh sudo wg
để verify lại kết quả đã kết nối VPN thành công chưa.
$ sudo wg
interface: utun2
public key: sJUIbKcnCu+KD3HSyza/o5Lo6c12yRD6jSDJhoMn3C8=
private key: (hidden)
listening port: 51820
peer: B555r9P1dBAiGpu6BaIa5qQFn6DGtJUkC/IdiDOG3yE=
preshared key: (hidden)
endpoint: 192.168.13.219:51820
allowed ips: 0.0.0.0/0, ::/0
transfer: 6.9 KiB received, 1.45 KiB sent
Tham khảo thêm https://trailofbits.github.io/algo/client-macos-wireguard.html.
5. Trường hợp WireGuard nằm sau thiết bị Nat.
Nếu WireGuard của bạn nằm sau 1 Router và NAT Port ra ngoài thì chạy có thể tham khảo sơ đồ và manifest sau:
– Sơ đồ.
– Manifest.
cat > /opt/wireguard-server/docker-compose.yaml << 'OEF'
version: "2.1"
services:
wireguard:
image: linuxserver/wireguard
container_name: wireguard
restart: always
cap_add:
- NET_ADMIN
- SYS_MODULE
environment:
- PUID=1000
- PGID=1000
- TZ=Asia/Ho_Chi_Minh
- SERVERURL=115.79.213.185
- SERVERPORT=51820
- PEERS=1
- PEERDNS=auto
- INTERNAL_SUBNET=10.10.100.0
volumes:
- /opt/wireguard-server/config:/config
- /lib/modules:/lib/modules
ports:
- 51821:51820/udp
sysctls:
- net.ipv4.conf.all.src_valid_mark=1
OEF
Kết quả bạn nhận file config dành cho client đầu cuối như sau:
[Interface]
Address = 10.10.100.2
PrivateKey = <PrivateKey>
ListenPort = 51820
DNS = 10.10.100.1
[Peer]
PublicKey = <PublicKey>
PresharedKey = <PresharedKey=
Endpoint = 115.79.213.185:51820
AllowedIPs = 0.0.0.0/0, ::/0
Do Router đã NAT Port 51821 ra ngoài internet nên bạn hãy sửa lại phần Endpoint = 115.79.213.185:51820 thành Endpoint = 115.79.213.185:51821 nhé.
Vậy manifest sau khi chỉnh sửa sẽ có dạng.
[Interface]
Address = 10.10.100.2
PrivateKey = <PrivateKey>
ListenPort = 51820
DNS = 10.10.100.1
[Peer]
PublicKey = <PublicKey>
PresharedKey = <PresharedKey=
Endpoint = 115.79.213.185:51821
AllowedIPs = 0.0.0.0/0, ::/0
6. So sánh WireGuard và OpenVPN.
WireGuard và OpenVPN đều là hai công nghệ VPN phổ biến và được sử dụng rộng rãi. Dưới đây là một số điểm khác nhau giữa hai công nghệ này:
- Hiệu suất: WireGuard được thiết kế để có hiệu suất tốt hơn so với OpenVPN. Với một số trường hợp sử dụng, WireGuard có thể nhanh hơn và tiết kiệm tài nguyên hơn so với OpenVPN.
- Kích thước mã nguồn: WireGuard có mã nguồn ngắn gọn hơn so với OpenVPN, giúp dễ dàng hơn trong việc phát triển và bảo trì mã nguồn.
- Bảo mật: Cả WireGuard và OpenVPN đều cung cấp cơ chế bảo mật đáng tin cậy. Tuy nhiên, WireGuard sử dụng một số phương pháp tiên tiến hơn để đảm bảo tính bảo mật.
- Hỗ trợ nền tảng: OpenVPN có thể hoạt động trên nhiều nền tảng hơn so với WireGuard. Tuy nhiên, WireGuard đang dần được hỗ trợ trên nhiều nền tảng hơn.
- Các tính năng: OpenVPN có một số tính năng phổ biến hơn so với WireGuard, bao gồm tính năng kết nối giữa các máy chủ và tính năng định tuyến riêng.
Như vậy cả WireGuard và OpenVPN đều là các công nghệ VPN đáng tin cậy và có ưu điểm riêng. Tuy nhiên, nếu bạn cần một giải pháp VPN đơn giản, nhanh chóng và hiệu suất tốt hơn, WireGuard có thể là một sự lựa chọn tốt.