2023-10-26 17:53:12 +02:00
|
|
|
package mesh
|
|
|
|
|
|
|
|
import (
|
2023-11-01 12:58:10 +01:00
|
|
|
"fmt"
|
2023-11-21 17:42:49 +01:00
|
|
|
"hash/fnv"
|
2023-10-26 17:53:12 +02:00
|
|
|
"net"
|
2023-11-21 17:42:49 +01:00
|
|
|
"time"
|
2023-10-26 17:53:12 +02:00
|
|
|
|
2023-11-21 17:42:49 +01:00
|
|
|
"github.com/tim-beatham/wgmesh/pkg/conf"
|
|
|
|
"github.com/tim-beatham/wgmesh/pkg/lib"
|
|
|
|
"github.com/tim-beatham/wgmesh/pkg/route"
|
2023-10-26 17:53:12 +02:00
|
|
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
|
|
|
)
|
|
|
|
|
|
|
|
// MeshConfigApplyer abstracts applying the mesh configuration
|
|
|
|
type MeshConfigApplyer interface {
|
|
|
|
ApplyConfig() error
|
2023-11-01 12:58:10 +01:00
|
|
|
RemovePeers(meshId string) error
|
2023-11-06 10:54:06 +01:00
|
|
|
SetMeshManager(manager MeshManager)
|
2023-10-26 17:53:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// WgMeshConfigApplyer applies WireGuard configuration
|
|
|
|
type WgMeshConfigApplyer struct {
|
2023-11-21 17:42:49 +01:00
|
|
|
meshManager MeshManager
|
|
|
|
config *conf.WgMeshConfiguration
|
|
|
|
routeInstaller route.RouteInstaller
|
2023-10-26 17:53:12 +02:00
|
|
|
}
|
|
|
|
|
2023-11-21 17:42:49 +01:00
|
|
|
func (m *WgMeshConfigApplyer) convertMeshNode(node MeshNode) (*wgtypes.PeerConfig, error) {
|
2023-10-26 17:53:12 +02:00
|
|
|
endpoint, err := net.ResolveUDPAddr("udp", node.GetWgEndpoint())
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
pubKey, err := node.GetPublicKey()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
allowedips := make([]net.IPNet, 1)
|
|
|
|
allowedips[0] = *node.GetWgHost()
|
|
|
|
|
|
|
|
for _, route := range node.GetRoutes() {
|
|
|
|
_, ipnet, _ := net.ParseCIDR(route)
|
|
|
|
allowedips = append(allowedips, *ipnet)
|
|
|
|
}
|
|
|
|
|
2023-11-21 17:42:49 +01:00
|
|
|
keepAlive := time.Duration(m.config.KeepAliveTime) * time.Second
|
|
|
|
|
2023-10-26 17:53:12 +02:00
|
|
|
peerConfig := wgtypes.PeerConfig{
|
2023-11-21 17:42:49 +01:00
|
|
|
PublicKey: pubKey,
|
|
|
|
Endpoint: endpoint,
|
|
|
|
AllowedIPs: allowedips,
|
|
|
|
PersistentKeepaliveInterval: &keepAlive,
|
2023-10-26 17:53:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return &peerConfig, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *WgMeshConfigApplyer) updateWgConf(mesh MeshProvider) error {
|
|
|
|
snap, err := mesh.GetMesh()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-11-21 17:42:49 +01:00
|
|
|
nodes := lib.MapValues(snap.GetNodes())
|
2023-10-26 17:53:12 +02:00
|
|
|
peerConfigs := make([]wgtypes.PeerConfig, len(nodes))
|
|
|
|
|
2023-11-21 17:42:49 +01:00
|
|
|
peers := lib.Filter(nodes, func(mn MeshNode) bool {
|
|
|
|
return mn.GetType() == conf.PEER_ROLE
|
|
|
|
})
|
|
|
|
|
2023-10-26 17:53:12 +02:00
|
|
|
var count int = 0
|
|
|
|
|
2023-11-21 17:42:49 +01:00
|
|
|
self, err := m.meshManager.GetSelf(mesh.GetMeshId())
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
rtnl, err := lib.NewRtNetlinkConfig()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-10-26 17:53:12 +02:00
|
|
|
for _, n := range nodes {
|
2023-11-21 17:42:49 +01:00
|
|
|
if n.GetType() == conf.CLIENT_ROLE && len(peers) > 0 {
|
|
|
|
a := fnv.New32a()
|
|
|
|
a.Write([]byte(n.GetHostEndpoint()))
|
|
|
|
sum := a.Sum32()
|
|
|
|
|
|
|
|
responsiblePeer := peers[int(sum)%len(peers)]
|
|
|
|
|
|
|
|
if responsiblePeer.GetHostEndpoint() != self.GetHostEndpoint() {
|
|
|
|
dev, err := mesh.GetDevice()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
rtnl.AddRoute(dev.Name, lib.Route{
|
|
|
|
Gateway: responsiblePeer.GetWgHost().IP,
|
|
|
|
Destination: *n.GetWgHost(),
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
peer, err := m.convertMeshNode(n)
|
2023-10-26 17:53:12 +02:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
peerConfigs[count] = *peer
|
|
|
|
count++
|
|
|
|
}
|
|
|
|
|
|
|
|
cfg := wgtypes.Config{
|
2023-11-01 11:39:46 +01:00
|
|
|
Peers: peerConfigs,
|
2023-10-26 17:53:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
dev, err := mesh.GetDevice()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-11-05 19:03:58 +01:00
|
|
|
return m.meshManager.GetClient().ConfigureDevice(dev.Name, cfg)
|
2023-10-26 17:53:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *WgMeshConfigApplyer) ApplyConfig() error {
|
2023-11-05 19:03:58 +01:00
|
|
|
for _, mesh := range m.meshManager.GetMeshes() {
|
2023-10-26 17:53:12 +02:00
|
|
|
err := m.updateWgConf(mesh)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-11-01 12:58:10 +01:00
|
|
|
func (m *WgMeshConfigApplyer) RemovePeers(meshId string) error {
|
|
|
|
mesh := m.meshManager.GetMesh(meshId)
|
|
|
|
|
|
|
|
if mesh == nil {
|
2023-11-06 10:54:06 +01:00
|
|
|
return fmt.Errorf("mesh %s does not exist", meshId)
|
2023-11-01 12:58:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
dev, err := mesh.GetDevice()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-11-05 19:03:58 +01:00
|
|
|
m.meshManager.GetClient().ConfigureDevice(dev.Name, wgtypes.Config{
|
2023-11-01 12:58:10 +01:00
|
|
|
ReplacePeers: true,
|
|
|
|
Peers: make([]wgtypes.PeerConfig, 1),
|
|
|
|
})
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-11-06 10:54:06 +01:00
|
|
|
func (m *WgMeshConfigApplyer) SetMeshManager(manager MeshManager) {
|
|
|
|
m.meshManager = manager
|
|
|
|
}
|
|
|
|
|
2023-11-21 17:42:49 +01:00
|
|
|
func NewWgMeshConfigApplyer(config *conf.WgMeshConfiguration) MeshConfigApplyer {
|
|
|
|
return &WgMeshConfigApplyer{
|
|
|
|
config: config,
|
|
|
|
routeInstaller: route.NewRouteInstaller(),
|
|
|
|
}
|
2023-10-26 17:53:12 +02:00
|
|
|
}
|