Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SAL for wireguard #44

Merged
merged 4 commits into from
Feb 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions examples/develop/wireguard/wg0.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[Interface]
Address = 10.10.3.0/24
PrivateKey = wDewSiri8jlaGnUDN6SwK7QhN082U7gfX27YMGILvVA=
[Peer]
PublicKey = 2JEGJQ8FbajdFk0fFs/881H/D3FRjwlUxvNDZFxDeWQ=
AllowedIPs = 10.10.0.0/16, 100.64.0.0/16
PersistentKeepalive = 25
Endpoint = 185.206.122.31:3241
35 changes: 35 additions & 0 deletions examples/develop/wireguard/wireguard.vsh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/usr/bin/env -S v -n -w -gc none -no-retry-compilation -d use_openssl -enable-globals run

import freeflowuniverse.herolib.clients.wireguard
import freeflowuniverse.herolib.installers.net.wireguard as wireguard_installer
import time
import os

mut wg_installer := wireguard_installer.get()!
wg_installer.install()!

// Create Wireguard client
mut wg := wireguard.get()!
config_file_path := '${os.dir(@FILE)}/wg0.conf'

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: 'wg0')!
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}')

wg.down(config_file_path: config_file_path)!
println('${config_file_path} is down')

wg_installer.destroy()!
8 changes: 8 additions & 0 deletions lib/clients/wireguard/.heroscript
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

!!hero_code.generate_client
name:'wireguard'
classname:'WireGuard'
singleton:0
default:1
hasconfig:1
reset:0
114 changes: 114 additions & 0 deletions lib/clients/wireguard/client.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
module wireguard

import os

pub struct WGPeer {
pub mut:
endpoint string
allowed_ips string
latest_handshake string
transfer string
persistent_keepalive string
}

pub struct WGInterface {
pub mut:
name string
public_key string
listening_port int
}

pub struct WGInfo {
pub mut:
interface_ WGInterface
peers map[string]WGPeer
}

pub struct WGShow {
pub mut:
configs map[string]WGInfo
}

pub fn (wg WireGuard) show() !WGShow {
cmd := 'sudo 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}'
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()
}
30 changes: 30 additions & 0 deletions lib/clients/wireguard/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# wireguard



To get started

```vlang


import freeflowuniverse.herolib.clients. wireguard

mut client:= wireguard.get()!

client...




```

## example heroscript

```hero
!!wireguard.configure
secret: '...'
host: 'localhost'
port: 8888
```


72 changes: 72 additions & 0 deletions lib/clients/wireguard/utils.v
Original file line number Diff line number Diff line change
@@ -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
}
}
102 changes: 102 additions & 0 deletions lib/clients/wireguard/wireguard_factory_.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
module wireguard

import freeflowuniverse.herolib.core.base
import freeflowuniverse.herolib.core.playbook

__global (
wireguard_global map[string]&WireGuard
wireguard_default string
)

/////////FACTORY

@[params]
pub struct ArgsGet {
pub mut:
name string
}

fn args_get(args_ ArgsGet) ArgsGet {
mut args := args_
if args.name == '' {
args.name = wireguard_default
}
if args.name == '' {
args.name = 'wireguard'
}
return args
}

pub fn get(args_ ArgsGet) !&WireGuard {
mut args := args_get(args_)
if args.name !in wireguard_global {
if args.name == 'wireguard' {
if !config_exists(args) {
if default {
println('When saving')
config_save(args)!
}
}
config_load(args)!
}
}
return wireguard_global[args.name] or {
println(wireguard_global)
panic('could not get config for wireguard with name:${args.name}')
}
}

fn config_exists(args_ ArgsGet) bool {
mut args := args_get(args_)
mut context := base.context() or { panic('bug') }
return context.hero_config_exists('wireguard', args.name)
}

fn config_load(args_ ArgsGet) ! {
mut args := args_get(args_)
mut context := base.context()!
mut heroscript := context.hero_config_get('wireguard', args.name)!
play(heroscript: heroscript)!
}

fn config_save(args_ ArgsGet) ! {
mut args := args_get(args_)
mut context := base.context()!
context.hero_config_set('wireguard', args.name, heroscript_default()!)!
}

fn set(o WireGuard) ! {
mut o2 := obj_init(o)!
wireguard_global[o.name] = &o2
wireguard_default = o.name
}

@[params]
pub struct PlayArgs {
pub mut:
heroscript string // if filled in then plbook will be made out of it
plbook ?playbook.PlayBook
reset bool
}

pub fn play(args_ PlayArgs) ! {
mut args := args_

if args.heroscript == '' {
args.heroscript = heroscript_default()!
}
mut plbook := args.plbook or { playbook.new(text: args.heroscript)! }

mut install_actions := plbook.find(filter: 'wireguard.configure')!
if install_actions.len > 0 {
for install_action in install_actions {
mut p := install_action.params
cfg_play(p)!
}
}
}

// switch instance to be used for wireguard
pub fn switch(name string) {
wireguard_default = name
}
Loading
Loading