Chuyên mục
Docker Network

Thiết lập WireGuard client bằng Docker trên máy chủ Linux

Các bài viết về cách thiết lập WireGuard VPN bằng Docker trước đây của mình như wg-easy, wirehole-ui đều dùng cho mục đích tạo WireGuard VPN Server. Rồi sau đó sẽ dùng WireGuard client trên Windows, Linux hay iOS để kết nối vào WireGuard VPN Server.

Bài viết này mình sẽ hướng dẫn cách thiết lập WireGuard client trên máy chủ Linux sử dụng Docker và chuyển hướng kết nối mạng của máy chủ đi qua WireGuard VPN.

I. Yêu cầu chuẩn bị

  • Một máy chủ đã được cài đặt sẵn Docker và Docker Compose.
  • Một máy chủ đã được thiết lập sẵn WireGuard VPN Server: có thể sử dụng wg-easy hoặc Wirehole-UI để thiết lập.
  • File cấu hình WireGuard client được tạo ra bởi WireGuard Server, có dạng như dưới đây
[Interface]
PrivateKey = sO7U9OS0s5vxxxdfY1aNHzVYXpJhKAaU/HG9MyfaWU=
Address = 10.6.0.8/24
DNS = 10.2.0.100

[Peer]
PublicKey = 9NknLwkQRaxxxsfNr9wKIC15KsqRvN5eOwUZxuhDFWI=
PresharedKey = xhqj9bnxxxuuFtzbVJ5GJPD6D4YAMHdk6RbL5JVkT+M=
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 0
Endpoint = 222.222.96.107:51822

II. Chuyển hướng mạng của máy chủ đi qua WireGuard VPN

Thứ tự công việc cần làm:

  1. Thiết lập WireGuard client sử dụng image WireGuard của Linuxserver
  2. Chỉnh sửa Routing Table để điều hướng mạng đi qua IP của WireGuard client container.

1. Tạo Docker network

Để có thể điều hướng mạng bằng routing table, container Wireguard client cần sử dụng IP tĩnh để tránh rắc rối về sau. Mình sẽ tạo 1 mạng docker bridge mới với tên gọi wgnet bằng lệnh

docker network create --subnet 172.30.0.0/24 wgnet

Kiểm tra lại thông số của wgnet bằng lệnh docker inspect wgnet

[
    {
        "Name": "wgnet",
        "Id": "395281362a007244f03d8b8008a12c55ecea141410143d970376029564d5a02c",
        "Created": "2022-09-28T21:40:39.761479649+07:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.30.0.0/24"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {},
        "Labels": {}
    }
]

Kiểm tra Routing Table hiện tại bằng lệnh ip route show

default via 192.168.0.1 dev ens18 
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown 
172.18.0.0/16 dev br-3e6bd5841382 proto kernel scope link src 172.18.0.1 
172.20.0.0/16 dev br-aadab51557e3 proto kernel scope link src 172.20.0.1 
172.22.0.0/16 dev br-31493810cb58 proto kernel scope link src 172.22.0.1 
172.24.0.0/16 dev br-e6e5decbac26 proto kernel scope link src 172.24.0.1 
172.30.0.0/24 dev br-395281362a00 proto kernel scope link src 172.30.0.1 linkdown 
192.168.0.0/24 dev ens18 proto kernel scope link src 192.168.0.138 

Có thể thấy mạng 172.30.0.0/24 đã được tạo và hiện ra trong routing table của máy chủ. Tuy nhiên nó đang có trạng thái linkdown vì chưa có container nào kết nối vào mạng này.

Hiện tại tất cả kết nối ra Internet đều được điều hướng qua LAN gateway 192.168.0.1, được cấu hình qua mục default via 192.168.0.1 dev ens18

Chúng ta cần phải chỉnh sửa lại routing table này sau khi đã thiết lập thành công Wireguard container, để cho kết nối mạng sẽ đi qua WireGuard chứ không phải qua LAN Gateway.

2. Tạo file cấu hình wg0.conf

Trong bài này mình sẽ lấy file cấu hình từ Wireguad VPN Server được thiết lập bằng Wirehole-UI. Truy cập vào dashboard của wg-easy, tạo client mới và tải file cấu hình về, có nội dung tương tự như dưới đây

[Interface]
PrivateKey = sO7U9OS0s5vxxxdfY1aNHzVYXpJhKAaU/HG9MyfaWU=
Address = 10.6.0.8/24
DNS = 10.2.0.100

[Peer]
PublicKey = 9NknLwkQRaxxxsfNr9wKIC15KsqRvN5eOwUZxuhDFWI=
PresharedKey = xhqj9bnxxxuuFtzbVJ5GJPD6D4YAMHdk6RbL5JVkT+M=
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 0
Endpoint = 222.222.96.107:51822

Với cấu hình này, một đường truyền tunnel sẽ được tạo ra và tất cả kết nố bên trong WireGuard container sẽ đi qua tunnel này. Tuy nhiên, chúng ta muốn còn kết nối của máy chủ, nằm ngoài container đi qua Wireguard Tunnel. Do đó, cần phải cấu hình NAT để kết nối đi đúng hướng bằng lệnh sau:

iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADE

Để thiết lập tự động, mình sẽ dùng thông số PostUp và PreDown trong file cấu hình của WireGuard.

  • PostUp: lệnh sẽ chạy sau khi WireGuard tunnel được tạo.
  • PostDown: lệnh sẽ chạy trước khi WireGuard tunnel bị hủy.
PostUp = iptables -t nat -A POSTROUTING -o wg+ -j MASQUERADE
PreDown = iptables -t nat -D POSTROUTING -o wg+ -j MASQUERADE

Lưu lại cấu hình cuối cùng trong thư mục /home/thuanbui/wireguad-client/config/wg0.conf

mkdir -p /home/thuanbui/wireguard-client/config
nano /home/thuanbui/wireguard-client/config/wg0.conf

Nhập vào nội dung sau và lưu lại

[Interface]
PrivateKey = sO7U9OS0s5vxxxdfY1aNHzVYXpJhKAaU/HG9MyfaWU=
Address = 10.6.0.8/24
DNS = 10.2.0.100
PostUp = iptables -t nat -A POSTROUTING -o wg+ -j MASQUERADE
PreDown = iptables -t nat -D POSTROUTING -o wg+ -j MASQUERADE

[Peer]
PublicKey = 9NknLwkQRaxxxsfNr9wKIC15KsqRvN5eOwUZxuhDFWI=
PresharedKey = xhqj9bnxxxuuFtzbVJ5GJPD6D4YAMHdk6RbL5JVkT+M=
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 0
Endpoint = 222.222.96.107:51822

3. Tạo Wireguard client container

Tạo file docker-compose.yml để thiết lập Wireguard Client

cd /home/thuanbui/wireguard-client
nano docker-compose.yml

Nhập vào nội dung sau

services:
  wireguard:
    image: lscr.io/linuxserver/wireguard
    container_name: wireguard
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Asia/Ho_Chi_Minh
    volumes:
      - ./config:/config
      - /lib/modules:/lib/modules
    networks:
      default:
        ipv4_address: 172.30.0.50
    sysctls:
      - net.ipv4.conf.all.src_valid_mark=1
    restart: unless-stopped
networks:
  default:
    name: wgnet
    external: true

Các thông số cần chú ý:

  • Phần networks được cấu hình để WireGuard container kết nối vào Docker netwok wgnet đã tạo trước đó.
  • Dòng 17: cấu hình IP tĩnh cho container 172.30.0.50

Kích hoạt Docker container bằng lệnh docker-compose up -d.

Để bảo đảm WireGuard Tunnel đã được tạo thành công, kiểm tra lại logs bằng lệnh docker logs wireguard. Nếu thấy nó kết thúc với nội dung như dưới đây nghĩa là Tunnel đã được tạo thành công

s6-rc: info: service 99-ci-service-check successfully started
Warning: `/config/wg0.conf' is world accessible
[#] ip link add wg0 type wireguard
[#] wg setconf wg0 /dev/fd/63
[#] ip -4 address add 10.6.0.8/24 dev wg0
[#] ip link set mtu 1420 up dev wg0
[#] resolvconf -a wg0 -m 0 -x
[#] wg set wg0 fwmark 51820
[#] ip -4 route add 0.0.0.0/0 dev wg0 table 51820
[#] ip -4 rule add not fwmark 51820 table 51820
[#] ip -4 rule add table main suppress_prefixlength 0
[#] sysctl -q net.ipv4.conf.all.src_valid_mark=1
sysctl: setting key "net.ipv4.conf.all.src_valid_mark", ignoring: Read-only file system
[#] iptables-restore -n
[#] iptables -t nat -A POSTROUTING -o wg+ -j MASQUERADE

Nếu trong logs hiện ra lỗi như dưới đây, bạn cần chỉnh sửa lại dòng AllowedIPs trong file wg0.conf, xóa ::/0 và chỉ chừa lại  0.0.0.0/0. Sau đó khởi động lại container.

Error: IPv6 is disabled on nexthop device.
[#] resolvconf -d wg0 -f
[#] ip link delete dev wg0

Kiểm tra lại routing table bằng lệnh ip route show

default via 192.168.0.1 dev ens18 
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown 
172.18.0.0/16 dev br-3e6bd5841382 proto kernel scope link src 172.18.0.1 
172.20.0.0/16 dev br-aadab51557e3 proto kernel scope link src 172.20.0.1 
172.22.0.0/16 dev br-31493810cb58 proto kernel scope link src 172.22.0.1 
172.24.0.0/16 dev br-e6e5decbac26 proto kernel scope link src 172.24.0.1 
172.30.0.0/24 dev br-395281362a00 proto kernel scope link src 172.30.0.1 
192.168.0.0/24 dev ens18 proto kernel scope link src 192.168.0.138 

Có thể thấy mạng 172.130.0.0/24 giờ đã được kích hoạt vì đã có WireGuard kết nối vào, không còn trạng thái linkdown nữa.

Tuy nhiên lúc này, kết nối ra ngoài của máy chủ vẫn chưa chạy qua WireGuard Tunnel, kiểm tra bằng lệnh curl ifconfig.me.

115.xxx.xxx.xx

Kết quả trả về là Public IP của máy chủ nghĩa là kết nối vẫn chưa đi qua WireGuard tunnel.

4. Chỉnh sửa routing table

Sử dụng lệnh sau để chỉnh sửa lại Routing table

sudo ip route del default
sudo ip route add 222.222.96.107 via 192.168.0.1
sudo ip route add default via 172.30.0.50
  • Dòng 1: xóa routing mặc định
  • Dòng 2: 222.222.96.107 là IP của WireGuard VPN, cần phải được đi qua LAN Gateway để tránh bị vòng lặp
  • Dòng 3: tạo routing mặc định cho mọi kết nối đi qua IP của WireGuard container 172.30.0.50

Kiểm tra lại ip route show sẽ thấy kết nối ra ngoài được điều hướng đi qua 172.30.0.50

default via 172.30.0.50 dev br-395281362a00 
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown 
172.18.0.0/16 dev br-3e6bd5841382 proto kernel scope link src 172.18.0.1 
172.20.0.0/16 dev br-aadab51557e3 proto kernel scope link src 172.20.0.1 
172.22.0.0/16 dev br-31493810cb58 proto kernel scope link src 172.22.0.1 
172.24.0.0/16 dev br-e6e5decbac26 proto kernel scope link src 172.24.0.1 
172.30.0.0/24 dev br-395281362a00 proto kernel scope link src 172.30.0.1 
192.168.0.0/24 dev ens18 proto kernel scope link src 192.168.0.138 
222.222.96.107 via 192.168.0.1 dev ens18 

Kiểm tra lại IP của máy chủ bằng lệnh curl ifconfig.me

222.222.96.107

Kết quả trả về bây giờ là IP của WireGuard Server. Nghĩa là chúng ta đã cấu hình thành công cho kết nối của máy chủ đi qua WireGuard Tunnel. Hura!

5. Thiết lập System Service

Routing table của máy chủ sẽ quay lại mặc định mỗi khi khởi động lại máy, khiến cho các thông số vừa mới chỉnh không còn hiệu lực. Để cấu hình cho kết nối mạng của máy chủ luôn luôn đi qua WireGuard Tunnel, chúng ta sẽ tạo 1 system service để nó tự động chỉnh sửa routing table mỗi khi khởi động

sudo nano /lib/systemd/system/iproute.service

Nhập vào nội dung sau

[Unit]
Description=Route everything through WireGuard
After=docker.service

[Service]
Type=oneshot
Restart=on-failure
ExecStart=ip route del default
ExecStart=ip route add 222.222.96.107 via 192.168.0.1
ExecStart=ip route add default via 172.30.0.50

[Install]
WantedBy=multi-user.target

Kích hoạt service

sudo systemctl enable iproute.service

Vậy là xong. Máy chủ sẽ luôn tự động kết nối thông qua WireGuard Tunnel mỗi khi khởi động lại.

Chúc bạn thực hiện thành công!

Nguồn tham khảo: https://www.linuxserver.io/blog/routing-docker-host-and-container-traffic-through-wireguard