feat (tfgrid3deployer): add more support for wireguard

- support adding multiple user access endpoints to a network
- support connecting gateways over wireguard

Co-authored-by: mahmoud <mahmmoud.hassanein@gmail.com>
This commit is contained in:
2025-01-30 18:07:58 +02:00
parent 6a8bd5c205
commit 45098785e9
5 changed files with 112 additions and 68 deletions

View File

@@ -11,11 +11,12 @@ griddriver.install()!
v := tfgrid3deployer.get()!
println('cred: ${v}')
deployment_name := 'vm_caddy1'
deployment_name := 'wireguard_dep_example'
mut deployment := tfgrid3deployer.new_deployment(deployment_name)!
deployment.add_network(ip_range: '1.1.1.1/16', user_access: 5)
deployment.configure_network(user_access_endpoints: 3)!
deployment.add_machine(
name: 'vm_caddy1'
name: 'vm1'
cpu: 1
memory: 2
planetary: false
@@ -25,12 +26,22 @@ deployment.add_machine(
)
deployment.deploy()!
vm1 := deployment.vm_get('vm_caddy1')!
vm1 := deployment.vm_get('vm1')!
println('vm1 info: ${vm1}')
vm1_public_ip4 := vm1.public_ip4.all_before('/')
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: 'gwnamecaddy', backend: 'http://${vm1_public_ip4}:80', use_wireguard: true)
deployment.add_webname(
name: 'gwoverwg'
backend: 'http://${vm1.wireguard_ip}:8000'
use_wireguard_network: true
)
deployment.deploy()!
gw1 := deployment.webname_get('gwnamecaddy')!
gw1 := deployment.webname_get('gwoverwg')!
println('gw info: ${gw1}')
// tfgrid3deployer.delete_deployment(deployment_name)!

View File

@@ -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:
@@ -285,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 {
@@ -339,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 {
@@ -514,7 +516,10 @@ 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
}

View File

@@ -32,15 +32,15 @@ fn new_deployment_setup(network_specs NetworkSpecs, vms []VMachine, zdbs []ZDB,
mut dls := DeploymentSetup{
deployer: deployer
network_handler: NetworkHandler{
req: network_specs.requirements
deployer: deployer
network_name: network_specs.name
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)!
@@ -68,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 {
@@ -177,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(
@@ -206,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
@@ -219,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 {

View File

@@ -7,12 +7,13 @@ import json
import rand
// NetworkInfo struct to represent network details
@[params]
pub struct NetworkRequirements {
pub:
pub mut:
name string = 'net' + rand.string(5)
user_access_endpoints int
}
@[params]
pub struct NetworkSpecs {
pub mut:
@@ -22,11 +23,26 @@ pub mut:
user_access_configs []UserAccessConfig
}
struct 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 {
@@ -51,7 +67,7 @@ mut:
}
// 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 = []
@@ -61,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 {
@@ -88,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]}'
}
}
@@ -99,14 +123,6 @@ 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.req.user_access_endpoints == 0 && (self.hidden_nodes.len < 1 || self.nodes.len == 1) {
self.public_node = 0
return
@@ -162,6 +178,26 @@ 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() ! {
console.print_header('Setting up network workload.')
self.hidden_nodes, self.none_accessible_ip_ranges = [], []
@@ -211,22 +247,6 @@ fn (mut self NetworkHandler) setup_wireguard_data() ! {
self.none_accessible_ip_ranges << wireguard_routing_ip(self.wg_subnet[node_id])
}
}
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]
}
}
}
fn (mut self NetworkHandler) prepare_public_node_peers(node_id u32) ![]grid_models.Peer {
@@ -265,7 +285,7 @@ fn (mut self NetworkHandler) prepare_public_node_peers(node_id u32) ![]grid_mode
}
for user_access in self.user_access_configs {
routing_ip := wireguard_routing_ip(subnet)
routing_ip := wireguard_routing_ip(user_access.ip)
peers << grid_models.Peer{
subnet: user_access.ip
@@ -319,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
@@ -339,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
@@ -368,5 +392,3 @@ fn (mut self NetworkHandler) generate_workloads() !map[u32]grid_models.Workload
return workloads
}

View File

@@ -7,6 +7,7 @@ pub struct WebNameRequirements {
pub mut:
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