feat: Add WireGuard client
- Add a WireGuard client to the project. - New utility functions have been added to parse the output of `wg show` command and improve error handling. - Add start, down, show, show_config, generate_private_key, and get_public_key method to interact with the wg binary. - Created a new example file to offer more clear usage. Co-authored-by: mariobassem12 <mariobassem12@gmail.com>
This commit is contained in:
34
examples/develop/wireguard/wireguard.vsh
Executable file
34
examples/develop/wireguard/wireguard.vsh
Executable file
@@ -0,0 +1,34 @@
|
|||||||
|
#!/usr/bin/env -S v -n -w -gc none -no-retry-compilation -d use_openssl -enable-globals run
|
||||||
|
|
||||||
|
import freeflowuniverse.herolib.clients.wireguard
|
||||||
|
import time
|
||||||
|
|
||||||
|
println('HIIII')
|
||||||
|
|
||||||
|
// Create Wireguard client
|
||||||
|
mut wg := wireguard.get()!
|
||||||
|
println('Hello')
|
||||||
|
config_file_path := '~/wg1.conf'
|
||||||
|
|
||||||
|
println('Before start')
|
||||||
|
wg.start(config_file_path: config_file_path)!
|
||||||
|
println('${config_file_path} is started')
|
||||||
|
|
||||||
|
time.sleep(time.second * 2)
|
||||||
|
|
||||||
|
info := wg.show()!
|
||||||
|
println('info: ${info}')
|
||||||
|
|
||||||
|
config := wg.show_config(interface_name: 'wg1')!
|
||||||
|
println('config: ${config}')
|
||||||
|
|
||||||
|
private_key := wg.generate_private_key()!
|
||||||
|
println('private_key: ${private_key}')
|
||||||
|
|
||||||
|
public_key := wg.get_public_key(private_key: private_key)!
|
||||||
|
println('public_key: ${public_key}')
|
||||||
|
|
||||||
|
time.sleep(time.second * 2)
|
||||||
|
|
||||||
|
wg.down(config_file_path: config_file_path)!
|
||||||
|
println('${config_file_path} is down')
|
||||||
@@ -1,16 +1,115 @@
|
|||||||
module wireguard
|
module wireguard
|
||||||
|
|
||||||
fn (wg WireGuard) show() {
|
import os
|
||||||
|
|
||||||
|
pub struct WGPeer {
|
||||||
|
pub mut:
|
||||||
|
endpoint string
|
||||||
|
allowed_ips string
|
||||||
|
latest_handshake string
|
||||||
|
transfer string
|
||||||
|
persistent_keepalive string
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (wg WireGuard) up() {
|
pub struct WGInterface {
|
||||||
|
pub mut:
|
||||||
|
name string
|
||||||
|
public_key string
|
||||||
|
listening_port int
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (wg WireGuard) down() {
|
pub struct WGInfo {
|
||||||
|
pub mut:
|
||||||
|
interface_ WGInterface
|
||||||
|
peers map[string]WGPeer
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (wg WireGuard) generate_key() !string {
|
pub struct WGShow {
|
||||||
|
pub mut:
|
||||||
|
configs map[string]WGInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (wg WireGuard) get_public_key() {
|
pub fn (wg WireGuard) show() !WGShow {
|
||||||
|
cmd := 'wg show'
|
||||||
|
res := os.execute(cmd)
|
||||||
|
if res.exit_code != 0 {
|
||||||
|
return error('failed to execute show command due to: ${res.output}')
|
||||||
|
}
|
||||||
|
|
||||||
|
return wg.parse_show_command_output(res.output)
|
||||||
|
}
|
||||||
|
|
||||||
|
@[params]
|
||||||
|
pub struct ShowConfigArgs {
|
||||||
|
pub:
|
||||||
|
interface_name string @[required]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (wg WireGuard) show_config(args ShowConfigArgs) !WGInfo {
|
||||||
|
configs := wg.show()!.configs
|
||||||
|
config := configs[args.interface_name] or {
|
||||||
|
return error('key ${args.interface_name} does not exists.')
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
@[params]
|
||||||
|
pub struct StartArgs {
|
||||||
|
pub:
|
||||||
|
config_file_path string @[required]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (wg WireGuard) start(args StartArgs) ! {
|
||||||
|
if os.exists(args.config_file_path) {
|
||||||
|
return error('File ${args.config_file_path} does not exists.')
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := 'sudo wg-quick up ${args.config_file_path}'
|
||||||
|
println('cmd: ${cmd}')
|
||||||
|
res := os.execute(cmd)
|
||||||
|
if res.exit_code != 0 {
|
||||||
|
return error('failed to execute start command due to: ${res.output}')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@[params]
|
||||||
|
pub struct DownArgs {
|
||||||
|
pub:
|
||||||
|
config_file_path string @[required]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (wg WireGuard) down(args DownArgs) ! {
|
||||||
|
if os.exists(args.config_file_path) {
|
||||||
|
return error('File ${args.config_file_path} does not exists.')
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := 'sudo wg-quick down ${args.config_file_path}'
|
||||||
|
res := os.execute(cmd)
|
||||||
|
if res.exit_code != 0 {
|
||||||
|
return error('failed to execute down command due to: ${res.output}')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (wg WireGuard) generate_private_key() !string {
|
||||||
|
cmd := 'wg genkey'
|
||||||
|
res := os.execute(cmd)
|
||||||
|
if res.exit_code != 0 {
|
||||||
|
return error('failed to execute genkey command due to: ${res.output}')
|
||||||
|
}
|
||||||
|
return res.output.trim_space()
|
||||||
|
}
|
||||||
|
|
||||||
|
@[params]
|
||||||
|
pub struct GetPublicKeyArgs {
|
||||||
|
pub:
|
||||||
|
private_key string @[required]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (wg WireGuard) get_public_key(args GetPublicKeyArgs) !string {
|
||||||
|
cmd := 'echo ${args.private_key} | wg pubkey'
|
||||||
|
res := os.execute(cmd)
|
||||||
|
if res.exit_code != 0 {
|
||||||
|
return error('failed to execute pubkey command due to: ${res.output}')
|
||||||
|
}
|
||||||
|
return res.output.trim_space()
|
||||||
}
|
}
|
||||||
|
|||||||
72
lib/clients/wireguard/utils.v
Normal file
72
lib/clients/wireguard/utils.v
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
module wireguard
|
||||||
|
|
||||||
|
fn (wg WireGuard) parse_show_command_output(res string) !WGShow {
|
||||||
|
mut configs := map[string]WGInfo{}
|
||||||
|
mut lines := res.split('\n')
|
||||||
|
mut current_interface := ''
|
||||||
|
mut current_peers := map[string]WGPeer{}
|
||||||
|
mut iface := WGInterface{}
|
||||||
|
mut peer_key := ''
|
||||||
|
|
||||||
|
for line in lines {
|
||||||
|
mut parts := line.trim_space().split(': ')
|
||||||
|
if parts.len < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
key := parts[0]
|
||||||
|
value := parts[1]
|
||||||
|
|
||||||
|
if key.starts_with('interface') {
|
||||||
|
if current_interface != '' {
|
||||||
|
configs[current_interface] = WGInfo{
|
||||||
|
interface_: iface
|
||||||
|
peers: current_peers.clone()
|
||||||
|
}
|
||||||
|
current_peers.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
current_interface = value
|
||||||
|
iface = WGInterface{
|
||||||
|
name: current_interface
|
||||||
|
public_key: ''
|
||||||
|
listening_port: 0
|
||||||
|
}
|
||||||
|
} else if key == 'public key' {
|
||||||
|
iface.public_key = value
|
||||||
|
} else if key == 'listening port' {
|
||||||
|
iface.listening_port = value.int()
|
||||||
|
} else if key.starts_with('peer') {
|
||||||
|
peer_key = value
|
||||||
|
mut peer := WGPeer{
|
||||||
|
endpoint: ''
|
||||||
|
allowed_ips: ''
|
||||||
|
latest_handshake: ''
|
||||||
|
transfer: ''
|
||||||
|
persistent_keepalive: ''
|
||||||
|
}
|
||||||
|
current_peers[peer_key] = peer
|
||||||
|
} else if key == 'endpoint' {
|
||||||
|
current_peers[peer_key].endpoint = value
|
||||||
|
} else if key == 'allowed ips' {
|
||||||
|
current_peers[peer_key].allowed_ips = value
|
||||||
|
} else if key == 'latest handshake' {
|
||||||
|
current_peers[peer_key].latest_handshake = value
|
||||||
|
} else if key == 'transfer' {
|
||||||
|
current_peers[peer_key].transfer = value
|
||||||
|
} else if key == 'persistent keepalive' {
|
||||||
|
current_peers[peer_key].persistent_keepalive = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if current_interface != '' {
|
||||||
|
configs[current_interface] = WGInfo{
|
||||||
|
interface_: iface
|
||||||
|
peers: current_peers.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return WGShow{
|
||||||
|
configs: configs
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,22 +22,29 @@ fn args_get(args_ ArgsGet) ArgsGet {
|
|||||||
args.name = wireguard_default
|
args.name = wireguard_default
|
||||||
}
|
}
|
||||||
if args.name == '' {
|
if args.name == '' {
|
||||||
args.name = 'default'
|
args.name = 'wireguard'
|
||||||
}
|
}
|
||||||
return args
|
return args
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(args_ ArgsGet) !&WireGuard {
|
pub fn get(args_ ArgsGet) !&WireGuard {
|
||||||
|
println('Before the args get')
|
||||||
mut args := args_get(args_)
|
mut args := args_get(args_)
|
||||||
|
println('Before the bigger if')
|
||||||
if args.name !in wireguard_global {
|
if args.name !in wireguard_global {
|
||||||
if args.name == 'default' {
|
println('Before the if connd')
|
||||||
|
if args.name == 'wireguard' {
|
||||||
|
println('Before saving')
|
||||||
if !config_exists(args) {
|
if !config_exists(args) {
|
||||||
if default {
|
if default {
|
||||||
|
println('When saving')
|
||||||
config_save(args)!
|
config_save(args)!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
println('When loading')
|
||||||
config_load(args)!
|
config_load(args)!
|
||||||
}
|
}
|
||||||
|
println('After all')
|
||||||
}
|
}
|
||||||
return wireguard_global[args.name] or {
|
return wireguard_global[args.name] or {
|
||||||
println(wireguard_global)
|
println(wireguard_global)
|
||||||
|
|||||||
@@ -20,13 +20,13 @@ pub fn heroscript_default() !string {
|
|||||||
@[heap]
|
@[heap]
|
||||||
pub struct WireGuard {
|
pub struct WireGuard {
|
||||||
pub mut:
|
pub mut:
|
||||||
name string = 'default'
|
name string = 'wireguard'
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cfg_play(p paramsparser.Params) ! {
|
fn cfg_play(p paramsparser.Params) ! {
|
||||||
// THIS IS EXAMPLE CODE AND NEEDS TO BE CHANGED IN LINE WITH struct above
|
// THIS IS EXAMPLE CODE AND NEEDS TO BE CHANGED IN LINE WITH struct above
|
||||||
mut mycfg := WireGuard{
|
mut mycfg := WireGuard{
|
||||||
name: p.get_default('name', 'default')!
|
name: p.get_default('name', 'wireguard')!
|
||||||
}
|
}
|
||||||
set(mycfg)!
|
set(mycfg)!
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user