diff --git a/examples/threefold/tfgrid3deployer/gw_over_wireguard/gw_over_wireguard.vsh b/examples/threefold/tfgrid3deployer/gw_over_wireguard/gw_over_wireguard.vsh new file mode 100755 index 00000000..e62bc498 --- /dev/null +++ b/examples/threefold/tfgrid3deployer/gw_over_wireguard/gw_over_wireguard.vsh @@ -0,0 +1,47 @@ +#!/usr/bin/env -S v -gc none -no-retry-compilation -d use_openssl -enable-globals -cg run + +//#!/usr/bin/env -S v -gc none -no-retry-compilation -cc tcc -d use_openssl -enable-globals -cg run +import freeflowuniverse.herolib.threefold.gridproxy +import freeflowuniverse.herolib.threefold.tfgrid3deployer +import freeflowuniverse.herolib.installers.threefold.griddriver +import os +import time + +griddriver.install()! + +v := tfgrid3deployer.get()! +println('cred: ${v}') +deployment_name := 'wireguard_dep_example' +mut deployment := tfgrid3deployer.new_deployment(deployment_name)! + +deployment.configure_network(user_access_endpoints: 3)! +deployment.add_machine( + name: 'vm1' + cpu: 1 + memory: 2 + planetary: false + public_ip4: true + size: 10 // 10 gig + mycelium: tfgrid3deployer.Mycelium{} +) +deployment.deploy()! + +vm1 := deployment.vm_get('vm1')! +println('vm1 info: ${vm1}') + +user_access_configs := deployment.get_user_access_configs() +for config in user_access_configs { + println('config:\n------\n${config.print_wg_config()}\n------\n') +} + +deployment.add_webname( + name: 'gwoverwg' + backend: 'http://${vm1.wireguard_ip}:8000' + use_wireguard_network: true +) +deployment.deploy()! + +gw1 := deployment.webname_get('gwoverwg')! +println('gw info: ${gw1}') + +// tfgrid3deployer.delete_deployment(deployment_name)! diff --git a/lib/threefold/tfgrid3deployer/deployment.v b/lib/threefold/tfgrid3deployer/deployment.v index 9157bcfc..2e221007 100644 --- a/lib/threefold/tfgrid3deployer/deployment.v +++ b/lib/threefold/tfgrid3deployer/deployment.v @@ -1,7 +1,6 @@ module tfgrid3deployer import freeflowuniverse.herolib.threefold.grid.models as grid_models -import freeflowuniverse.herolib.threefold.gridproxy.model as gridproxy_models import freeflowuniverse.herolib.threefold.grid import freeflowuniverse.herolib.ui.console import compress.zlib @@ -9,7 +8,6 @@ import encoding.hex import x.crypto.chacha20 import crypto.sha256 import json -import rand struct GridContracts { pub mut: @@ -51,7 +49,7 @@ pub fn new_deployment(name string) !TFDeployment { kvstore := KVStoreFS{} if _ := kvstore.get(name) { - return error('Deployment with the same name is already exist.') + return error('Deployment with the same name "${name}" already exists.') } deployer := get_deployer()! @@ -114,6 +112,7 @@ pub fn (mut self TFDeployment) deploy() ! { } fn (mut self TFDeployment) set_nodes() ! { + // TODO: each request should run in a separate thread for mut vm in self.vms { if vm.node_id != 0 { continue @@ -284,10 +283,10 @@ fn (mut self TFDeployment) finalize_deployment(setup DeploymentSetup) ! { } } - self.update_state(name_contracts_map, returned_deployments)! + self.update_state(setup, name_contracts_map, returned_deployments)! } -fn (mut self TFDeployment) update_state(name_contracts_map map[string]u64, dls map[u32]&grid_models.Deployment) ! { +fn (mut self TFDeployment) update_state(setup DeploymentSetup, name_contracts_map map[string]u64, dls map[u32]&grid_models.Deployment) ! { mut workloads := map[u32]map[string]&grid_models.Workload{} for node_id, deployment in dls { @@ -338,6 +337,10 @@ fn (mut self TFDeployment) update_state(name_contracts_map map[string]u64, dls m wn.node_contract_id = dls[wn.node_id].contract_id wn.name_contract_id = name_contracts_map[wn.requirements.name] } + + self.network.ip_range = setup.network_handler.ip_range + self.network.mycelium = setup.network_handler.mycelium + self.network.user_access_configs = setup.network_handler.user_access_configs.clone() } pub fn (mut self TFDeployment) vm_get(vm_name string) !VMachine { @@ -512,3 +515,11 @@ pub fn (mut self TFDeployment) list_deployments() !map[u32]grid_models.Deploymen return dls } + +pub fn (mut self TFDeployment) configure_network(req NetworkRequirements) ! { + self.network.requirements = req +} + +pub fn (mut self TFDeployment) get_user_access_configs() []UserAccessConfig { + return self.network.user_access_configs +} diff --git a/lib/threefold/tfgrid3deployer/deployment_setup.v b/lib/threefold/tfgrid3deployer/deployment_setup.v index d09e9afa..fa2f6581 100644 --- a/lib/threefold/tfgrid3deployer/deployment_setup.v +++ b/lib/threefold/tfgrid3deployer/deployment_setup.v @@ -32,14 +32,15 @@ fn new_deployment_setup(network_specs NetworkSpecs, vms []VMachine, zdbs []ZDB, mut dls := DeploymentSetup{ deployer: deployer network_handler: NetworkHandler{ - deployer: deployer - network_name: network_specs.name - mycelium: network_specs.mycelium - ip_range: network_specs.ip_range + req: network_specs.requirements + deployer: deployer + mycelium: network_specs.mycelium + ip_range: network_specs.ip_range + user_access_configs: network_specs.user_access_configs.clone() } } - dls.setup_network_workloads(vms, old_deployments)! + dls.setup_network_workloads(vms, webnames, old_deployments)! dls.setup_vm_workloads(vms)! dls.setup_zdb_workloads(zdbs)! dls.setup_webname_workloads(webnames)! @@ -67,9 +68,9 @@ fn (mut self DeploymentSetup) match_versions(old_dls map[u32]grid_models.Deploym // - st: Modified DeploymentSetup struct with network workloads set up // Returns: // - None -fn (mut st DeploymentSetup) setup_network_workloads(vms []VMachine, old_deployments map[u32]grid_models.Deployment) ! { +fn (mut st DeploymentSetup) setup_network_workloads(vms []VMachine, webnames []WebName, old_deployments map[u32]grid_models.Deployment) ! { st.network_handler.load_network_state(old_deployments)! - st.network_handler.create_network(vms)! + st.network_handler.create_network(vms, webnames)! data := st.network_handler.generate_workloads()! for node_id, workload in data { @@ -176,6 +177,11 @@ fn (mut self DeploymentSetup) setup_webname_workloads(webnames []WebName) ! { tls_passthrough: req.tls_passthrough backends: [req.backend] name: gw_name + network: if wn.requirements.use_wireguard_network { + self.network_handler.req.name + } else { + none + } } self.workloads[wn.node_id] << gw.to_workload( @@ -205,7 +211,7 @@ fn (mut self DeploymentSetup) set_zmachine_workload(vmachine VMachine, public_ip network: grid_models.ZmachineNetwork{ interfaces: [ grid_models.ZNetworkInterface{ - network: self.network_handler.network_name + network: self.network_handler.req.name ip: if vmachine.wireguard_ip.len > 0 { used_ip_octets[vmachine.node_id] << vmachine.wireguard_ip.all_after_last('.').u8() vmachine.wireguard_ip @@ -218,7 +224,7 @@ fn (mut self DeploymentSetup) set_zmachine_workload(vmachine VMachine, public_ip planetary: vmachine.requirements.planetary mycelium: if mycelium := vmachine.requirements.mycelium { grid_models.MyceliumIP{ - network: self.network_handler.network_name + network: self.network_handler.req.name hex_seed: mycelium.hex_seed } } else { diff --git a/lib/threefold/tfgrid3deployer/network.v b/lib/threefold/tfgrid3deployer/network.v index ce6055c4..07ef3cc6 100644 --- a/lib/threefold/tfgrid3deployer/network.v +++ b/lib/threefold/tfgrid3deployer/network.v @@ -1,23 +1,54 @@ module tfgrid3deployer import freeflowuniverse.herolib.threefold.grid.models as grid_models -import freeflowuniverse.herolib.threefold.gridproxy import freeflowuniverse.herolib.threefold.grid import freeflowuniverse.herolib.ui.console import json import rand // NetworkInfo struct to represent network details +@[params] +pub struct NetworkRequirements { +pub mut: + name string = 'net' + rand.string(5) + user_access_endpoints int +} + +@[params] pub struct NetworkSpecs { pub mut: - name string = 'net' + rand.string(5) - ip_range string = '10.10.0.0/16' - mycelium string = rand.hex(64) + requirements NetworkRequirements + ip_range string = '10.10.0.0/16' + mycelium string = rand.hex(64) + user_access_configs []UserAccessConfig +} + +pub struct UserAccessConfig { +pub: + ip string + secret_key string + public_key string + + peer_public_key string + network_ip_range string + public_node_endpoint string +} + +pub fn (c UserAccessConfig) print_wg_config() string { + return '[Interface] +Address = ${c.ip} +PrivateKey = ${c.secret_key} +[Peer] +PublicKey = ${c.peer_public_key} +AllowedIPs = ${c.network_ip_range}, 100.64.0.0/16 +PersistentKeepalive = 25 +Endpoint = ${c.public_node_endpoint}' } struct NetworkHandler { mut: - network_name string + req NetworkRequirements + // network_name string nodes []u32 ip_range string wg_ports map[u32]u16 @@ -29,11 +60,14 @@ mut: none_accessible_ip_ranges []string mycelium string + // user_access_endopoints int + user_access_configs []UserAccessConfig + deployer &grid.Deployer @[skip; str: skip] } // TODO: maybe rename to fill_network or something similar -fn (mut self NetworkHandler) create_network(vmachines []VMachine) ! { +fn (mut self NetworkHandler) create_network(vmachines []VMachine, webnames []WebName) ! { // Set nodes self.nodes = [] @@ -43,9 +77,16 @@ fn (mut self NetworkHandler) create_network(vmachines []VMachine) ! { } } + for webname in webnames { + if webname.requirements.use_wireguard_network && !self.nodes.contains(webname.node_id) { + self.nodes << webname.node_id + } + } + console.print_header('Network nodes: ${self.nodes}.') self.setup_wireguard_data()! self.setup_access_node()! + self.setup_user_access()! } fn (mut self NetworkHandler) generate_workload(node_id u32, peers []grid_models.Peer, mycleium_hex_key string) !grid_models.Workload { @@ -62,7 +103,7 @@ fn (mut self NetworkHandler) generate_workload(node_id u32, peers []grid_models. } return network_workload.to_workload( - name: self.network_name + name: self.req.name description: 'VGridClient network workload' ) } @@ -70,10 +111,11 @@ fn (mut self NetworkHandler) generate_workload(node_id u32, peers []grid_models. fn (mut self NetworkHandler) prepare_hidden_node_peers(node_id u32) ![]grid_models.Peer { mut peers := []grid_models.Peer{} if self.public_node != 0 { + ip_range_oct := self.ip_range.all_before('/').split('.') peers << grid_models.Peer{ subnet: self.wg_subnet[self.public_node] wireguard_public_key: self.wg_keys[self.public_node][1] - allowed_ips: [self.ip_range, '100.64.0.0/16'] + allowed_ips: [self.ip_range, '100.64.${ip_range_oct[1]}.${ip_range_oct[2]}/24'] endpoint: '${self.endpoints[self.public_node]}:${self.wg_ports[self.public_node]}' } } @@ -81,15 +123,7 @@ fn (mut self NetworkHandler) prepare_hidden_node_peers(node_id u32) ![]grid_mode } fn (mut self NetworkHandler) setup_access_node() ! { - // Case 1: Deployment on 28 which is hidden node - // - Setup access node - // Case 2: Deployment on 11 which is public node - // - Already have the access node - // Case 3: if the saved state has already public node. - // - Check the new deployment if its node is hidden take the saved one - // - if the access node is already set, that means we have set its values e.g. the wireguard port, keys - - if self.hidden_nodes.len < 1 || self.nodes.len == 1 { + if self.req.user_access_endpoints == 0 && (self.hidden_nodes.len < 1 || self.nodes.len == 1) { self.public_node = 0 return } @@ -144,8 +178,27 @@ fn (mut self NetworkHandler) setup_access_node() ! { self.endpoints[self.public_node] = access_node.public_config.ipv4.split('/')[0] } +fn (mut self NetworkHandler) setup_user_access() ! { + to_create_user_access := self.req.user_access_endpoints - self.user_access_configs.len + if to_create_user_access < 0 { + // TODO: support removing user access + return error('removing user access is not supported') + } + + for i := 0; i < to_create_user_access; i++ { + wg_keys := self.deployer.client.generate_wg_priv_key()! + self.user_access_configs << UserAccessConfig{ + ip: self.calculate_subnet()! + secret_key: wg_keys[0] + public_key: wg_keys[1] + peer_public_key: self.wg_keys[self.public_node][1] + public_node_endpoint: '${self.endpoints[self.public_node]}:${self.wg_ports[self.public_node]}' + network_ip_range: self.ip_range + } + } +} + fn (mut self NetworkHandler) setup_wireguard_data() ! { - // TODO: We need to set the extra node console.print_header('Setting up network workload.') self.hidden_nodes, self.none_accessible_ip_ranges = [], [] @@ -230,6 +283,17 @@ fn (mut self NetworkHandler) prepare_public_node_peers(node_id u32) ![]grid_mode endpoint: '' } } + + for user_access in self.user_access_configs { + routing_ip := wireguard_routing_ip(user_access.ip) + + peers << grid_models.Peer{ + subnet: user_access.ip + wireguard_public_key: user_access.public_key + allowed_ips: [user_access.ip, routing_ip] + endpoint: '' + } + } } return peers @@ -237,10 +301,16 @@ fn (mut self NetworkHandler) prepare_public_node_peers(node_id u32) ![]grid_mode fn (mut self NetworkHandler) calculate_subnet() !string { mut parts := self.ip_range.split('/')[0].split('.') + user_access_subnets := self.user_access_configs.map(it.ip) + node_subnets := self.wg_subnet.values() + mut used_subnets := []string{} + used_subnets << node_subnets.clone() + used_subnets << user_access_subnets.clone() + for i := 2; i <= 255; i += 1 { parts[2] = '${i}' candidate := parts.join('.') + '/24' - if !self.wg_subnet.values().contains(candidate) { + if !used_subnets.contains(candidate) { return candidate } } @@ -269,7 +339,7 @@ fn (mut self NetworkHandler) load_network_state(dls map[u32]grid_models.Deployme continue } - self.network_name = network_name + self.req.name = network_name self.nodes << node_id self.ip_range = znet.ip_range self.wg_ports[node_id] = znet.wireguard_listen_port @@ -289,7 +359,11 @@ fn (mut self NetworkHandler) load_network_state(dls map[u32]grid_models.Deployme } for subnet, endpoint in subnet_to_endpoint { - node_id := subnet_node[subnet] + node_id := subnet_node[subnet] or { + // this maybe a user access, not a node + continue + } + if endpoint == '' { self.hidden_nodes << node_id continue @@ -318,9 +392,3 @@ fn (mut self NetworkHandler) generate_workloads() !map[u32]grid_models.Workload return workloads } - -fn (mut n NetworkHandler) remove_node(node_id u32) ! { -} - -fn (mut n NetworkHandler) add_node() ! { -} diff --git a/lib/threefold/tfgrid3deployer/webnames.v b/lib/threefold/tfgrid3deployer/webnames.v index f372c7f9..67d4434c 100644 --- a/lib/threefold/tfgrid3deployer/webnames.v +++ b/lib/threefold/tfgrid3deployer/webnames.v @@ -5,10 +5,12 @@ import json @[params] pub struct WebNameRequirements { pub mut: - name string @[required] - node_id ?u32 + name string @[required] + node_id ?u32 + use_wireguard_network bool // must be in the format ip:port if tls_passthrough is set, otherwise the format should be http://ip[:port] backend string @[required] + use_wireguard bool tls_passthrough bool }