Dựng mạng VPN với WireGuard
Bài viết được sự cho phép của tác giả Nguyễn Hồng Quân
Tiếp nối bài trước, Câu chuyện sử dụng VPN, tôi xin mô tả cách dựng một mạng VPN cho mục đích cá nhân bằng phần mềm WireGuard.
Mục đích sử dụng mạng VPN của tôi chỉ là để truy cập từ xa vào các thiết bị IoT của tôi nên mô hình VPN của tôi chỉ phù hợp với nhu cầu đó. Nếu bạn cần dùng VPN cho mục đích khác, ví dụ để vượt tường lửa, truy cập vào website bị chặn, thì bạn không nên trông đợi gì vào bài viết này.
Trước khi bắt tay vào việc, ta cần mường tượng sơ đồ mạng sẽ như thế nào:
Phải luôn có một máy có địa chỉ IP tĩnh làm server. Nó cũng đóng vai trò router trong mạng ảo (VPN) được tạo ra. Các gói tin từ máy con A sẽ được mã hóa, gửi lên server, server sẽ chuyển tiếp tới máy con B rồi gói tin được giải mã tại đó.
Ta sẽ chọn dải IP cho mạng ảo này. Ví dụ tôi chọn dải 192.168.2.0/24. Trong thực tế, ta cần chọn dải nào sao cho không đụng chạm đến bất cứ mạng LAN thật nào của từng máy con. Vì server đóng vai trò router nên ta sẽ dành địa chỉ 192.168.2.1 cho nó.
Cài đặt WireGuard
Việc cài đặt WireGuard thì khá đơn giản, đã có trên tài liệu. Trước khi cài WireGuard thì bạn phải cài trước header source code của Linux. Chuyện này khá đơn giản trên desktop và server, vì câu lệnh để cài WireGuard, sudo apt install wireguard
sẽ tự kéo gói header của Linux về, tự cài. Nhưng trên máy tính nhúng như BeagleBone, Raspberry Pi, bạn phải tự chỉ định và cài gói header đó.
Trên Raspberry Pi, tên gói là raspberrypi-kernel-headers
, nên bạn cài bằng:
sudo apt install raspberrypi-kernel-headers
Trên BeagleBone thì phức tạp hơn. Đầu tiên bạn phải xác định xem BeagleBone của bạn đang chạy Linux phiên bản nào:
$ uname -a Linux beaglebone 4.4.88-ti-r125 #1 SMP Thu Sep 21 19:23:24 UTC 2017 armv7l GNU/Linux
Tìm kiếm gói header theo tên linux-headers-[version]
:
$ apt search linux-headers-4.4.88-ti-r125 Sorting... Done Full Text Search... Done linux-headers-4.4.88-ti-r125/unknown,now 1stretch armhf Linux kernel headers for 4.4.88-ti-r125 on armhf
Ok, vậy là gói linux-headers-4.4.88-ti-r125
có tồn tại trong repo, hãy cài đặt nó:
sudo apt install linux-headers-4.4.88-ti-r125
Trong trường hợp bạn cấu hình APT repo cho Raspberry Pi, bạn có thể gặp lỗi này:
$ sudo apt update Get:1 http://raspbian.raspberrypi.org/raspbian stretch InRelease [15.0 kB] Get:2 http://deb.debian.org/debian unstable InRelease [233 kB] Hit:3 http://archive.raspberrypi.org/debian stretch InRelease Get:4 http://raspbian.raspberrypi.org/raspbian stretch/main armhf Packages [11.7 MB] Err:2 http://deb.debian.org/debian unstable InRelease The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 8B48AD6246925553 NO_PUBKEY 7638D0442B90D010 NO_PUBKEY 04EE7237B7D453EC Reading package lists... Done W: GPG error: http://deb.debian.org/debian unstable InRelease: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 8B48AD6246925553 NO_PUBKEY 7638D0442B90D010 NO_PUBKEY 04EE7237B7D453EC E: The repository 'http://deb.debian.org/debian unstable InRelease' is not signed. N: Updating from such a repository can't be done securely, and is therefore disabled by default. N: See apt-secure(8) manpage for repository creation and user configuration details.
Có thể khắc phục bằng cách chạy lệnh sau:
$ sudo apt install dirmngr $ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 04EE7237B7D453EC
Cấu hình cho WireGuard
Vì server sẽ đóng vai trò router, ta cần cấu hình kernel của server để cho phép “IPv4 forwarding”:
$ echo 'net.ipv4.ip_forward=1' | sudo tee /etc/sysctl.d/99-ipv4_forwarding.conf $ sudo sysctl -p /etc/sysctl.d/99-ipv4_forwarding.conf
Trên từng máy, ta làm theo hướng dẫn trên trang chủ để tạo key. Các file sinh ra privatekey, publickey nằm đâu cũng được, vì sau khi cấu hình xong ta có thể xóa nó đi.
Trên máy con A, ta tạo file /etc/wireguard/wg0.conf với nội dung như sau:
[Interface] PrivateKey = private_key_cua_may_A Address = 192.168.2.2/24 [Peer] PublicKey = public_key_cua_server Endpoint = ip_cua_server:51820 AllowedIPs = 192.168.2.0/24
Trên máy con B, ta tạo file tương tự, với nội dung như sau:
[Interface] PrivateKey = private_key_cua_may_B Address = 192.168.2.3/24 [Peer] PublicKey = public_key_cua_server Endpoint = ip_cua_server:51820 AllowedIPs = 192.168.2.0/24
Trên server, ta tạo file ở đường dẫn giống vậy, với nội dung như sau:
[Interface] Address = 192.168.2.1/24 PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o ens18 -j MASQUERADE; ip6tables -A FORWARD -i wg0 -j ACCEPT; ip6tables -t nat -A POSTROUTING -o ens18 -j MASQUERADE PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o ens18 -j MASQUERADE; ip6tables -D FORWARD -i wg0 -j ACCEPT; ip6tables -t nat -D POSTROUTING -o ens18 -j MASQUERADE ListenPort = 51820 PrivateKey = private_key_cua_server [Peer] # May A PublicKey = public_key_cua_may_A AllowedIPs = 192.168.2.2/32 [Peer] # May B PublicKey = public_key_cua_may_B AllowedIPs = 192.168.2.3/32
Lưu ý sự khác nhau giữa các file trên:
- File cấu hình trên máy con thì chỉ cần 1 mục
[Peer]
để điền thông tin của server. - File cấu hình trên server thì có nhiều mục
[Peer]
, mỗi cái tương ứng với một máy con. - Vì server đóng vai trò router nên ta có một chùm cấu hình
iptables
kèm theo. Trong các dòngiptables
này, bạn cần thayens18
bằng tên card mạng trên server của bạn (xem danh sách card mạng bằng lệnhip link
hay ngắn gọn hơn,ip l
).
Một vài lưu ý khác:
- Đừng bao giờ xài
AllowedIPs = 0.0.0.0/0
trên file cấu hình của client (máy con), như một số bài hướng dẫn khác trên mạng, nếu không chức năng DNS sẽ tê liệt.
Tham khảo việc làm lập trình nhúng mới nhất tại đây!
Kiểm tra
Sau khi ghi các file cấu hình kể trên, thử chạy
$ sudo wg-quick up wg0 $ ip a
để xem đã có network interface nào tên wg0
được tạo ra và gán địa chỉ chưa.
Ví dụ, đây là kết quả trên máy tôi:
$ ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: eno1: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc fq_codel state DOWN group default qlen 1000 link/ether a0:2b:b8:24:0b:55 brd ff:ff:ff:ff:ff:ff 3: wlo1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000 link/ether 9c:d2:1e:7d:f7:e7 brd ff:ff:ff:ff:ff:ff inet 192.168.1.88/24 brd 192.168.1.255 scope global dynamic noprefixroute wlo1 valid_lft 79700sec preferred_lft 79700sec inet6 2001:ee0:5511:860:a8c1:937b:b26:8095/64 scope global temporary dynamic valid_lft 1198sec preferred_lft 1198sec inet6 2001:ee0:5511:860:64ca:7ee4:ac78:5940/64 scope global dynamic mngtmpaddr noprefixroute valid_lft 1198sec preferred_lft 1198sec inet6 fe80::236c:9478:b305:1c05/64 scope link noprefixroute valid_lft forever preferred_lft forever 4: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000 link/none inet 192.168.2.2/24 scope global wg0 valid_lft forever preferred_lft forever
Bảng route:
$ ip r default via 192.168.1.1 dev wlo1 proto dhcp metric 600 169.254.0.0/16 dev wlo1 scope link metric 1000 192.168.1.0/24 dev wlo1 proto kernel scope link src 192.168.1.88 metric 600 192.168.2.0/24 dev wg0 proto kernel scope link src 192.168.2.2
Trên các máy con, thử ping về server bằng địa chỉ của mạng VPN:
ping 192.168.2.1
Trên máy con A, thử ping đến máy con B bằng địa chỉ của mạng VPN:
ping 192.168.2.3
Nếu việc ping thành công thì chúc mừng, bạn đã hoàn tất 95%.
Duy trì mạng VPN
Vì mạng VPN chỉ được tạo ra bằng lệnh wg-quick up
, nên sau khi bạn khởi động lại máy, mạng sẽ biến mất. WireGuard có cung cấp file systemd service để máy tự chạy wq-quick up
mỗi lần khởi động. Kích hoạt service này trên server bằng lệnh:
sudo systemctl enable wg-quick@wg0
Trong trường hợp của tôi, tôi cần truy cập từ laptop (máy A) vào board Raspberry Pi (máy B) nên tôi cũng cần kích hoạt service kia trên máy B. Riêng laptop thì không cần.
Làm được tới đây, nếu bạn khởi động lại máy B, bạn sẽ không ping được từ A vào B, cho dù wg0
vẫn đang hoạt động trên máy B và trên server. Tại sao? Đó là vì khi gói tin từ A lên server, server không biết máy B ở đâu để mà chuyển tiếp đến (nên nhớ là máy B đang nằm khuất sau 1 router nào đó nên nó không có địa chỉ IP public). Muốn server biết máy B ở đâu thì phải có 1 sự tương tác giữa máy B và server. Ta phải duy trì sự tương tác này để vị trí của máy B luôn được cập nhật. Để làm việc này, ta thêm tùy chọn PersistentKeepalive
vào file của máy B như sau:
[Interface] PrivateKey = private_key_cua_may_B Address = 192.168.2.3/24 [Peer] PublicKey = public_key_cua_server Endpoint = ip_cua_server:51820 AllowedIPs = 192.168.2.0/24 PersistentKeepalive = 60
Thông số trên có nghĩa là cứ 60s, WireGuard trên máy B sẽ gửi một gói tin đặc biệt đến server để duy trì kết nối VPN.
Phụ chú
Trước đây, khi tôi chưa biết đến tham số PersistentKeepalive
thì giải pháp của tôi là làm cho máy B cứ tự động ping đến server sau mỗi khoảng thời gian nhất định. Tôi sẽ ứng dụng systemd timer để làm việc này.
Tạo 2 file như sau trên máy B:
ping-wireguard-server.service:
[Unit] Description=Ping WireGuard server [Service] Type=oneshot ExecStart=/bin/ping -c 2 192.168.2.1
ping-wireguard-server.timer:
[Unit] Description=Ping WireGuard server every minute [Timer] OnBootSec=2 OnUnitActiveSec=60 [Install] WantedBy=timers.target
Copy 2 file trên vào /usr/local/lib/system/system/ rồi chạy lệnh sau để kích hoạt timer:
$ sudo systemctl enable ping-wireguard-server.timer
$ sudo systemctl start ping-wireguard-server.timer
Bạn cũng có thể làm công cụ ping tương tự với cron
, nhưng tôi không thích cron lắm.
Truy cập SSH mà không cần bật WireGuard
Thật ra, nếu chỉ để SSH thì ta chỉ cần duy trì mạng VPN trên máy đích chứ không cần bật WireGuard trên máy của ta. Khi đó ta sẽ sử dụng server làm “đá bước dặm” để “nhảy cóc” đến máy bên kia, và kết nối từ máy ta đến server vẫn là kết nối trực tiếp, không qua VPN. Ví dụ ta sẽ dùng tính năng ProxyCommand của SSH.
Đầu tiên, mở file ~/.ssh/config và đặt lối tắt cho server:
Host my-server User user_tren_server Hostname ip_cua_server
Trong đó ip_cua_server
là IP trực tiếp, không phải IP trong mạng VPN của server. Tiếp theo, tạo lối tắt cho máy đích
Host may-b User user_tren_may_b Hostname ip_vpn_may_b ProxyJump my-server
trong đó, ip_vpn_may_b
là IP trong mạng VPN của máy đích.
Như vậy, chỉ cần gõ:
ssh may-b
ta đã có thể SSH vào máy B.
Một số góp ý của độc giả
1. Có thể cài linux-headers theo version của kernel ngắn hơn bằng lệnh sau:
apt install linux-headers-$(uname -r)
2. Gần đây wireguard-tools
trên repo unstable
của debian bị outdated, gây ra conflict nếu cài thẳng wireguard
bằng apt. Cách giải quyết: tải gói wireguard-tools.deb
về cài tay, rồi mới cài wireguard được.
Bài viết gốc được đăng tải tại quan.hoabinh.vn
Xem thêm:
- Xu hướng phát triển của IoT hiện nay
- BlenderBot 3 – Chatbot AI “nói xấu” Mark Zuckerberg
- Ứng dụng của phép toán Bitwise
Tìm việc làm IT mới nhất tại đây!
- B BenQ RD Series – Dòng Màn Hình Lập Trình 4k+ Đầu Tiên Trên Thế Giới
- F Framework nào tốt nhất cho dự án của bạn? – Checklist chi tiết
- K Kinh nghiệm xử lý responsive table hiệu quả
- S Stackoverflow là gì? Bí kíp tận dụng Stack Overflow hiệu quả
- 7 7 kinh nghiệm hữu ích khi làm việc với GIT trong dự án
- B Bài tập Python từ cơ bản đến nâng cao (có lời giải)
- B Bảo mật API là gì? Một số nguyên tắc và kỹ thuật cần biết
- H Hướng dẫn cài đặt và tự học lập trình Python cơ bản từ A-Z
- C Chinh Phục Phân Tích Dữ Liệu Với Pandas Trong Python: Hướng Dẫn Từng Bước
- D Display CSS là gì? Cách khai báo và sử dụng thuộc tính display trong CSS