이번 시간에는 (지난 시간에 이어) QR-WireGuard 자동 연결(a.k.a AutoConnect) 기능에 대해 소개해 보고자 한다. 😎
목차
1. Gl-iNet MT2500과 NanoPi R5S Security Gateway 소개
2. WireGuard AutoConnect 기능 소개
3. WireGuard AutoConnect 기능 설계 및 구현
4. WireGuard AutoConnect 동작 시험과 남은 작업
5. References
그동안 다소 느슨(?)했던 시간을 뒤로 하고, 이제 다시 새로운 출발을 앞두고 있다. 아쉽지만 이번 posting을 끝으로 당분간은 blog 활동을 잠정적으로 중단해야 할 것 같다. 언젠가 다시 복귀할 날을 기약하며, 그동안 부족한 글에 많은 관심을 보여주신 독자 여러분에게 진심으로 감사의 마음을 전한다. 💚
오래 전 Jeju Island 겨울 바다에서~
흔히들 "C는 2년을 공부해서 20년간 써 먹지만, C++는 20년 공부해서 2년간만 써 먹는다"라고들 한다(누가 그런 소릴 ?). 이는 그만큼 C++가 정복하기에 매우 난해한 언어라는 점을 애둘러 표현한 말이 아닐까 싶다. 최근에는 C++를 위협하는 새로운 language로 Rust(성능면에서 C/C++와 대등, 난이도 상)나 Go(성능은 상대적으로 떨어지나 현대적이며 배우기 수월함) 같은 것들도 유행하고 있으니, C++의 입지가 더욱더 좁아지고 있음도 부인할 수 없는 사실이다. 하지만, 그럼에도 불구하고 C++11로 대표되는 Modern C++만의 장점을 깨닫고 이를 정복할 수만 있다면, 그 동안 알지 못했던 새로운 세상(?)이 여러분 앞에 펼쳐질 지도 모를 일이다. 💢
1. Gl-iNet MT2500과 NanoPi R5S Security Gateway 소개
필자는 개인적으로 2 ~ 3 ports로 구성된 소형 Security Gateway를 좋아한다. 😍 이유는 이것만 있으면 왠만한 모든(?) 것이 가능하기 때문이다. 이번 장에서는 WireGuard AutoConnect 기능을 본격적으로 구현하기에 앞서, 이를 시험할 두종류의 target 장치(security gateway)를 먼저 소개하고자 한다.
1.1) Gl-iNet MT2500 Security Gateway
Gl-iNet 제품은 거의 7-8년전 부터 사용해 오고 있는데, 장치 외관이나 WebUI도 무척 예쁘고, 실용성이 뛰어나기로 정평이 나있다. ☆☆☆☆☆ (어느 순간 부터) OpenWrt full source code를 open하지 않고 있는 것이 단점이긴 하지만, 그럼에도 불구하고 간단한 program을 올려 test해 볼 수 있다는 점(ssh root 계정이 open되어 있음)에서, 여느 상용 Access Point 제품과도 차별화된다고 말할 수 있을 듯하다.
이번 posting에서는 여러 Gl-iNet 제품 중, wireguard AutoConnect client 용으로 MT2500(Brume 2)을 사용하고자 한다.
📌 이 글을 쓰고 있는 현재 $59에 판매되고 있다. 👍
[그림 1.1] Gl-iNet MT2500 (플라스틱 케이스 모델) [출처 - 참고문헌 5]
📌 위의 모델은 플라스틱 케이스로 되어 있어, 매우 가벼운 특징이 있다. 이 밖에도 알루미늄 케이스가 장착된 MT2500A 모델도 있다.
[그림 1.2] Gl-iNet MT2500 PCB [출처 - 참고문헌 5]
Gl-iNet 전 제품은 이미 WireGuard와 OpenVPN이 탑재되어 있어, VPN client나 server로도 활용이 가능하다.
[그림 1.3] Gl-iNet MT2500 VPN 기능 [출처 - 참고문헌 5]
Gl-iNet 제품의 수려한 WebUI는 아래와 같은 모습을 하고 있다. 😊
[그림 1.4] Gl-iNet MT2500 WebUI
📌Gl-iNet 제품의 또 다른 장점은 network을 통한 firmware upgrade가 매우 유연하게 동작한다는 점이다.
Gl-iNet MT2500의 h/w 스펙은 다음과 같다.
[그림 1.5] Gl-iNet MT2500 H/W 스펙 [출처 - 참고문헌 5]
Gl-iNet MT2500(A)용 OpenWrt source code를 download 후, build하는 과정을 소개하고 싶으나, Gl-iNet github에는 아직 MT2500(A) 용 openwrt source가 올라와 있지 않다. 😈
일단 우리에게 필요한 것은 toolchain이므로, 아래 site의 내용(동일 CPU MediaTek MT7981B dual-core 사용)을 이용해서 간접적으로 cross toolchain을 만들어 보도록 하자.
<Ubuntu 22.04 LTS 환경에서 개발 환경 구축하기>
$ cd ~/workspace/gl-inet/gainstrong/openwrt-2102-Oolite-MT7981
$ unzip openwrt-2102-Oolite-MT7981.zip
$ cd openwrt-2102-Oolite-MT7981/
$ ./scripts/feeds update -a
$ ./scripts/feeds install -a
$ make menuconfig
-> CPU MediaTek MT7981B 선택
[그림 1.6] make menuconfig - Target System, Subtarget 조정 모습
$ make
Build에 성공할 경우, toolchain은 아래 디렉토리에 만들어지게 된다.
$ ls -l staging_dir/toolchain-aarch64_cortex-a53_gcc-8.4.0_musl/bin
$ ./aarch64-openwrt-linux-musl-gcc --version
aarch64-openwrt-linux-musl-gcc (OpenWrt GCC 8.4.0 r16630-b8cb12f77b) 8.4.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1.2) FriendlyELEC NanoPi R5S
NanoPi 제품은 이전 posting을 통해 여러 차례 소개한 바 있다. NanoPi 제품의 장점은 뭐니뭐니 해도 상대적으로 발열이 적고, 제공하는 s/w의 완성도(full source code 제공)가 높다는 점을 들 수가 있다. 😍 이번에 소개할 제품은 3개의 ethernet ports(2 x 2.5G LAN, 1 x 1G WAN)로 무장한 NanoPi R5S 모델인데, 이번 posting에서는 이를 wireguard AutoConnect server 용으로 사용하고자 한다.
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjE8NejMp1PF6E0o2zPDhyo7mtNMTSheNWux4yTgcZuSlk4yJTsJ5OwCIvs0BYwoPK7LjkzFlyIQPn5Nc4GqxE6A8MnB3T5U_MlGF4LKWZuIU6dZhY9ULkbmqGxYT_cIL0k3B5dEpPwDBDhRSYmG5bQe_sdfsAtNI-LggaVYHwViE_Khgze-kmyCfcoScXG/w366-h400/nanopi_r5s_pcb.png)
[그림 1.7] FriendlyELEC NanoPi R5S(1) - Board [출처 - 참고문헌 3]
[그림 1.8] FriendlyELEC NanoPi R5S(2) - Case 장착 [출처 - 참고문헌 3]
📌 얘는 케이스가 쇳덩이 인지라 무지 무겁다. 💪 가격은 $65로 되어 있다.
[그림 1.9] FriendlyELEC NanoPi R5S - CPU Rockchip RK3568 [출처 - 참고문헌 4]
자, 그럼 지금부터는 아래 wiki page를 참조하여 friendlywrt(OpenWrt) source code를 내려 받아 full build를 진행해 보도록 하자.
<Ubuntu 22.04 LTS 환경에서 개발 환경 구축하기>
$ mkdir friendlywrt23-rk3568
$ cd friendlywrt23-rk3568
$ git clone https://github.com/friendlyarm/repo --depth 1 tools
$ tools/repo init -u https://github.com/friendlyarm/friendlywrt_manifests -b master-v23.05 -m rk3568.xml --repo-url=https://github.com/friendlyarm/repo --no-clone-bundle
$ tools/repo sync -c --no-clone-bundle
$ ./build.sh rk3568.mk
-> error 없이 한방에 build가 된다.
...
Writing inode tables: done
Creating journal (4096 blocks): done
Copying files into the device: done
Writing superblocks and filesystem accounting information: done
-----------------------------------------
rootfs dir:
/mnt/hdd/workspace/mini_devices/0122025/friendlywrt23-rk3568/scripts/sd-fuse/out/rootfs.yHEc7yQ9z
boot dir:
/mnt/hdd/workspace/mini_devices/0122025/friendlywrt23-rk3568/scripts/sd-fuse/out/boot.8OzSRsAYY
-----------------------------------------
Creating RAW image: out/friendlywrt_23.05_20250122_rk3568_arm64_sd.img (1500 MB)
---------------------------------
0+0 레코드 들어옴
0+0 레코드 나감
0 bytes copied, 0.000343003 s, 0.0 kB/s
----------------------------------------------------------------
[out/friendlywrt_23.05_20250122_rk3568_arm64_sd.img] capacity = 1430MB, 1499999232 bytes
current out/friendlywrt_23.05_20250122_rk3568_arm64_sd.img partition:
----------------------------------------------------------------
parsing ./friendlywrt23/parameter.txt:
create new GPT 9:
----------------------------------------------------------------
copy from: ./friendlywrt23 to out/friendlywrt_23.05_20250122_rk3568_arm64_sd.img
[RAW. 0]: 310 KB | ./friendlywrt23/idbloader.img > 100% : done.
[RAW. 1]: 4096 KB | ./friendlywrt23/uboot.img > 100% : done.
[RAW. 2]: 48 KB | ./friendlywrt23/misc.img > 100% : done.
[RAW. 3]: 1 KB | ./friendlywrt23/dtbo.img > 100% : done.
[RAW. 4]: 7185 KB | ./friendlywrt23/resource.img > 100% : done.
[RAW. 5]: 35984 KB | ./friendlywrt23/kernel.img > 100% : done.
[RAW. 6]: 7759 KB | ./friendlywrt23/boot.img > 100% : done.
[RAW. 8]: 386444 KB | ./friendlywrt23/rootfs.img > 100% : done.
[RAW. 9]: 156 KB | ./friendlywrt23/userdata.img > 100% : done.
----------------------------------------------------------------
---------------------------------
RAW image successfully created (14:42:06).
-rw-rw-r-- 1 chyi chyi 1499999232 1월 22 14:42 out/friendlywrt_23.05_20250122_rk3568_arm64_sd.img
Tip: You can compress it to save disk space.
-----------------------------------------
Run the following command for sdcard install:
sudo dd if=out/friendlywrt_23.05_20250122_rk3568_arm64_sd.img bs=1M of=/dev/sdX
-----------------------------------------
Build 결과, toolchain 디렉토리는 아래의 위치에 만들어 진다.
$ cd friendlywrt23-rk3568/friendlywrt/staging_dir/toolchain-aarch64_generic_gcc-12.3.0_musl/bin
$ ./aarch64-openwrt-linux-musl-gcc --version
aarch64-openwrt-linux-musl-gcc (OpenWrt GCC 12.3.0 r24106-10cc5fcd00) 12.3.0
Copyright (C) 2022 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
📌 시간 관계 상 kernel & u-boot code를 살펴 보는 것은 생략하기로 한다. 😂
chyi@earth:~/friendlywrt23-rk3568$ ls -la
합계 48
drwxrwxr-x 12 chyi chyi 4096 1월 22 13:50 .
drwxrwxr-x 3 chyi chyi 4096 1월 22 13:36 ..
lrwxrwxrwx 1 chyi chyi 36 1월 22 13:50 .current_config.mk -> device/friendlyelec/rk3568/rk3568.mk
drwxrwxr-x 7 chyi chyi 4096 1월 22 13:42 .repo
lrwxrwxrwx 1 chyi chyi 16 1월 22 13:42 build.sh -> scripts/build.sh
drwxrwxr-x 5 chyi chyi 4096 1월 22 13:42 configs
drwxrwxr-x 4 chyi chyi 4096 1월 22 13:42 device
drwxrwxr-x 18 chyi chyi 4096 1월 22 14:30 friendlywrt
drwxrwxr-x 28 chyi chyi 4096 1월 22 13:54 kernel
lrwxrwxrwx 1 chyi chyi 19 1월 22 13:42 out -> scripts/sd-fuse/out
drwxrwxr-x 10 chyi chyi 4096 1월 22 13:50 rkbin
drwxrwxr-x 4 chyi chyi 4096 1월 22 13:42 scripts
drwxrwxr-x 5 chyi chyi 4096 1월 22 13:42 toolchain
drwxrwxr-x 7 chyi chyi 4096 1월 22 13:37 tools
drwxrwxr-x 28 chyi chyi 4096 1월 22 13:50 u-boot
____________________________________
지금까지, Gl-iNet MT2500과 FriendlyELEC NanoPi R5S 두 제품을 (정말) 가볍게 살펴 보았다. 부족한 부분은 앞서 제시한 link를 참조해 주기 바란다. 🍦
2. WireGuard AutoConnect 기능 소개
WireGuard는 다 좋은데, 최초 설정 시 상대방의 public key(ECC curve25519)를 사전에 미리 교환해야만 통신이 가능한 구조이다. 다시 말해 상대방과의 암호 통신에 필요한 public key 정보를 사전에 online(예: mail)이나 offline으로 교환한 상태에서만 통신이 가능하다는 얘기다. 이 같은 사실은 간단한 1 대 1 통신이라면 전혀 문제가 될게 없겠지만, 통신하려는 대상이 많을 경우(예: N 대 1 통신)에는 심각한 문제가 아닐 수 없다. 따라서 이번 posting에서는 이를 자동으로 해결해 줄 수 있는 protocol(일명 WireGuard AutoConnect)을 간단히 설계하고, 이를 Modern C++을 통해 구현하는 과정을 소개해 보고자 한다.
[그림 2.1] WireGuard AutoConnect 개요(1)
📌 AutoConnect는 사용자가 특별히 설정을 하지 않아도, 부팅하면서 자동으로 서버와 통신하여 wireguard tunnel을 생성하는 프로토콜이다.
AutoConnect를 이해하기 위해서는 WireGuard에 대한 이해가 선행되어야 한다. 😎
<WireGuard 관련 이전 posting 모음>
____________________________________________
(이번 posting을 통해 구현하고자 하는) WireGuard AutoConnect는 wireguard handshaking(NoiseIK)을 수행하기 위해 필요한 정보, 즉 peer의 public key(Curve25519), allowed-ips 정보, endpoint ip/port, keepalive duration 등을 사전에 안전하게 교환하는 행위를 말한다.
[그림 2.2] WireGuard AutoConnect 개요(2)
[그림 2.3] WireGuard AutoConnect 개요(3)
이렇게 구현한 WireGuard AutoConnect 기능은 이전 posting에서 소개한 qr-wireguard-ui project랑 연계해서 사용이 가능하다(가능하도록 만들 것이다).
[그림 2.4] WireGuard AutoConnect 개요(4) - qr-wireguard-ui 프로젝트와 연동
<AutoConnect 설계 방향>
1) (공통 사항) client와 server간의 통신 protocol 정의 필요
-> message format 정의해야 함(HELLO/BYE/PING/PONG/OK/NOK)
-> 복수개의 client 접속 시도 시 처리 가능한 구조 고려해야 함.
-> 자체 암호 통신 고려해야 함(libsodium library를 활용하자).
2) client는 embedded device에 탑재, server는 intel appliance에 탑재된다고 가정
-> wg_autoc(wireguard autoconnect client) <=> wg_autod(wireguard autoconnect daemon)
3) (server 기능) client에 대한 vpn ip를 자동 할당해주는 기능 필요
-> dhcp server code를 참조할 필요가 있을 듯 함.
4) (server 기능) client로 부터 정보를 수신한 후, 이를 관리하는 client table 필요
-> C++ map 기능 활용 : client 고유 정보를 이용하여 map에서 검색
-> client 정보 추가/갱신/삭제/조회 기능
-> (향후) Redis DB 등과 연계해야 함.
5) (공통 사항) client/server 모두 wireguard kernel이 설치된 환경을 가정
-> quantum-resistant wireguard 활용
6) (공통 사항) 이전 posting의 qr-wireguard-ui project와 연동 가능해야 함.
-> client & server => vtysh => wg => wireguard kernel
____________________________________________________
3. WireGuard AutoConnect 기능 설계 및 구현
이번 장에서는 WireGuard AutoConnect 기능을 구현하는 과정을 소개하도록 한다.
3.1) AutoConnect 통신 프로토콜 설계 및 구현
AutoConnect 프로토콜(일명 PING-PONG 프로토콜)의 대략적인 모습과 client & server 간에 주고받는 message format을 정리해 보면 다음과 같다.
[그림 3.1] AutoConnect 프로토콜(1) - message 교환(1)
📌 HELLO <-> HELLO, PING <-> PONG, BYE <-> BYE로 이루어진 간단한 프로토콜이다. 실패 시에는 NOK가 날라온다.
[그림 3.2] AutoConnect 프로토콜(1) - message 교환(2)
AutoConnect Client의 code 흐름과 s/w 구조를 대략적으로 정리해 보면 다음과 같다.
<AutoConnect Protocol - Client>
1) message 수신 처리는 별도의 thread에서 한다.
-> thread는 message 수신 후, 무조건 queue에 message(수신 message queue)를 넣는다.
-> queue에서 message를 가져가는 것은 main thread에서 진행한다.
2) HELLO message를 보낸다.
-> eth0 interface에 대한 MAC address만 전송하다.
3) (서버로 부터) HELLO message 수신하면, PING message를 보내는 코드로 분기한다.
-> NOK message 수신하면, 10초 대기 후, 다시 시도한다.
4) HELLO send/HELLO recv는 하나의 함수내에서 처리하자.
-> recv는 수신 message queue에서 message를 가져와서 처리한다.
5) PING message를 보낸다.
-> wireguard tunnel 생성에 필요한 정보를 모두 보낸다.
6) PONG message를 수신하면, server(wg_autod)의 wg tunnel 정보 추출한다.
-> NOK message 수신하면, 10초 대기 후, 다시 처음부터 시도한다.
7) PONG message 수신 시, wireguard 설정 후, loop를 빠져 나온다.
-> 이후 wait 한다(while loop).
8) PING send/PONG recv도 하나의 함수에서 치리하자.
-> recv는 수신 message queue에서 message를 가져와서 처리한다.
9) 사용자로 부터 signal 받으면, BYE message를 보낸다.
-> (서버로 부터) BYE/NOK를 받는다.
-> BYE를 수신한 server는 client 정보를 삭제한다.
-> BYE message는 일반적으로 발생하지 않으며, 필요시에만 활용하도록 한다.
[그림 3.3] AutoConnect 프로토콜(2) - Client Architecture(1)
[그림 3.4] AutoConnect 프로토콜(2) - Client Architecture(2)
3.3) Server wg_autod 구현
한편, Server의 code 흐름과 s/w architecture는 다음과 같다.
<AutoConnect Protocol - Server>
1) message 송/수신 처리는 별도의 thread에서 한다.
-> main thread는 tcp accept 후, 별도의 client(thread)를 만들어 송/수신 처리를 위임한다.
2) HELLO message를 수신한 후, client의 MAC address를 추출한다.
-> 이어 peer table을 별도로 하나 만든다(hash map을 이용하여 구현).
-> peer table은 GET/ADD/UPDATE/REMOVE operation을 제공한다.
-> 추후, redis table에 저장하는 형태로 개선한다(부팅 후에도 정보 유지 차원).
3) client에 대한 vpn ip를 신규 할당하고, 이를 HELLO에 대한 응답 message에 실어 보낸다.
-> vpn ip 할당 방법은 (효과적인 방법에 관하여) 고민이 필요해 보인다.
4) PING message를 수신한다. client의 vpn ip 설정 정보를 추출한 후, peer table에 추가한다(UPDATE).
5) PING에 대한 응답으로 PONG message를 보낸다.
-> 자신의 wireguard tunnel 설정에 필요한 정보를 실어 전달한다.
6) 이후, wireguard 설정을 진행한다.
-> wg 명령과 vtysh 명령을 이용한 2가지 방법을 구현한다.
7) 새로운 client로 부터 요청이 들어올 경우, 위의 2~6 항의 내용을 반복한다.
8) client로 부터 BYE message가 도착한 경우, peer table로 부터 해당 entry를 삭제한다.
[그림 3.5] AutoConnect 프로토콜(3) - Server Architecture(1)
서버는 아래 그림과 같이 복수개의 client로 부터의 (동시) 접속을 받아 처리 가능하도록 multi-thread 방식으로 설계하도록 한다.
[그림 3.6] AutoConnect 프로토콜(3) - Server Architecture(2)
또한, client로 부터의 이전 연결을 관리할 목적으로 peer table(hash map 기반)을 운용한다. 즉, client으로 부터 전달되는 구분자(예: mac address)를 기준으로 특정 client의 wireguard 설정 정보를 검색/추가/갱신/삭제하는 기능을 제공하도록 한다.
[그림 3.7] AutoConnect 프로토콜(3) - Server Architecture(3)
3.4) 구현한 코드 소개
지금까지 1차적으로 구현한 AutoConnect client & server를 Ubuntu 22.04에서 돌려 보면 다음과 같다.
$ sudo ./wg_autod -f ../config/server.conf
[그림 3.8] Server(wg_autod) 실행 모습
$ sudo ./wg_autoc -f 127.0.0.1 ../config/client.conf
[그림 3.9] Client(wg_autoc) 실행 모습
📌 Server가 구동되어 있는 상태에서 Client를 시작/중지(Ctrl-C)하는 시험을 반복한 결과, 이상 없이 동작된다.
지금까지 작성한 코드는 아래 위치에서 확인 가능하다. 아직은 초기 버젼이므로, 많은 문제가 내포되어 있을 수 있다. 😋
4. WireGuard AutoConnect 동작 시험과 남은 작업
이번 장에서는 2-3장에서 구현한 WireGuard AutoConnect 기능을 실제 target 장치(security gateway)에서 돌려 보는 과정을 소개하고자 한다.
4.1) 테스트베드
이번 장에서 사용할 시험 환경은 다음과 같다. (역시나) HOME에서 시험하는 것이기 때문에, 실제 vpn 시험 환경과는 다소 거리가 있을 수 밖에 없다. 😓
[그림 4.1] AutoConnect Testbed(1)
[그림 4.2] AutoConnect Testbed(2)
4.2) AutoConnect project build 하기
$ git clone https://github.com/ChunghanYi/wireguard-auto
$ vi ./build_arm64.sh
-> 먼저, 아래 예를 참조하여 자신이 사용 중인 toolchain path를 맞추어 주도록 한다.
YOUR_LOCAL_PATH=XXX
TOOLCHAIN_PATH=$XXX/friendlywrt23-rk3568/friendlywrt/staging_dir/toolchain-aarch64_generic_gcc-12.3.0_musl/bin
...
$ ./build_arm64.sh
$ cd build
$ ls -al
-rw-rw-r-- 1 chyi chyi 16081 1월 22 17:05 CMakeCache.txt
drwxrwxr-x 6 chyi chyi 4096 1월 22 17:05 CMakeFiles
-rw-rw-r-- 1 chyi chyi 15732 1월 22 17:05 Makefile
-rw-rw-r-- 1 chyi chyi 1817 1월 22 17:05 cmake_install.cmake
-rwxrwxr-x 1 chyi chyi 1973288 1월 22 17:05 wg_autoc
-rwxrwxr-x 1 chyi chyi 2129152 1월 22 17:05 wg_autod
$ file wg_autoc wg_autod
wg_autoc: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-aarch64.so.1, with debug_info, not stripped
wg_autod: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-aarch64.so.1, with debug_info, not stripped
이상의 2개 파일을 target 장치로 복사하여 테스트하면 된다. 하지만, 이 방법 보다는 qr-wireguard-ui package를 설치하는 아래 방법이 더 좋을 듯 하다.
📌 참고로, qr-wireguard-ui project build 시에 wireguard-auto source code도 함께 build하도록 작업해 두었다.
4.3) qr-wireguard-ui project build 하기
$ git clone https://github.com/ChunghanYi/qr-wireguard-ui
$ cd qr-wireguard-ui
$ vi ./build_qrwg.sh
-> toolchain path는 자신의 것으로 수정해 주어야 한다.
$ ./build_qrwg.sh release
...
$ cd output
$ ls -la
-rw-rw-r-- 1 chyi chyi 12816596 1월 22 16:50 qr_wireguard_0.9.02.tar.gz
-> 이전 posting의 내을 참조하여 target 장치에 설치하도록 한다.
(여기서는 별도로 설명하지는 않지만) qr-wireguard-ui 설치 파일이 제대로 동작하려면, 이전 posting 내용(4.2절)을 참조하여 wireguard kernel 교체 부분을 사전에 적절히 수정해 주어야 한다.
4.4) Target 장치에서 돌려 보기
아래와 같이 2개의 target 장치에 qr-wireguard-ui package를 설치하도록 하자.
a) NanoPi R5S에 서버 설치하기
<Ubuntu 22.04 LTS>
$ scp ./qr_wireguard_0.9.02.tar.gz root@192.168.2.1:~/workspace
$ ssh root@192.168.2.1
-> default password 값은 'password'이다.
<NanoPi R5S>
# cd /root/workspace
# tar xvzf qr_wireguard_0.9.02.tar.gz
# cd qr_install
# ./Install.sh
[그림 4.3] NanoPi R5S에 qr-wireguard-ui package 설치 모습
# cat /qrwg/config/publickey
smXaDzoYEhNfwNZmODBA80wlXJYOv4SD1TOmW91HUFE=
# vi /qrwg/config/server.conf
-> 아래와 같이 server.conf 파일을 적절히 수정한다.
[그림 4.4] NanoPi R5S wg_autod server configuration 파일 수정 모습
📌 제대로 완성된 경우라면, 위와 같이 수동으로 config 파일을 수정할 필요는 없어야 한다. 😓
# /usr/bin/qrwg/wg_autod -f /qrwg/config/server.conf
-> server를 구동시킨다.
[그림 4.5] NanoPi R5S wg_autod 구동 모습
📌 AutoConnect client/server가 정상 동작하기 위해서는 사전에 openWrt UI(LuCI)를 통해 tcp 51822 port를 열어주어야 한다.
b) MT2500에 Client 설치하기
<Ubuntu 22.04 LTS>
$ scp ./qr_wireguard_0.9.02.tar.gz root@192.168.7.1:~/workspace
$ ssh root@192.168.7.1
<MT2500>
# cd /root/workspace
# tar xvzf qr_wireguard_0.9.02.tar.gz
# cd qr_install
# ./Install.sh
[그림 4.6] MT2500에 qr-wireguard-ui package 설치 모습
# vi /qrwg/config/client.conf
-> 아래와 같이 client.conf 파일을 적절히 수정한다.
[그림 4.7] MT2500 wg_auto client configuration 파일 수정 모습
# /usr/bin/qrwg/wg_autoc -f 192.168.8.218 /qrwg/config/client.conf
-> client를 구동시킨다.
[그림 4.8] MT2500 wg_autoc 구동 모습
이상태에서 vtysh running-config 확인 후, peer vpn ip로 ping을 해 보면 정상적으로 동작함을 알 수 있다. 😎
[그림 4.9] NanoPi R5S vtysh show running-config 및 ping 명령 실행 모습
[그림 4.10] NanoPi R5S vtysh show wg 및 show ip route 명령 실행 모습
[그림 4.11] MT2500 vtysh show running-config 및 ping 명령 실행 모습
[그림 4.12] NanoPi R5S wireguard-ui 실행 모습 - wireguard 설정 상태 확인
________________________________________
이상으로 Modern C++를 이용하여 WireGuard AutoConnect 기능을 구현하는 과정을 간단히 소개해 보았다. 아직은 초기 개발 단계이므로, 해결해야 할 숙제가 많이 남아 있다(갈 길이 멀다). 😓
<TODO List>
1) 서버에서 Client의 VPN IP를 자동 할당해 주는 기능 구현
-> dhcp server 활용
2) 서버 기능 중, peer table 기능을 확장하여 Redis 등에 저장하는 기능 추가
-> server 재 구동 시 기존 client 정보 유지 차원
3) Client와 Server 간 message 통신 암호화 처리 및 인증 기능 추가
-> libsodium library 통합
4) Client, Server 모두 curve25519 keypair 자동 생성 및 추출 기능 추가
5) qr-wireguard-ui package와 통합
-> 부팅 시 autoconnect daemon이 자동 수행되도록 해 주어야 함.
6) 기타 안정화 작업 등
Good luck~
5. References
[1] https://www.wireguard.com/
[2] https://github.com/cedrickchee/awesome-wireguard
[3] https://www.friendlyelec.com/index.php?route=product/product&product_id=287
[4] https://wiki.friendlyelec.com/wiki/index.php/NanoPi_R5S
[5] https://www.gl-inet.com/products/gl-mt2500/
[6] https://github.com/GainStrongService/openwrt
[7] https://gainstrong.cn/
[8] https://github.com/elhayra/tcp_server_client
[9]Professional C++, 5th Edition, Marc Gregorie
[10] Do it! C++ 완전 정복, 조규남, 문종채, 이지스퍼블리싱
[11] Effective Modern C++, Scott Meyers
[12] 이것이 C++이다, 최호성, 한빛미디어
[13] And, Google~
StopBugs Company
ZepLin Company