diff --git a/examples/develop/wireguard/wg0.conf b/examples/develop/wireguard/wg0.conf new file mode 100644 index 00000000..2a6b5223 --- /dev/null +++ b/examples/develop/wireguard/wg0.conf @@ -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 \ No newline at end of file diff --git a/examples/develop/wireguard/wireguard.vsh b/examples/develop/wireguard/wireguard.vsh new file mode 100755 index 00000000..0c112ce0 --- /dev/null +++ b/examples/develop/wireguard/wireguard.vsh @@ -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()! diff --git a/lib/clients/wireguard/.heroscript b/lib/clients/wireguard/.heroscript new file mode 100644 index 00000000..3ce752f5 --- /dev/null +++ b/lib/clients/wireguard/.heroscript @@ -0,0 +1,8 @@ + +!!hero_code.generate_client + name:'wireguard' + classname:'WireGuard' + singleton:0 + default:1 + hasconfig:1 + reset:0 \ No newline at end of file diff --git a/lib/clients/wireguard/client.v b/lib/clients/wireguard/client.v new file mode 100644 index 00000000..7cc5dcf2 --- /dev/null +++ b/lib/clients/wireguard/client.v @@ -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() +} diff --git a/lib/clients/wireguard/readme.md b/lib/clients/wireguard/readme.md new file mode 100644 index 00000000..5fe40e9f --- /dev/null +++ b/lib/clients/wireguard/readme.md @@ -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 +``` + + diff --git a/lib/clients/wireguard/utils.v b/lib/clients/wireguard/utils.v new file mode 100644 index 00000000..968973e7 --- /dev/null +++ b/lib/clients/wireguard/utils.v @@ -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 + } +} diff --git a/lib/clients/wireguard/wireguard_factory_.v b/lib/clients/wireguard/wireguard_factory_.v new file mode 100644 index 00000000..679e4064 --- /dev/null +++ b/lib/clients/wireguard/wireguard_factory_.v @@ -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 +} diff --git a/lib/clients/wireguard/wireguard_model.v b/lib/clients/wireguard/wireguard_model.v new file mode 100644 index 00000000..678a308b --- /dev/null +++ b/lib/clients/wireguard/wireguard_model.v @@ -0,0 +1,38 @@ +module wireguard + +import freeflowuniverse.herolib.data.paramsparser + +pub const version = '1.14.3' +const singleton = false +const default = true + +// TODO: THIS IS EXAMPLE CODE AND NEEDS TO BE CHANGED IN LINE TO STRUCT BELOW, IS STRUCTURED AS HEROSCRIPT +pub fn heroscript_default() !string { + heroscript := " + !!wireguard.configure + name:'wireguard' + " + return heroscript +} + +// THIS THE THE SOURCE OF THE INFORMATION OF THIS FILE, HERE WE HAVE THE CONFIG OBJECT CONFIGURED AND MODELLED + +@[heap] +pub struct WireGuard { +pub mut: + name string = 'wireguard' +} + +fn cfg_play(p paramsparser.Params) ! { + // THIS IS EXAMPLE CODE AND NEEDS TO BE CHANGED IN LINE WITH struct above + mut mycfg := WireGuard{ + name: p.get_default('name', 'wireguard')! + } + set(mycfg)! +} + +fn obj_init(obj_ WireGuard) !WireGuard { + // never call get here, only thing we can do here is work on object itself + mut obj := obj_ + return obj +} diff --git a/lib/installers/net/wireguard/.heroscript b/lib/installers/net/wireguard/.heroscript new file mode 100644 index 00000000..7f6d3479 --- /dev/null +++ b/lib/installers/net/wireguard/.heroscript @@ -0,0 +1,13 @@ + +!!hero_code.generate_installer + name:'wireguard_installer' + classname:'WireGuard' + singleton:0 + templates:0 + default:1 + title:'' + supported_platforms:'' + reset:0 + startupmanager:0 + hasconfig:0 + build:0 \ No newline at end of file diff --git a/lib/installers/net/wireguard/readme.md b/lib/installers/net/wireguard/readme.md new file mode 100644 index 00000000..73a58c61 --- /dev/null +++ b/lib/installers/net/wireguard/readme.md @@ -0,0 +1,44 @@ +# wireguard + + + +To get started + +```vlang + + +import freeflowuniverse.herolib.installers.something.wireguard as wireguard_installer + +heroscript:=" +!!wireguard.configure name:'test' + password: '1234' + port: 7701 + +!!wireguard.start name:'test' reset:1 +" + +wireguard_installer.play(heroscript=heroscript)! + +//or we can call the default and do a start with reset +//mut installer:= wireguard_installer.get()! +//installer.start(reset:true)! + + + + +``` + +## example heroscript + +```hero +!!wireguard.configure + homedir: '/home/user/wireguard' + username: 'admin' + password: 'secretpassword' + title: 'Some Title' + host: 'localhost' + port: 8888 + +``` + + diff --git a/lib/installers/net/wireguard/wireguard_actions.v b/lib/installers/net/wireguard/wireguard_actions.v new file mode 100644 index 00000000..596c63b1 --- /dev/null +++ b/lib/installers/net/wireguard/wireguard_actions.v @@ -0,0 +1,59 @@ +module wireguard + +import freeflowuniverse.herolib.osal +import freeflowuniverse.herolib.installers.ulist +import freeflowuniverse.herolib.ui.console +import freeflowuniverse.herolib.core + +//////////////////// following actions are not specific to instance of the object + +// checks if a certain version or above is installed +fn installed() !bool { + osal.execute_silent('wg --version') or { return false } + return true +} + +// get the Upload List of the files +fn ulist_get() !ulist.UList { + return ulist.UList{} +} + +// uploads to S3 server if configured +fn upload() ! { +} + +fn install() ! { + console.print_header('install wireguard') + + cmd := match core.platform()! { + .ubuntu { + 'sudo apt install -y wireguard' + } + .osx { + 'sudo brew install -y wireguard-tools' + } + else { + return error('unsupported platfrom ${core.platform()!}') + } + } + + osal.execute_stdout(cmd)! +} + +fn destroy() ! { + console.print_header('uninstall wireguard') + + cmd := match core.platform()! { + .ubuntu { + 'sudo apt remove -y wireguard wireguard-tools' + } + .osx { + 'sudo brew uninstall -y wireguard-tools' + } + else { + return error('unsupported platform ${core.platform()!}') + } + } + + osal.execute_stdout(cmd)! +} diff --git a/lib/installers/net/wireguard/wireguard_factory_.v b/lib/installers/net/wireguard/wireguard_factory_.v new file mode 100644 index 00000000..eae7890d --- /dev/null +++ b/lib/installers/net/wireguard/wireguard_factory_.v @@ -0,0 +1,74 @@ +module wireguard + +import freeflowuniverse.herolib.core.base +import freeflowuniverse.herolib.core.playbook +import freeflowuniverse.herolib.ui.console +import freeflowuniverse.herolib.sysadmin.startupmanager +import freeflowuniverse.herolib.osal.zinit +import time + +__global ( + wireguard_installer_global map[string]&WireGuard + wireguard_installer_default string +) + +/////////FACTORY + +@[params] +pub struct ArgsGet { +pub mut: + name string +} + +pub fn get(args_ ArgsGet) !&WireGuard { + return &WireGuard{} +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////# LIVE CYCLE MANAGEMENT FOR INSTALLERS /////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +fn startupmanager_get(cat zinit.StartupManagerType) !startupmanager.StartupManager { + // unknown + // screen + // zinit + // tmux + // systemd + match cat { + .zinit { + console.print_debug('startupmanager: zinit') + return startupmanager.get(cat: .zinit)! + } + .systemd { + console.print_debug('startupmanager: systemd') + return startupmanager.get(cat: .systemd)! + } + else { + console.print_debug('startupmanager: auto') + return startupmanager.get()! + } + } +} + +@[params] +pub struct InstallArgs { +pub mut: + reset bool +} + +pub fn (mut self WireGuard) install(args InstallArgs) ! { + switch(self.name) + if args.reset || (!installed()!) { + install()! + } +} + +pub fn (mut self WireGuard) destroy() ! { + switch(self.name) + destroy()! +} + +// switch instance to be used for wireguard_installer +pub fn switch(name string) { + wireguard_installer_default = name +} diff --git a/lib/installers/net/wireguard/wireguard_model.v b/lib/installers/net/wireguard/wireguard_model.v new file mode 100644 index 00000000..c4c7d5d2 --- /dev/null +++ b/lib/installers/net/wireguard/wireguard_model.v @@ -0,0 +1,23 @@ +module wireguard + +pub const version = '1.14.3' +const singleton = false +const default = true + +// THIS THE THE SOURCE OF THE INFORMATION OF THIS FILE, HERE WE HAVE THE CONFIG OBJECT CONFIGURED AND MODELLED +@[heap] +pub struct WireGuard { +pub mut: + name string = 'default' +} + +fn obj_init(obj_ WireGuard) !WireGuard { + // never call get here, only thing we can do here is work on object itself + mut obj := obj_ + return obj +} + +// called before start if done +fn configure() ! { + // mut installer := get()! +}