이번 시간에는 (오랫만에 머리도 식힐겸) GoLang으로 구현한 wireguard-ui를 가지고 NanoPi 위에서 동작하는 Quantum Resistant WireGuard VPN Router를 만드는 과정을 소개해 보고자 한다. 😎
WebUI -> backend routines -> qr-wireguard kernel
목차
1. WireGuard WebUI 프로젝트
2. WireGuard WebUI Backend Architecture
3. WireGuard CLI Shell 프로젝트
4. QR-WireGuard(Quantum Resistant WireGuard) 돌려 보기
5. TODO
6. References
Network 보안 장비를 위한 management UI는 대부분 Web을 기반으로 한다. 이와 관련하여 국내 환경에서는 아직까지도 Java 기반의 Spring Boot 등을 많이 사용하고 있는 듯 보이지만, (보다 간결하면서도 최신 trend를 반영한) GoLang을 기반으로 개발해 보는 것도 나름의 묘미가 있을 것 같다. 자, 그렇다면 지금부터 Webserver(GoLang으로 구현)를 중심으로 frontend 부터 backend 서비스(역시 GoLang)를 거쳐, 최종적으로 Linux kernel에 이르기까지의 대장정(?)을 시작해 보도록 하자. 😋 이번 posting은 qr-l2-wireguard(kernel)를 소개한 이전 posting의 후속 글이다.
1. WireGuard WebUI 프로젝트
이번 장에서는 Khanh Ngo 님이 작성한 wireguard-ui라고 하는 Golang 기반의 wireguard 전용 webui project를 소개하고자 한다. wireguard-ui는 화면도 예쁘고, 동작 과정도 크게 문제가 없어, 적절히 개선할 경우 상용 버젼으로 사용하기에도 무리가 없어 보인다.
1.1) wireguard-ui 프로젝트 개요
wireguard-ui는 frontend 구현을 위해 Bootstrap Admin Dashboard Template인 AdminLTE 3.x를 사용하고, webserver 자체와 backend 처리를 위해 Echo라고 하는 Go web framework를 이용한다.
[그림 1.1] AdminLTE(bootstrap admin dashboard template) [출처 - https://adminlte.io/]
📌 bootstrap은 X(구 Twitter)에서 개발한 frontend용 toolkit이다.
[그림 1.2] echo web framework [출처 - https://echo.labstack.com/]
- Optimized HTTP router which smartly prioritize routes
- Build robust and scalable RESTful APIs
- Group APIs
- Extensible middleware framework
- Define middleware at root, group or route level
- Data binding for JSON, XML and form payload
- Handy functions to send variety of HTTP responses
- Centralized HTTP error handling
- Template rendering with any template engine
- Define your format for the logger
- Highly customizable
- Automatic TLS via Let’s Encrypt
- HTTP/2 support
📌 Go web framework으로는 Gin이 제일 유명한데, Echo도 많이 사용되는 것 같다. 👌
1.2) wireguard-ui 설치
wireguard-ui build 및 install 방법은 docker(docker compose 포함)를 이용한 방법과 그렇지 않은 방법이 있는데, 여기에서는 Ubuntu 22.04 환경에서 직접 build & 설치하는 방법을 살펴 보기로 한다.
📌 ARM64 환경(NanoPi)에서의 build 방법은 3장에서 다시 설명하도록 하자.
GOOS=linux GOARCH=arm64 go build -o wireguard-ui
먼저, 아래 shell script를 실행하도록 한다.
$ ./prepare_assets.sh
-> 이 shell script는 AdminLTE(js, css 등)를 설치하는 역할을 한다. AdminLTE가 실제로 설치되는 위치는 wireguard-ui/node_modules/admin-lte 이며, 이 내용 중 일부가 assets 디렉토리로 복사(by prepare_assets.sh)된다.
chyi@earth:~/wireguard/wireguard-ui/assets$ ls -la
합계 20
drwxrwxr-x 5 chyi chyi 4096 5월 22 2024 .
drwxrwxr-x 17 chyi chyi 4096 12월 24 15:11 ..
-rw-rw-r-- 1 chyi chyi 0 5월 11 2024 .gitkeep
drwxrwxr-x 4 chyi chyi 4096 5월 22 2024 custom
drwxrwxr-x 4 chyi chyi 4096 5월 22 2024 dist
drwxrwxr-x 10 chyi chyi 4096 5월 22 2024 plugins
chyi@earth:~/wireguard/wireguard-ui/assets/dist$ ls -la
합계 16
drwxrwxr-x 4 chyi chyi 4096 5월 22 2024 .
drwxrwxr-x 5 chyi chyi 4096 5월 22 2024 ..
drwxrwxr-x 2 chyi chyi 4096 5월 22 2024 css
drwxrwxr-x 2 chyi chyi 4096 12월 23 21:27 js
$ go build -o wireguard-ui
-> go code를 build한다.
$ ls -l wireguard-ui
-rwxrwxr-x 1 chyi chyi 23181241 12월 21 16:20 wireguard-ui
$ sudo ./wireguard-ui
-> go binary를 실행한다.
...
⇨ http server started on [::]:5000
[그림 1.4] wireguard-ui wireguard server 설정 화면
wireguard-ui project는 (자체적으로 생성하는 json 형식으로 자정된 db 파일을 제외하면) wireguard 설정 파일 즉, /etc/wireguard/wg0.conf 파일 만을 생성하거나 갱신한다. 다시 말해 wireguard tool인 wg 명령을 직접 실행하여 wireguard 설정을 곧 바로 수행하지는 않는다. 따라서, wg0.conf 파일이 갱신되었을 때, 이를 자동으로 반영하는 방법(mechanism)이 필요하다. 이를 위해 wireguard-ui는 systemd 혹은 openrc 등의 script를 활용하여, wg0.conf 파일이 변경되는 것을 monitoring하고 있다가, wg-quick 명령을 통해 실시간으로 wireguard 설정을 하는 방법을 사용한다. 아래 내용은 이중 Ubuntu 환경에서 사용하는 systemd 설정 방법의 한 예가 되겠다.
$ sudo vi /etc/systemd/system/wgui.service
[Unit]
Description=Restart WireGuard
After=network.target
[Service]
Type=oneshot
ExecStart=/usr/bin/systemctl restart wg-quick@wg0.service
[Install]
RequiredBy=wgui.path
~
$ sudo vi /etc/systemd/system/wgui.path
[Unit]
Description=Watch /etc/wireguard/wg0.conf for changes
[Path]
PathModified=/etc/wireguard/wg0.conf
[Install]
WantedBy=multi-user.target
~
$ systemctl status wgui.service
○ wgui.service - Restart WireGuard
Loaded: loaded (/etc/systemd/system/wgui.service; enabled; vendor preset: enabled)
Active: inactive (dead) since Mon 2024-12-23 14:42:44 KST; 11min ago
TriggeredBy: ● wgui.path
Process: 30905 ExecStart=/usr/bin/systemctl restart wg-quick@wg0.service (code=exited, status=0/SUCCESS)
Main PID: 30905 (code=exited, status=0/SUCCESS)
CPU: 6ms
12월 23 14:42:44 earth systemd[1]: Starting Restart WireGuard...
12월 23 14:42:44 earth systemd[1]: wgui.service: Deactivated successfully.
12월 23 14:42:44 earth systemd[1]: Finished Restart WireGuard.
이렇게 해 주고 나면, 신기하게도 wg0.conf 파일이 갱신되는 순간에, wireguard 설정이 자동으로 이루어지는 것을 알 수 있다. 💢
1.3) wireguard-ui의 구조
Go언어로 구현한 일반적인 web application의 구조는 아래 그림과 같다.
[그림 1.5] 일반적인 web application 구조#1 - webserver architecture [출처 - 참고문헌 8]
📌 Web application에서 말하는 routing(라우팅)이란, client로 부터 전달받은 URI(Resource Identifier)를 handler code에 mapping 시키는 과정을 일컫는다.
[그림 1.6] 일반적인 web application 구조#2 - web server와 db 관계도 [출처 - 참고문헌 8]
[그림 1.7] 일반적인 web application 구조#3 - template 엔진 [출처 - 참고문헌 8]
📌 Golang의 template engine은 handler로 부터 전달 받은 data(db에서 가져옴)를 이용하여 html을 생성하는 역할을 한다. 즉, template engine은 입력으로 전달 받은 html 파일 내의 {{ }} 부분을 필요한 action으로 채워 넣게 된다.
[그림 1.8] HTTP methods와 Restful API [출처 - 참고문헌 8]
wireguard-ui를 구성하는 Go 코드의 량은 실제로 그렇게 많지가 않으며, echo web framework의 동작 원리를 제대로 이해한다면, 쉽게 분석이 가능하다.
<Go code 모음>
$ find . -name "*.go" -print
-> Go 코드만 모아 보면 다음과 같다.
./store/jsondb/jsondb_wake_on_lan.go
./store/jsondb/jsondb.go
./store/store.go //json 형식의 파일 저장
./util/hash.go //여러가지 utility function 정의
./util/util.go
./util/cache.go
./util/config.go
./main.go //main routine => web server 시작
./emailer/smtp.go //emailer 관련 code
./emailer/sendgrid.go
./emailer/interface.go
./router/validator.go
./router/router.go //web service router package 코드(New 함수 신규 정의)
./handler/session.go
./handler/routes.go //web service route 함수에 대한 handler 정의
./handler/response.go
./handler/routes_wake_on_lan.go
./handler/middlewares.go
./model/client_defaults.go //model 디렉토리 => wireguard client, server, user 등에 대한 data structure 정의
./model/setting.go
./model/user.go
./model/client.go
./model/misc.go
./model/wake_on_lan_host.go
./model/server.go
./telegram/bot.go //telegram 관련 코드
_____________________________________________
<Go code 흐름 요약>
_____________________________________________
1) wireguard client, server, user 등 주요 data structure를 정의한다(modeling 과정).
-> model 디렉토리
2) wireguard-ui 동작 중 db에 저장되는 내용을 구현한다(database 정의 과정).
-> store 디렉토리
-> db라고 하지만, 실제로는 json 형식으로 된 파일이다. 이를 위해 https://github.com/sdomino/scribble 프로젝트가 활용된다.
-> 실제 json 파일 형태로 저장되는 내용은 db/ 디렉토리이다.
-> 예를 들어, interfaces.json은 다음과 같다.
chyi@earth:/mnt/hdd/workspace/wireguard/wireguard-ui/db/server$ sudo cat interfaces.json
[sudo] chyi 암호:
{
"addresses": [
"10.1.1.1/24"
],
"listen_port": "51820",
"updated_at": "2024-12-23T05:38:40.645096548Z",
"post_up": "",
"pre_down": "",
"post_down": ""
}
3) echo web framework을 기준으로 web server를 시작한다. 이때 다양한 restful API를 정의한다(웹서버 정의 과정)
-> router.New( ) 함수를 정의하고, GET, POST 같은 다양한 HTTP method를 정의한다.
4) 개별 기능 혹은 restful API에 대한 처리 루틴은 handler/ 디렉토리에서 구현한다(handler & template 구현 과정).
-> 응답으로 html을 던져주는 경우가 있는데, 그 내용은 templates/ 디렉토리 아래에 존재한다.
-> html에 대한 동적 rendering 처리도 한다.
chyi@earth:/mnt/hdd/workspace/wireguard/wireguard-ui/templates$ ls -la
합계 164
drwxrwxr-x 2 chyi chyi 4096 5월 15 2024 .
drwxrwxr-x 17 chyi chyi 4096 12월 24 15:26 ..
-rw-rw-r-- 1 chyi chyi 6139 5월 11 2024 about.html
-rw-rw-r-- 1 chyi chyi 31824 5월 11 2024 base.html
-rw-rw-r-- 1 chyi chyi 41680 5월 11 2024 clients.html
-rw-rw-r-- 1 chyi chyi 13551 5월 11 2024 global_settings.html
-rw-rw-r-- 1 chyi chyi 5375 5월 11 2024 login.html
-rw-rw-r-- 1 chyi chyi 4622 5월 11 2024 profile.html
-rw-rw-r-- 1 chyi chyi 11122 5월 11 2024 server.html
-rw-rw-r-- 1 chyi chyi 2493 5월 11 2024 status.html
-rw-rw-r-- 1 chyi chyi 11057 5월 11 2024 users_settings.html
-rw-rw-r-- 1 chyi chyi 5936 5월 11 2024 wake_on_lan_hosts.html
_____________________________________________
이상의 내용을 하나의 그림으로 정리해 보면 다음과 같다.
[그림 1.9] wireguard-ui의 대략적인 구성 [출처 - 참고문헌 8]
이번 posting의 목적은 wireguard-ui code를 개선하여, backend routine 및 wireguard kernel과 적절히 연동시키는 것이다. 따라서, handler/routes.go 등의 파일을 적절히 수정한다면, 2장에서 구현할 backend routine과의 원할한 통신이 이루어질 수 있을 것이다. 😀
wireguard-ui(plugin) -> web_agentd(backend go daemon) -> vtysh(CLI shell) -> kernel wireguard
<앞으로의 개선 point>
1) systemd나 openrc script를 사용하지 않고, backend routine을 통해 wireguard 설정을 실시간으로 진행하는 코드 추가
2) network interface, routing table 등의 신규 기능(front/backend) 추가
3) iptables 기반의 firewall/NAT 신규 기능(front/backend) 추가
4) 로그 기능(front/backend) 신규 추가 등
5) json db를 대체하는 db routine 추가(예: in memory Redis, RDBS SQlite3, PostgresQL 등)
6) Chart 기능 활용한 통계 기능 추가
7) HTTPS 화 작업(Let's encrypt 사용)
-> 혹은 nginx proxy 기능을 이용할 수도 있음.
...
2. WireGuard WebUI Backend Architecture
이번 장에서는 wireguard-ui를 제대로 사용하기 위해 (필자가) 새로 구현하고자 하는 webui backend architecture를 소개하고자 한다.
2.1) wireguard-ui backend interface 설계
1장에서 설명한 바와 같이, wireguard-ui는 외부 backend interface를 전혀 고려하고 있지 않다. 다시말해 /etc/wireguard/wg0.conf 파일만을 생성/갱신하는 것을 최종 목표로 한다. 따라서, 이를 확장하여 webui 뒷단의 다양한 루틴과 연동시키기 위해서는 기존의 구조를 (아래와 같이) 살짝 변경할 필요가 있다. 다행히도 wireguard-ui의 s/w 구조가 simple하면서도 구조적으로 잘 설계되어 있어 변경(확장) 작업이 수월할 듯 보인다. 😋
[그림 2.1] wireguard-ui와 backend routines overview
위의 그림에서 wireguard-ui와 backend daemon(web-agentd) 간에는 일정한 형태의 message를 전달하기 위한 통신 규약(protocol)이 필요하다.
(통신 규약을 구현하기 위해서) 처음에 든 생각은 wireguard 설정에 맞는 적당한 structure를 하나 정의하고, 그에 맞는 format으로 message를 전달해야겠다는 것이었으나, 향후 비 wireguard 설정 즉, network interface, routing table, firewall/NAT, 각종 system 명령, log 등을 지원하도록 기능을 확장하기 위해서라도 좀 더 일반적인 형태의 message format이 필요하겠다는 생각이 들었다. 따라서 특정 기능에 맞는 message format 보다는 아래와 같이 일반적인 내용을 담을 수 있는 형태로 설계를 진행하기로 하자. 🚀
REQUEST message format (wireguard-ui => backend routine)
cmd:=HELLO
subcmd:=wg_peer_add | wg_peer_remove | wg_interface_set | ...
field_count:=N
key1:=value1
key2:=value2
key3:=value3
...
REPLY message format (wireguard-ui <= backend routine)
[그림 2.2] wireguard-ui와 backend daemon(web_agentd) 간의 통신 protocol
📌 다소 시간이 지체되더라도, 생각한 바를 일일이 그림으로 그리고, 이를 바탕으로 개발을 진행하는 습관을 들이자. 이러한 행위 자체가 모두 개발 과정에 포함된다. 😋
2.2) wireguard-ui backend interface 구현
자, 그럼 이제 부터는 위의 내용을 토대로 아래와 같이 webui backend interface를 위한 plugin code를 아래와 같이 추가하도록 하자.
<wireguard-ui handlers/routes.go 파일 수정 예시>
import (
...
"github.com/ChunghanYi/qr-wireguard-ui/webui/beplugin"
)
...
이 코드를 handler/routes.go 파일 등의 적당한 위치에서 호출하면, web-agentd로 wireguard 설정 정보가 전달될 것이다.
준비가 되었으니, (wireguard-ui에 대해) module 초기화 및 전체 build를 다시 하도록 한다.
chyi@earth:~/wireguard/wireguard-ui$ rm go.mod go.sum
chyi@earth:~/wireguard/wireguard-ui$ go mod init github.com/ngoduykhanh/wireguard-ui
chyi@earth:~/wireguard/wireguard-ui$ go mod tidy
chyi@earth:~/wireguard/wireguard-ui$ go build -o wireguard-ui
Build가 성공하였으니, 이번에는 wireguard-ui를 돌려 볼 차례이다.
chyi@earth:~/wireguard/wireguard-ui$ sudo ./wireguard-ui
...
이 상태에서 아래와 같이 backend daemon을 실행한 후, UI 화면에서 필요한 동작(예: wireguard server 설정, client 추가 등)을 취해 본다. 정상적으로 동작할 경우, 아래와 같은 log가 화면에 출력되어야 한다.
<backend daemon 시험>
root@nanopi:~/workspace/qr# /usr/bin/qrwg/web-agentd -f
>>> cmd:=HELLO message received.
>>> subcmd:=ADD_WIREGUARD_PEER
>>> field_count:=3
>>> key0:=ddd4q6TpkI9vW9zz0mCF72ZD6CbIlc9EczAOhMHhVgk=
>>> key1:=172.16.1.0/24,10.1.1.0/24,192.168.0.0/16
>>> key2:=192.168.8.205:51820
2025/01/03 06:30:48 <<< OK message sent.
OK, 지금까지 1차적으로 webUI를 위한 backend interface를 설계하고, 이를 wireguard-ui와 연동시켜 보았다.
wireguard-ui(plugin) -> web-agentd(backend go routine)
그런데, plugin routine과 backend go code를 통해 wireguard kernel 까지 wireguard 설정 정보가 제대로 전달되는지를 확인하기 위해서는 다음 장에서 설명할 wireguard CLI shell이 준비되어 있어야 한다. 다음 step으로 go go~ 😗
3. WireGuard CLI Shell 프로젝트
Wireguard CLI(Command Line Interface) shell은 webUI가 없는 상태에서도 명령행에서 wireguard 설정(기타 시스템 설정도 가능)이 가능하도록 하기 위해 (필자에 의해) 개발 중인 project로, 아래 2개의 project에서 제공하는 vtysh(virtual terminal interface shell)에서 영감을 받아 개발을 시작하게 되었다. 😎
참고로, 국내외의 router/switch 제조사에서는 (꼭 그런 것은 아니겠지만) IP Infusion사에 개발한 ZebOS(최근에는 OcNOS CP라고 부름)를 많이 사용하는데, 이 녀석(?)이 vtysh과 외관상 많이 닮아 있다. vtysh은 cisco CLI와 유사한 형태로 동작하므로, CLI router/switch에 익숙한 독자들은 무슨 얘기인데 금방 알 수 있을 것이다. 😋
📌 NanoPi의 경우는 LAN과 WAN 2개의 port로만 구성되어 있지만, vtysh을 잘 활용할 경우, 복수개의 port로 구성된 router/swith를 만드는데 활용할 수도 있다.
위의 그림에서도 알 수 있듯이, (필자가 개발 중인) wg CLI shell(vtysh)은 크게 4개의 파트로 구성된다.
- network interface 설정 및 routing table 관리 기능
- iptables 기반의 packet filtering, NAT 설정 기능
- wireguard 기반의 VPN 설정 기능
- 기타 시스템 설정 기능
또한 wg CLI shell(vtysh)은 아래와 같이 3개의 동작 모드로 구성되어 있다.
View mode <=> Enable mode <=> Configuration mode
📌 가장 핵심적인 설정은 configuration mode에서 이루어진다. mode간 전환 방법은 (진입 시) enable 명령과 (탈출 시) exit 명령을 사용해 이루어진다.
wg CLI shell(vtysh)이 실제로 동작하는 모습은 다음과 같다.
_________________________________________________________________________________
nanopi-r2s-plus login: root
Password:
___ _ _ _ __ __ _
| __| _(_)___ _ _ __| | |_ \ \ / / _| |_
| _| '_| / -_) ' \/ _` | | || \ \/\/ / '_| _|
|_||_| |_\___|_||_\__,_|_|\_, |\_/\_/|_| \__|
|__/
-----------------------------------------------------
FriendlyWrt 23.05.3, r23809-234f1a2efa
-----------------------------------------------------
Build On Apr 14 2024 15:28:02
Password:
___ _ _ _ __ __ _
| __| _(_)___ _ _ __| | |_ \ \ / / _| |_
| _| '_| / -_) ' \/ _` | | || \ \/\/ / '_| _|
|_||_| |_\___|_||_\__,_|_|\_, |\_/\_/|_| \__|
|__/
-----------------------------------------------------
FriendlyWrt 23.05.3, r23809-234f1a2efa
-----------------------------------------------------
Build On Apr 14 2024 15:28:02
nanopi-r2s-plus> ?
enable Turn on privileged mode command
exit Exit current mode and down to previous mode
ping Send echo messages
show Show running system information
ssh Open a ssh connection
nanobox-r2s-plus>
enable Turn on privileged mode command
exit Exit current mode and down to previous mode
ping Send echo messages
show Show running system information
ssh Open a ssh connection
nanobox-r2s-plus>
nanobox-r2s-plus> en
nanobox-r2s-plus# ?
configure Configuration from vty interface
disable Turn off privileged mode command
exit Exit current mode and down to previous mode
list Print command list
netstat show netstat
ping Send echo messages
show Show running system information
ssh Open a ssh connection
tcpdump Tcpdump packet
write Write running configuration to memory, network, or terminal
nanobox-r2s-plus# ?
configure Configuration from vty interface
disable Turn off privileged mode command
exit Exit current mode and down to previous mode
list Print command list
netstat show netstat
ping Send echo messages
show Show running system information
ssh Open a ssh connection
tcpdump Tcpdump packet
write Write running configuration to memory, network, or terminal
nanopi-r2s-plus#
nanopi-r2s-plus# configure terminal
nanopi-r2s-plus(config)# ?
bridge Specify whether bridge mode is enabled or disabled
end End a cli
exit Exit current mode and down to previous mode
hostname Set system's network name
ip IP information set
nameserver Config the dns server
no Negate a command or set its defaults
ping Send echo messages
poweroff Power off the system
reboot Reboot the system
show Show running system information
ssh Open a ssh connection
wg Configure WireGuard rules
write Write running configuration to memory, network, or terminal
nanopi-r2s-plus(config)#
nanopi-r2s-plus# configure terminal
nanopi-r2s-plus(config)# ?
bridge Specify whether bridge mode is enabled or disabled
end End a cli
exit Exit current mode and down to previous mode
hostname Set system's network name
ip IP information set
nameserver Config the dns server
no Negate a command or set its defaults
ping Send echo messages
poweroff Power off the system
reboot Reboot the system
show Show running system information
ssh Open a ssh connection
wg Configure WireGuard rules
write Write running configuration to memory, network, or terminal
nanopi-r2s-plus(config)#
nanopi-r2s-plus(config)# wg ?
clear-key Remove curve25519 private & public keys including searchkey
listenport Listening port
peer Specify peer information
regenerate-key Regenerate private & public keys
clear-key Remove curve25519 private & public keys including searchkey
listenport Listening port
peer Specify peer information
regenerate-key Regenerate private & public keys
nanopi-r2s-plus(config)# show ?
date Displays the current date
ip IP information set
running-config running configuration
sfirewall show smart firewall rules
startup-config Contentes of startup configuration
version Displays the version information
wg Show the wireguard tunnel info
date Displays the current date
ip IP information set
running-config running configuration
sfirewall show smart firewall rules
startup-config Contentes of startup configuration
version Displays the version information
wg Show the wireguard tunnel info
nanopi-r2s-plus(config)# exit
nanopi-r2s-plus#
nanopi-r2s-plus# exit
nanopi-r2s-plus>
nanopi-r2s-plus>
nanopi-r2s-plus#
nanopi-r2s-plus# exit
nanopi-r2s-plus>
nanopi-r2s-plus>
_________________________________________________________________________________
📌 wg CLI shell에 상에서 신규로 추가한 명령을 삭제하기 위해서는 (cisco CLI 처럼) 맨 앞에 no를 추가해 주면 된다.
<wireguard CLI 명령>
wg listenport PORT
-> wireguard port 설정 명령
wg peer PUBLICKEY allowed-ips WORD endpoint A.B.C.D:PORT persistent-keepalive NUM
-> wireguard peer(client) 설정 명령
no wg peer PUBLIC KEY
-> wireguard peer 삭제 명령
wg regenerate-key
-> curve25519 keypair 재생성 명령
show wg
-> wireguard 상태 확인 명령
📌 위의 명령과 wireguard-ui에서 전달되는 내용을 web-agentd를 통해서 일치시켜 주어야 한다.
[그림 3.3] 완성된 wg CLI shell(= vtysh) wireguard command
자, wireguard CLI shell이 준비 되었으니, wireguard-ui <=> web-agentd <=> vtysh을 연동시켜 보도록 하자.
<wireguard-ui <=> web-agentd <=> vtysh 시험>
root@nanopi:~/workspace/qr# /usr/bin/qrwg/web-agentd -f
>>> cmd:=HELLO message received.
>>> subcmd:=ADD_WIREGUARD_PEER
>>> field_count:=3
>>> key0:=ddd4q6TpkI9vW9zz0mCF72ZD6CbIlc9EczAOhMHhVgk=
>>> key1:=172.16.1.0/24,10.1.1.0/24,192.168.0.0/16
>>> key2:=192.168.8.205:51820
Build On Jan 2 2025 16:15:52
>>> scmd = [wg peer ddd4q6TpkI9vW9zz0mCF72ZD6CbIlc9EczAOhMHhVgk= allowed-ips 172.16.1.0/24,10.1.1.0/24,192.168.0.0/16 endpoint 192.168.8.2]
Build On Jan 2 2025 16:15:52
Configuration saved SUCCESS
2025/01/03 06:30:48 <<< OK message sent.
OK, wireguard-ui 설정 버튼을 누를 경우, wireguard 설정 정보가 web-agentd를 거쳐 vtysh로 잘 전달되는 것이 보인다.
📌 Wireguard CLI shell 사용과 관련해서는 4장에서 좀 더 확인해 보도록 하자.
-------------------
지금까지 2, 3장에서 wireguard-ui backend 처리를 위해 구현한 코드(beplugin, web-agentd, vtysh, build script 등)는 아래 github에서 확인 가능하다. 당연히 아직은 초기 개발 단계인 만큼, 처리해야 할 내용이 많이 남아 있다. 😓
<디렉토리 구성>
chyi@earth:~/wireguard/qr-wireguard-ui$ ls -la
-rwxrwxr-x 1 chyi chyi 6064 1월 1 21:24 build_qrwg.sh
-> 전체 build script
drwxrwxr-x 4 chyi chyi 4096 1월 1 18:08 wgshell
-> wireguard CLI shell(vtysh) source code
drwxrwxr-x 5 chyi chyi 4096 1월 1 21:25 rootfs
-> 설치용 tar.gz 파일을 구성하기 위한 기초 파일(booting script, 설치 folder 등) 모음
drwxrwxr-x 4 chyi chyi 4096 1월 1 21:25 webagent
-> web-agentd source code
drwxrwxr-x 4 chyi chyi 4096 12월 31 13:47 webui
-> web-agentd와의 interface 관련 수정 사항을 포함한 wireguard-ui source code
4. QR-WireGuard(Quantum Resistant WireGuard) 돌려 보기
이번 장에서는 2대의 NanoPi 보드(FriendlyElec NanoPi R2S Plus, NanoPi R4S)를 이용하여 1~3장에서 구현한 webUI 환경에서도 QR-WireGuard가 제대로 동작하는지를 시험하고자 한다.
4.1) Quantum-Resistant WireGuard 개요
필자는 이미 이전 blog posting을 통해서 original wireguard code를 수정하여, quantum resistant wireguard code로 변형하는 과정을 소개한 바 있다.
<#1 kernel wireguard>
<#2 wireguard-go>
<여기서 잠깐!>
NanoPi R2S Plus 및 R4S 개발 환경과 관련해서는 위의 link를 통해 이미 소개한 바가 있다. 하지만, R4S의 개발 환경을 오래 전에 구축한 탓에 현재 사용 중인 PC에 관련 내용이 없다. 따라서 빠른 속도로 개발 환경을 다시 구축해 보기로 한다.
<Ubuntu 22.04>
$ mkdir friendlywrt21-rk3399
$ cd friendlywrt21-rk3399
$ git clone https://github.com/friendlyarm/repo --depth 1 tools
$ tools/repo init -u https://github.com/friendlyarm/friendlywrt_manifests -b master-v21.02 -m rk3399.xml --repo-url=https://github.com/friendlyarm/repo --no-clone-bundle
warning: Python 3 support is currently experimental. YMMV.
Please use Python 2.7 instead.
Traceback (most recent call last):
File "/mnt/hdd/workspace/mini_devices/friendlywrt23-rk3399/tools/repo", line 917, in <module>
main(sys.argv[1:])
File "/mnt/hdd/workspace/mini_devices/friendlywrt23-rk3399/tools/repo", line 879, in main
_Init(args, gitc_init=(cmd == 'gitc-init'))
File "/mnt/hdd/workspace/mini_devices/friendlywrt23-rk3399/tools/repo", line 323, in _Init
if branch.startswith('refs/heads/'):
TypeError: startswith first arg must be bytes or a tuple of bytes, not str
-> python3 대신 2.x 버젼을 사용한단다. 따라서, 아래와 같이 python 2.7을 사용하도록 조정해 주도록 한다.
$ sudo update-alternatives --config python
[sudo] chyi 암호:
대체 항목 python에 대해 (/usr/bin/python 제공) 2개 선택이 있습니다.
선택 경로 우선순� 상태
------------------------------------------------------------
0 /usr/bin/python3.10 2 자동 모드
* 1 /usr/bin/python2.7 1 수동 모드
2 /usr/bin/python3.10 2 수동 모드
현재 선택[*]을 유지하려면 <엔터>를 누르고, 아니면 선택 번호를 입력하시오: <Enter>
$ tools/repo init -u https://github.com/friendlyarm/friendlywrt_manifests -b master-v21.02 -m rk3399.xml --repo-url=https://github.com/friendlyarm/repo --no-clone-bundle
$ tools/repo sync -c --no-clone-bundle
$ ./build.sh rk3399.mk
-> full build를 시도해 보자.
$ ./build.sh kernel
-> kernel만 build해 본다.
_____________________________________________
4.2) Quantum-Resistant WireGuard Kernel 준비하기
지금 부터는 Quantum Resistant WireGuard Kernel을 build해 보기로 하겠다. 1-3장에서 소개한 project에는 wireguard kernel을 제외한 wireguard-ui(open source project) 코드와 backend routine(web-agentd, vtysh)만이 포함되어 있다. 따라서 wireguard kernel은 별도로 build해 주어야 한다. (반복되는 내용인 관계로) 자세한 사항은 이전 posting의 5장 내용을 참조해 주기 바란다.
(내용 생략을 하긴 했지만) 모든 작업이 진행된 후에는 wireguard.ko 파일을 아래 위치에 복사해 주도록 하자.
📌 wireguard kernel도 함께 build하도록 할 수도 있겠으나, 일단은 qr-l2-wireguard project와 qr-wireguard-ui project를 별도로 분리하기로 한다.
<quantum resistant layer 2 wireguard kernel project>
<original wireguard.ko binary 교체>
$ cp drivers/net/wireguard/wireguard.ko $YOUR_PATH/qr-wireguard-ui/rootfs/kernel/wireguard_orig.ko
<quantum resistant wireguard 작업 후, wireguard.ko 교체>
$ cp drivers/net/wireguard/wireguard.ko $YOUR_PATH/qr-wireguard-ui/rootfs/kernel/wireguard_qr.ko
4.3) 전체 build 및 target board에 설치하기
OpenWrt 환경을 고려한다면, 원칙적으로는 openwrt 환경에서 build 가능한 방식이 필요하겠지만, 여기에서는 qr-wireguard 만을 대상으로 자체 build가 가능한 방법을 소개하고자 한다.
<전체 source code build 하기>
$ git clone https://github.com/ChunghanYi/qr-wireguard-ui
$ cd qr-wireguard-ui
$ ./build_qrwg.sh
Usage: ./build_qrwg.sh release|clean
$ ./build_qrwg.sh release
-> 전체 build 과정에 약간의 시간이 소모된다.
$ cd output
-> 결과 파일을 확인하도록 하자.
$ ls -l
-rw-rw-r-- 1 chyi chyi 15683309 1월 1 17:10 qr_wireguard_0.9.00.tar.gz
<tar.gz 파일을 target board에 복사 후 설치하기>
$ scp ./qr_wireguard_0.9.00.tar.gz root@192.168.2.1:~/workspace/qr
-> /root/workspace/qr 디렉토리를 사전에 만들어 두었다고 가정한다.
$ ssh root@192.168.2.1
$ cd /root/workspace/qr
$ tar xvzf ./qr_wireguard_0.9.00.tar.gz
$ cd qr_install
$ ./Install.sh
-> 정상적으로 설치가 진행될 경우, 시스템 재부팅으로 이어진다.
📌 한번 설치가 된 상태에서 다시 설치하고자 할 경우에는 반드시 Uninstall.sh 명령을 실행해 주어야 한다.
<재부팅 후 다시 로긴>
nanopi login: root
Password:
-> login시 자동으로 wireguard CLI shell(vtysh)이 구동된다.
nanopi> en
nanopi# configure terminal
nanopi(config)# ?
[그림 4.3] 부팅 후, wgshell 실행 모습
📌 참고로 CLI 설정을 변경한 후에는, 반드시 write 명령을 실행하여 설정한 내용을 저장해 주어야 한다.
이 상태에서 몇가지 주요 명령을 실행해 보면 다음과 같다.
nanopi(config)# show running-config
nanopi(config)# show ip config
nanopi(config)# show ip route
nanopi(config)# exit
nanopi# show sysinfo
📌 web-agentd와 wireguard-ui가 동작 중인 것이 보인다.
CLI는 이상 없이 동작하는 것 같다. 그럼 webUI는 어떨까 ?
http://192.168.2.1:5000
📌 PC에서 실행할 때보다, login page에서 다음 page로 넘어가는데 시간이 좀 더 걸린다.
4.4) QR-WireGuard 동작 시험하기
이제 마지막으로 (지금까지 만든 코드를 기반으로) qr-wireguard가 제대로 동작하는지 시험할 차례이다.
WebUI -> backend routines -> wireguard CLI shell -> wireguard kernel(QR-WireGuard)
이를 확인할 네트워크 구성은 다음과 같다.
[그림 4.10] 2대의 NanoPi를 이용한 QR-WireGuard 테스트베드(1)
(vpn ip: 172.16.1.200 <-> 172.16.1.100)
📌 HOME에서 테스트베드를 꾸민 관계로, 실제 vpn 환경과는 다소 거리가 있다. 감안해서 봐 주셔야 한다. 😂
<NanoPi R2S Plus 설정하기>
먼저, 아래와 같이 R2S Plus 장치의 wireguard-ui 설정(server 및 client)을 진행한다.
[그림 4.13] Client page 설정 모습 - wireguard peer 추가
위의 2개의 web page가 정상적으로 동작할 경우, 아래와 같이 wireguard CLI shell을 통해 새로운 wireguard 설정이 자동 추가되는 것을 확인할 수 있다.
[그림 4.14] wireguard web page 설정 후, CLI 확인하기(1)
(아래 R4S 설정을 마친 후에) 이 상태에서 peer vpn ip로 ping을 시도해 보니, 정상 동작한다. 😀
<NanoPi R4S 설정하기>
이번에는 R4S 장치 설정 차례이다.
[그림 4.16] Server page 설정 모습 - wireguard interface 설정
[그림 4.17] Client page 설정 모습 - wireguard peer 추가
역시, 위의 2개의 web page가 정상적으로 동작할 경우, 아래와 같이 wireguard CLI shell을 통해 새로운 wireguard 설정이 자동 추가되는 것을 확인할 수 있다.
OK, 지금까지 wireguard-ui 프로젝트를 소개한 후, backend routine인 web-agentd와 wireguard CLI shell을 구현한 후, 이들을 상호 연결하여 qr-wireguard(linux kernel module)와 연동시키는 과정을 (가볍게) 소개해 보았다. 😎
5. TODO
(다시 정리해 보면) 이번 posting에서는 (아주 훌륭한) open source project인 wireguard-ui를 기초로 하여, 뒷단의 backend 서비스 루틴(web-agentd, vtysh)을 만들고, 이를 qr-wireguard kernel과 연결하는 아주 기본적인 작업을 진행해 보았다. 따라서 앞으로는 (시간이 허락한다면) 아래와 같이 좀 더 실질적인 기능 확장 및 안정화 작업이 이루어질 수 있도록 노력해 볼 생각이다. 😎
1) /etc/wireguard/wg0.conf 관련 코드 제거하기
2) 각종 설정 관련하여 좀 더 상세하게 control 하기
-> 현재는 wireguard server 및 client 기초 설정 부분만 연동하는 상태임.
3) Network interface, routing table 관련 UI 화면 추가하기(frontend, backend)
4) System 설정 UI 화면 추가하기(frontend, backend)
5) 안정화 작업 등
To be continued...
6. References
<wireguard-ui project>
[1] https://github.com/ngoduykhanh/wireguard-ui
[2] https://insights.ditatompel.com/en/tutorials/installing-wireguard-ui-to-manage-your-wireguard-vpn-server/
[3] https://echo.labstack.com/docs/quick-start
[4] https://adminlte.io/
[5] https://adminlte.io/themes/v3/
[6] https://getbootstrap.com/
[7] https://github.com/sdomino/scribble
<backend and vtysh>
[8] Go Web Programming, Sau Sheong Chang, Manning Publications Co., 2016
[9] https://github.com/Quagga/quagga
[10] https://github.com/FRRouting/frr
[11] And, Google~
Slowboot
StopBugs Company
ZepLin Company