50-give-client-ability-to-bridge-meshes

Client can act as a route bridging meshes. Cient send keepalives
to all of it's peers in the different meshes act as a bridge between
the meshes
This commit is contained in:
Tim Beatham 2023-12-08 23:56:07 +00:00
parent 52feb5767b
commit f855f53fbf

View File

@ -27,6 +27,7 @@ type WgMeshConfigApplyer struct {
meshManager MeshManager meshManager MeshManager
config *conf.WgMeshConfiguration config *conf.WgMeshConfiguration
routeInstaller route.RouteInstaller routeInstaller route.RouteInstaller
hashFunc func(MeshNode) int
} }
type routeNode struct { type routeNode struct {
@ -38,12 +39,6 @@ func (m *WgMeshConfigApplyer) convertMeshNode(node MeshNode, device *wgtypes.Dev
peerToClients map[string][]net.IPNet, peerToClients map[string][]net.IPNet,
routes map[string][]routeNode) (*wgtypes.PeerConfig, error) { routes map[string][]routeNode) (*wgtypes.PeerConfig, error) {
endpoint, err := net.ResolveUDPAddr("udp", node.GetWgEndpoint())
if err != nil {
return nil, err
}
pubKey, err := node.GetPublicKey() pubKey, err := node.GetPublicKey()
if err != nil { if err != nil {
@ -66,17 +61,12 @@ func (m *WgMeshConfigApplyer) convertMeshNode(node MeshNode, device *wgtypes.Dev
if len(bestRoutes) == 1 { if len(bestRoutes) == 1 {
pickedRoute = bestRoutes[0] pickedRoute = bestRoutes[0]
} else if len(bestRoutes) > 1 { } else if len(bestRoutes) > 1 {
keyFunc := func(mn MeshNode) int {
pubKey, _ := mn.GetPublicKey()
return lib.HashString(pubKey.String())
}
bucketFunc := func(rn routeNode) int { bucketFunc := func(rn routeNode) int {
return lib.HashString(rn.gateway) return lib.HashString(rn.gateway)
} }
// Else there is more than one candidate so consistently hash // Else there is more than one candidate so consistently hash
pickedRoute = lib.ConsistentHash(bestRoutes, node, bucketFunc, keyFunc) pickedRoute = lib.ConsistentHash(bestRoutes, node, bucketFunc, m.hashFunc)
} }
if pickedRoute.gateway == pubKey.String() { if pickedRoute.gateway == pubKey.String() {
@ -91,6 +81,13 @@ func (m *WgMeshConfigApplyer) convertMeshNode(node MeshNode, device *wgtypes.Dev
return p.PublicKey.String() == pubKey.String() return p.PublicKey.String() == pubKey.String()
}) })
endpoint, err := net.ResolveUDPAddr("udp", node.GetWgEndpoint())
if err != nil {
return nil, err
}
// Don't override the existing IP in case it already exists
if existing != -1 { if existing != -1 {
endpoint = device.Peers[existing].Endpoint endpoint = device.Peers[existing].Endpoint
} }
@ -110,9 +107,12 @@ func (m *WgMeshConfigApplyer) convertMeshNode(node MeshNode, device *wgtypes.Dev
// consistently hash to evenly spread the distribution of traffic // consistently hash to evenly spread the distribution of traffic
func (m *WgMeshConfigApplyer) getRoutes(meshProvider MeshProvider) map[string][]routeNode { func (m *WgMeshConfigApplyer) getRoutes(meshProvider MeshProvider) map[string][]routeNode {
mesh, _ := meshProvider.GetMesh() mesh, _ := meshProvider.GetMesh()
routes := make(map[string][]routeNode) routes := make(map[string][]routeNode)
peers := lib.Filter(lib.MapValues(mesh.GetNodes()), func(p MeshNode) bool {
return p.GetType() == conf.PEER_ROLE
})
meshPrefixes := lib.Map(lib.MapValues(m.meshManager.GetMeshes()), func(mesh MeshProvider) *net.IPNet { meshPrefixes := lib.Map(lib.MapValues(m.meshManager.GetMeshes()), func(mesh MeshProvider) *net.IPNet {
ula := &ip.ULABuilder{} ula := &ip.ULABuilder{}
ipNet, _ := ula.GetIPNet(mesh.GetMeshId()) ipNet, _ := ula.GetIPNet(mesh.GetMeshId())
@ -144,6 +144,24 @@ func (m *WgMeshConfigApplyer) getRoutes(meshProvider MeshProvider) map[string][]
route: route, route: route,
} }
// Client's only acessible by another peer
if node.GetType() == conf.CLIENT_ROLE {
peer := m.getCorrespondingPeer(peers, node)
self, _ := m.meshManager.GetSelf(meshProvider.GetMeshId())
// If the node isn't the self use that peer as the gateway
if !NodeEquals(peer, self) {
peerPub, _ := peer.GetPublicKey()
rn.gateway = peerPub.String()
rn.route = &RouteStub{
Destination: rn.route.GetDestination(),
HopCount: rn.route.GetHopCount() + 1,
// Append the path to this peer
Path: append(rn.route.GetPath(), peer.GetWgHost().IP.String()),
}
}
}
if !ok { if !ok {
otherRoute = make([]routeNode, 1) otherRoute = make([]routeNode, 1)
otherRoute[0] = rn otherRoute[0] = rn
@ -163,31 +181,35 @@ func (m *WgMeshConfigApplyer) getRoutes(meshProvider MeshProvider) map[string][]
// getCorrespondignPeer: gets the peer corresponding to the client // getCorrespondignPeer: gets the peer corresponding to the client
func (m *WgMeshConfigApplyer) getCorrespondingPeer(peers []MeshNode, client MeshNode) MeshNode { func (m *WgMeshConfigApplyer) getCorrespondingPeer(peers []MeshNode, client MeshNode) MeshNode {
hashFunc := func(mn MeshNode) int { peer := lib.ConsistentHash(peers, client, m.hashFunc, m.hashFunc)
pubKey, _ := mn.GetPublicKey()
return lib.HashString(pubKey.String())
}
peer := lib.ConsistentHash(peers, client, hashFunc, hashFunc)
return peer return peer
} }
func (m *WgMeshConfigApplyer) getClientConfig(mesh MeshProvider, peers []MeshNode, clients []MeshNode, dev *wgtypes.Device) (*wgtypes.Config, error) { type GetConfigParams struct {
self, err := m.meshManager.GetSelf(mesh.GetMeshId()) mesh MeshProvider
ula := &ip.ULABuilder{} peers []MeshNode
meshNet, _ := ula.GetIPNet(mesh.GetMeshId()) clients []MeshNode
dev *wgtypes.Device
routes map[string][]routeNode
}
routes := lib.Map(lib.MapKeys(m.getRoutes(mesh)), func(destination string) net.IPNet { func (m *WgMeshConfigApplyer) getClientConfig(params *GetConfigParams) (*wgtypes.Config, error) {
self, err := m.meshManager.GetSelf(params.mesh.GetMeshId())
ula := &ip.ULABuilder{}
meshNet, _ := ula.GetIPNet(params.mesh.GetMeshId())
routes := lib.Map(lib.MapKeys(params.routes), func(destination string) net.IPNet {
_, ipNet, _ := net.ParseCIDR(destination) _, ipNet, _ := net.ParseCIDR(destination)
return *ipNet return *ipNet
}) })
routes = append(routes, *meshNet) routes = append(routes, *meshNet)
if err != nil { if err != nil {
return nil, err return nil, err
} }
peer := m.getCorrespondingPeer(peers, self) peer := m.getCorrespondingPeer(params.peers, self)
pubKey, _ := peer.GetPublicKey() pubKey, _ := peer.GetPublicKey()
@ -205,6 +227,7 @@ func (m *WgMeshConfigApplyer) getClientConfig(mesh MeshProvider, peers []MeshNod
Endpoint: endpoint, Endpoint: endpoint,
PersistentKeepaliveInterval: &keepAlive, PersistentKeepaliveInterval: &keepAlive,
AllowedIPs: routes, AllowedIPs: routes,
ReplaceAllowedIPs: true,
} }
installedRoutes := make([]lib.Route, 0) installedRoutes := make([]lib.Route, 0)
@ -220,24 +243,43 @@ func (m *WgMeshConfigApplyer) getClientConfig(mesh MeshProvider, peers []MeshNod
Peers: peerCfgs, Peers: peerCfgs,
} }
m.routeInstaller.InstallRoutes(dev.Name, installedRoutes...) m.routeInstaller.InstallRoutes(params.dev.Name, installedRoutes...)
return &cfg, err return &cfg, err
} }
func (m *WgMeshConfigApplyer) getPeerConfig(mesh MeshProvider, peers []MeshNode, clients []MeshNode, dev *wgtypes.Device) (*wgtypes.Config, error) { func (m *WgMeshConfigApplyer) getRoutesToInstall(wgNode *wgtypes.PeerConfig, mesh MeshProvider, node MeshNode) []lib.Route {
routes := make([]lib.Route, 0)
for _, route := range wgNode.AllowedIPs {
ula := &ip.ULABuilder{}
ipNet, _ := ula.GetIPNet(mesh.GetMeshId())
_, defaultRoute, _ := net.ParseCIDR("::/0")
if !ipNet.Contains(route.IP) && !ipNet.IP.Equal(defaultRoute.IP) {
routes = append(routes, lib.Route{
Gateway: node.GetWgHost().IP,
Destination: route,
})
}
}
return routes
}
func (m *WgMeshConfigApplyer) getPeerConfig(params *GetConfigParams) (*wgtypes.Config, error) {
peerToClients := make(map[string][]net.IPNet) peerToClients := make(map[string][]net.IPNet)
routes := m.getRoutes(mesh)
installedRoutes := make([]lib.Route, 0) installedRoutes := make([]lib.Route, 0)
peerConfigs := make([]wgtypes.PeerConfig, 0) peerConfigs := make([]wgtypes.PeerConfig, 0)
self, err := m.meshManager.GetSelf(mesh.GetMeshId()) self, err := m.meshManager.GetSelf(params.mesh.GetMeshId())
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, n := range clients { for _, n := range params.clients {
if len(peers) > 0 { if len(params.peers) > 0 {
peer := m.getCorrespondingPeer(peers, n) peer := m.getCorrespondingPeer(params.peers, n)
pubKey, _ := peer.GetPublicKey() pubKey, _ := peer.GetPublicKey()
clients, ok := peerToClients[pubKey.String()] clients, ok := peerToClients[pubKey.String()]
@ -249,42 +291,30 @@ func (m *WgMeshConfigApplyer) getPeerConfig(mesh MeshProvider, peers []MeshNode,
peerToClients[pubKey.String()] = append(clients, *n.GetWgHost()) peerToClients[pubKey.String()] = append(clients, *n.GetWgHost())
if NodeEquals(self, peer) { if NodeEquals(self, peer) {
cfg, err := m.convertMeshNode(n, dev, peerToClients, routes) cfg, err := m.convertMeshNode(n, params.dev, peerToClients, params.routes)
if err != nil { if err != nil {
return nil, err return nil, err
} }
installedRoutes = append(installedRoutes, m.getRoutesToInstall(cfg, params.mesh, n)...)
peerConfigs = append(peerConfigs, *cfg) peerConfigs = append(peerConfigs, *cfg)
} }
} }
} }
for _, n := range peers { for _, n := range params.peers {
if NodeEquals(n, self) { if NodeEquals(n, self) {
continue continue
} }
peer, err := m.convertMeshNode(n, dev, peerToClients, routes) peer, err := m.convertMeshNode(n, params.dev, peerToClients, params.routes)
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, route := range peer.AllowedIPs { installedRoutes = append(installedRoutes, m.getRoutesToInstall(peer, params.mesh, n)...)
ula := &ip.ULABuilder{}
ipNet, _ := ula.GetIPNet(mesh.GetMeshId())
_, defaultRoute, _ := net.ParseCIDR("::/0")
if !ipNet.Contains(route.IP) && !ipNet.IP.Equal(defaultRoute.IP) {
installedRoutes = append(installedRoutes, lib.Route{
Gateway: n.GetWgHost().IP,
Destination: route,
})
}
}
peerConfigs = append(peerConfigs, *peer) peerConfigs = append(peerConfigs, *peer)
} }
@ -293,7 +323,7 @@ func (m *WgMeshConfigApplyer) getPeerConfig(mesh MeshProvider, peers []MeshNode,
ReplacePeers: true, ReplacePeers: true,
} }
err = m.routeInstaller.InstallRoutes(dev.Name, installedRoutes...) err = m.routeInstaller.InstallRoutes(params.dev.Name, installedRoutes...)
return &cfg, err return &cfg, err
} }
@ -327,11 +357,20 @@ func (m *WgMeshConfigApplyer) updateWgConf(mesh MeshProvider) error {
var cfg *wgtypes.Config = nil var cfg *wgtypes.Config = nil
routes := m.getRoutes(mesh)
configParams := &GetConfigParams{
mesh: mesh,
peers: peers,
clients: clients,
dev: dev,
routes: routes,
}
switch self.GetType() { switch self.GetType() {
case conf.PEER_ROLE: case conf.PEER_ROLE:
cfg, err = m.getPeerConfig(mesh, peers, clients, dev) cfg, err = m.getPeerConfig(configParams)
case conf.CLIENT_ROLE: case conf.CLIENT_ROLE:
cfg, err = m.getClientConfig(mesh, peers, clients, dev) cfg, err = m.getClientConfig(configParams)
} }
if err != nil { if err != nil {
@ -388,5 +427,9 @@ func NewWgMeshConfigApplyer(config *conf.WgMeshConfiguration) MeshConfigApplyer
return &WgMeshConfigApplyer{ return &WgMeshConfigApplyer{
config: config, config: config,
routeInstaller: route.NewRouteInstaller(), routeInstaller: route.NewRouteInstaller(),
hashFunc: func(mn MeshNode) int {
pubKey, _ := mn.GetPublicKey()
return lib.HashString(pubKey.String())
},
} }
} }