diff --git a/cmd/wgmeshd/main.go b/cmd/wgmeshd/main.go index 9fd57a9..ff33091 100644 --- a/cmd/wgmeshd/main.go +++ b/cmd/wgmeshd/main.go @@ -5,6 +5,7 @@ import ( "github.com/tim-beatham/wgmesh/pkg/conf" ctrlserver "github.com/tim-beatham/wgmesh/pkg/ctrlserver" + "github.com/tim-beatham/wgmesh/pkg/ip" "github.com/tim-beatham/wgmesh/pkg/ipc" logging "github.com/tim-beatham/wgmesh/pkg/log" "github.com/tim-beatham/wgmesh/pkg/middleware" @@ -33,8 +34,14 @@ func main() { ctrlServer, err := ctrlserver.NewCtrlServer(&ctrlServerParams) authProvider.Manager = ctrlServer.ConnectionServer.JwtManager + + robinIpcParams := robin.RobinIpcParams{ + CtrlServer: ctrlServer, + Allocator: &ip.ULABuilder{}, + } + robinRpc.Server = ctrlServer - robinIpc.Server = ctrlServer + robinIpc = robin.NewRobinIpc(robinIpcParams) if err != nil { logging.ErrorLog.Fatalln(err.Error()) diff --git a/pkg/automerge/automerge.go b/pkg/automerge/automerge.go new file mode 100644 index 0000000..3a39156 --- /dev/null +++ b/pkg/automerge/automerge.go @@ -0,0 +1,53 @@ +package crdt + +import "github.com/automerge/automerge-go" + +// CrdtNodeManager manages nodes in the crdt mesh +type CrdtNodeManager struct { + meshId string + IfName string + doc *automerge.Doc +} + +func (c *CrdtNodeManager) AddNode(crdt MeshNodeCrdt) { + c.doc.Path("nodes").Map().Set(crdt.PublicKey, crdt) +} + +// GetCrdt(): Converts the document into a struct +func (c *CrdtNodeManager) GetCrdt() (*MeshCrdt, error) { + return automerge.As[*MeshCrdt](c.doc.Root()) +} + +// Load: Load an entire mesh network +func (c *CrdtNodeManager) Load(bytes []byte) error { + doc, err := automerge.Load(bytes) + + if err != nil { + return err + } + + c.doc = doc + return nil +} + +// Save: Save an entire mesh network +func (c *CrdtNodeManager) Save(doc []byte) []byte { + return c.doc.Save() +} + +func (c *CrdtNodeManager) LoadChanges(changes []byte) { + c.doc.LoadIncremental(changes) +} + +func (c *CrdtNodeManager) SaveChanges() []byte { + return c.SaveChanges() +} + +// NewCrdtNodeManager: Create a new crdt node manager +func NewCrdtNodeManager(meshId, devName string) *CrdtNodeManager { + var manager CrdtNodeManager + manager.meshId = meshId + manager.doc = automerge.New() + manager.IfName = devName + return &manager +} diff --git a/pkg/automerge/types.go b/pkg/automerge/types.go new file mode 100644 index 0000000..58e52f9 --- /dev/null +++ b/pkg/automerge/types.go @@ -0,0 +1,12 @@ +package crdt + +type MeshNodeCrdt struct { + HostEndpoint string `automerge:"hostEndpoint"` + WgEndpoint string `automerge:"wgEndpoint"` + PublicKey string `automerge:"publicKey"` + WgHost string `automerge:"wgHost"` +} + +type MeshCrdt struct { + Nodes map[string]MeshNodeCrdt `automerge:"nodes"` +} diff --git a/pkg/conn/conn.go b/pkg/conn/conn.go index 4b51df1..b884055 100644 --- a/pkg/conn/conn.go +++ b/pkg/conn/conn.go @@ -13,7 +13,6 @@ import ( "github.com/tim-beatham/wgmesh/pkg/rpc" "google.golang.org/grpc" "google.golang.org/grpc/credentials" - "google.golang.org/grpc/keepalive" "google.golang.org/grpc/metadata" ) @@ -35,12 +34,6 @@ type WgCtrlConnection struct { tokens map[string]string } -var keepAliveParams = keepalive.ClientParameters{ - Time: 5 * time.Minute, - Timeout: time.Second, - PermitWithoutStream: true, -} - func NewWgCtrlConnection(clientConfig *tls.Config, server string) (*WgCtrlConnection, error) { var conn WgCtrlConnection conn.tokens = make(map[string]string) @@ -82,7 +75,6 @@ func (c *WgCtrlConnection) Authenticate(meshId string) error { // ConnectWithToken: Connects to a new gRPC peer given the address of the other server. func (c *WgCtrlConnection) Connect() error { conn, err := grpc.Dial(c.endpoint, - grpc.WithKeepaliveParams(keepAliveParams), grpc.WithTransportCredentials(credentials.NewTLS(c.clientConfig)), ) diff --git a/pkg/conn/conn_manager.go b/pkg/conn/conn_manager.go index 0634497..ed5c88c 100644 --- a/pkg/conn/conn_manager.go +++ b/pkg/conn/conn_manager.go @@ -7,22 +7,27 @@ import ( logging "github.com/tim-beatham/wgmesh/pkg/log" ) +type ConnectionManager interface { + AddConnection(endPoint string) (PeerConnection, error) + GetConnection(endPoint string) (PeerConnection, error) +} + // ConnectionManager manages connections between other peers // in the control plane. -type ConnectionManager struct { +type JwtConnectionManager struct { // clientConnections maps an endpoint to a connection clientConnections map[string]PeerConnection serverConfig *tls.Config clientConfig *tls.Config } -type NewConnectionManagerParams struct { +type NewJwtConnectionManagerParams struct { CertificatePath string PrivateKey string SkipCertVerification bool } -func NewConnectionManager(params *NewConnectionManagerParams) (*ConnectionManager, error) { +func NewJwtConnectionManager(params *NewJwtConnectionManagerParams) (ConnectionManager, error) { cert, err := tls.LoadX509KeyPair(params.CertificatePath, params.PrivateKey) if err != nil { @@ -49,11 +54,11 @@ func NewConnectionManager(params *NewConnectionManagerParams) (*ConnectionManage } connections := make(map[string]PeerConnection) - connMgr := ConnectionManager{connections, serverConfig, clientConfig} + connMgr := JwtConnectionManager{connections, serverConfig, clientConfig} return &connMgr, nil } -func (m *ConnectionManager) GetConnection(endpoint string) (PeerConnection, error) { +func (m *JwtConnectionManager) GetConnection(endpoint string) (PeerConnection, error) { conn, exists := m.clientConnections[endpoint] if !exists { @@ -63,12 +68,8 @@ func (m *ConnectionManager) GetConnection(endpoint string) (PeerConnection, erro return conn, nil } -type AddConnectionParams struct { - TokenId string -} - // AddToken: Adds a connection to the list of connections to manage -func (m *ConnectionManager) AddConnection(endPoint string) (PeerConnection, error) { +func (m *JwtConnectionManager) AddConnection(endPoint string) (PeerConnection, error) { _, exists := m.clientConnections[endPoint] if exists { diff --git a/pkg/ctrlserver/ctrlserver.go b/pkg/ctrlserver/ctrlserver.go index 267f7d5..07114f9 100644 --- a/pkg/ctrlserver/ctrlserver.go +++ b/pkg/ctrlserver/ctrlserver.go @@ -6,16 +6,11 @@ package ctrlserver import ( - "errors" - "net" - "github.com/tim-beatham/wgmesh/pkg/conf" "github.com/tim-beatham/wgmesh/pkg/conn" - "github.com/tim-beatham/wgmesh/pkg/lib" + "github.com/tim-beatham/wgmesh/pkg/manager" "github.com/tim-beatham/wgmesh/pkg/rpc" - "github.com/tim-beatham/wgmesh/pkg/wg" "golang.zx2c4.com/wireguard/wgctrl" - "golang.zx2c4.com/wireguard/wgctrl/wgtypes" ) type NewCtrlServerParams struct { @@ -33,17 +28,16 @@ type NewCtrlServerParams struct { */ func NewCtrlServer(params *NewCtrlServerParams) (*MeshCtrlServer, error) { ctrlServer := new(MeshCtrlServer) - ctrlServer.Meshes = make(map[string]Mesh) ctrlServer.Client = params.WgClient - ctrlServer.IfName = params.Conf.IfName + ctrlServer.MeshManager = &manager.MeshManger{} - connManagerParams := conn.NewConnectionManagerParams{ + connManagerParams := conn.NewJwtConnectionManagerParams{ CertificatePath: params.Conf.CertificatePath, PrivateKey: params.Conf.PrivateKeyPath, SkipCertVerification: params.Conf.SkipCertVerification, } - connMgr, err := conn.NewConnectionManager(&connManagerParams) + connMgr, err := conn.NewJwtConnectionManager(&connManagerParams) if err != nil { return nil, err @@ -68,162 +62,3 @@ func NewCtrlServer(params *NewCtrlServerParams) (*MeshCtrlServer, error) { ctrlServer.ConnectionServer = connServer return ctrlServer, nil } - -/* - * MeshExists returns true if the client is part of the mesh - * false otherwise. - */ -func (server *MeshCtrlServer) MeshExists(meshId string) bool { - _, inMesh := server.Meshes[meshId] - return inMesh -} - -/* - * CreateMesh creates a new mesh instance, adds it to the map - * of meshes and returns the newly created mesh. - */ -func (server *MeshCtrlServer) CreateMesh() (*Mesh, error) { - key, err := wgtypes.GenerateKey() - - if err != nil { - return nil, err - } - - var mesh Mesh = Mesh{ - SharedKey: &key, - Nodes: make(map[string]MeshNode), - } - - server.Meshes[key.String()] = mesh - return &mesh, nil -} - -/* - * AddHostArgs parameters needed to add - * a host to the mesh network - */ -type AddHostArgs struct { - // HostEndpoint: Public IP and port of the gRPC server the node is running - HostEndpoint string - // PubilcKey: WireGuard public key of the device - PublicKey string - // MeshId: Mesh ID of the node the the host is joining - MeshId string - // WgEndpoint: Public IP and port of the WireGuard server that is running - WgEndpoint string - // WgIp: SLAAC generated WireGuard IP of the node - WgIp string -} - -/* - * AddHost adds a host to the mesh - */ -func (server *MeshCtrlServer) AddHost(args AddHostArgs) error { - nodes, contains := server.Meshes[args.MeshId] - - if !contains { - return errors.New("The mesh: " + args.MeshId + " does not exist") - } - - _, contains = nodes.Nodes[args.HostEndpoint] - - if contains { - return errors.New("The node already has an endpoint in the mesh network") - } - - node := MeshNode{ - HostEndpoint: args.HostEndpoint, - WgEndpoint: args.WgEndpoint, - PublicKey: args.PublicKey, - WgHost: args.WgIp, - } - - err := server.AddWgPeer(node) - - if err == nil { - nodes.Nodes[args.HostEndpoint] = node - } - - return err -} - -/* - * GetDevice gets the WireGuard client associated with the - * interface name. - */ -func (server *MeshCtrlServer) GetDevice() *wgtypes.Device { - dev, err := server.Client.Device(server.IfName) - - if err != nil { - return nil - } - - return dev -} - -/* - * AddWgPeer Updates the WireGuard configuration to include the peer - */ -func (server *MeshCtrlServer) AddWgPeer(node MeshNode) error { - peer := make([]wgtypes.PeerConfig, 1) - - peerPublic, err := wgtypes.ParseKey(node.PublicKey) - - if err != nil { - return err - } - - peerEndpoint, err := net.ResolveUDPAddr("udp", node.WgEndpoint) - - if err != nil { - return err - } - - allowedIps := make([]net.IPNet, 1) - _, ipnet, err := net.ParseCIDR(node.WgHost) - - if err != nil { - return err - } - - allowedIps[0] = *ipnet - - peer[0] = wgtypes.PeerConfig{ - PublicKey: peerPublic, - Endpoint: peerEndpoint, - AllowedIPs: allowedIps, - } - - cfg := wgtypes.Config{ - Peers: peer, - } - - server.Client.ConfigureDevice(server.IfName, cfg) - - if err != nil { - return err - } - - return nil -} - -/* - * EnableInterface: Enables the given WireGuard interface. - */ -func (s *MeshCtrlServer) EnableInterface(meshId string) error { - mesh, contains := s.Meshes[meshId] - - if !contains { - return errors.New("Mesh does not exist") - } - - endPoint := lib.GetOutboundIP().String() + ":8080" - - node, contains := mesh.Nodes[endPoint] - - if !contains { - return errors.New("Node does not exist in the mesh") - } - - return wg.EnableInterface(s.IfName, node.WgHost) -} diff --git a/pkg/ctrlserver/ctrltypes.go b/pkg/ctrlserver/ctrltypes.go index 8abd3d7..5a12892 100644 --- a/pkg/ctrlserver/ctrltypes.go +++ b/pkg/ctrlserver/ctrltypes.go @@ -2,6 +2,7 @@ package ctrlserver import ( "github.com/tim-beatham/wgmesh/pkg/conn" + "github.com/tim-beatham/wgmesh/pkg/manager" "golang.zx2c4.com/wireguard/wgctrl" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" ) @@ -27,8 +28,7 @@ type Mesh struct { */ type MeshCtrlServer struct { Client *wgctrl.Client - Meshes map[string]Mesh - IfName string - ConnectionManager *conn.ConnectionManager + MeshManager *manager.MeshManger + ConnectionManager conn.ConnectionManager ConnectionServer *conn.ConnectionServer } diff --git a/pkg/gossip/gossip_requester.go b/pkg/gossip/gossip_requester.go new file mode 100644 index 0000000..4bedf23 --- /dev/null +++ b/pkg/gossip/gossip_requester.go @@ -0,0 +1,28 @@ +package gossip + +import ( + "github.com/tim-beatham/wgmesh/pkg/ctrlserver" + "github.com/tim-beatham/wgmesh/pkg/ip" + "github.com/tim-beatham/wgmesh/pkg/ipc" +) + +type GossipRequester struct { + Server *ctrlserver.MeshCtrlServer + ipAlloactor ip.IPAllocator +} + +func (r *GossipRequester) CreateMesh(name string, reply *string) error { + return nil +} + +func (r *GossipRequester) ListMeshes(name string, reply string) error { + return nil +} + +func (r *GossipRequester) JoinMesh(args ipc.JoinMeshArgs, reply *string) error { + return nil +} + +func (r *GossipRequester) GetMesh(meshId string, reply *ipc.GetMeshReply) error { + return nil +} diff --git a/pkg/gossip/gossip_responser.go b/pkg/gossip/gossip_responser.go new file mode 100644 index 0000000..1022ea1 --- /dev/null +++ b/pkg/gossip/gossip_responser.go @@ -0,0 +1 @@ +package gossip diff --git a/pkg/cga/cga.go b/pkg/ip/cga.go similarity index 97% rename from pkg/cga/cga.go rename to pkg/ip/cga.go index dd57c71..e10876e 100644 --- a/pkg/cga/cga.go +++ b/pkg/ip/cga.go @@ -1,4 +1,4 @@ -package cga +package ip /* * Use a WireGuard public key to generate a unique interface ID @@ -106,7 +106,7 @@ func (c *CgaParameters) generateInterface() []byte { return interfaceId } -func (c *CgaParameters) GetIpv6() net.IP { +func (c *CgaParameters) GetIP() net.IP { if c.flag == 1 { return c.interfaceId[:] } diff --git a/pkg/ip/types.go b/pkg/ip/types.go new file mode 100644 index 0000000..55fe314 --- /dev/null +++ b/pkg/ip/types.go @@ -0,0 +1,11 @@ +package ip + +import ( + "net" + + "golang.zx2c4.com/wireguard/wgctrl/wgtypes" +) + +type IPAllocator interface { + GetIP(key wgtypes.Key, meshId string) (net.IP, error) +} diff --git a/pkg/slaac/slaac.go b/pkg/ip/ula.go similarity index 62% rename from pkg/slaac/slaac.go rename to pkg/ip/ula.go index 5eea8ef..df70ea8 100644 --- a/pkg/slaac/slaac.go +++ b/pkg/ip/ula.go @@ -1,15 +1,13 @@ -package slaac +package ip import ( "crypto/sha1" + "net" - "github.com/tim-beatham/wgmesh/pkg/cga" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" ) -type ULA struct { - CGA cga.CgaParameters -} +type ULABuilder struct{} func getULAPrefix(meshId string) [8]byte { var ulaPrefix [8]byte @@ -26,14 +24,13 @@ func getULAPrefix(meshId string) [8]byte { return ulaPrefix } -func NewULA(key wgtypes.Key, meshId string) (*ULA, error) { +func (u *ULABuilder) GetIP(key wgtypes.Key, meshId string) (net.IP, error) { ulaPrefix := getULAPrefix(meshId) - c, err := cga.NewCga(key, ulaPrefix) + c, err := NewCga(key, ulaPrefix) if err != nil { return nil, err } - - return &ULA{CGA: *c}, nil + return c.GetIP(), nil } diff --git a/pkg/manager/mesh_manager.go b/pkg/manager/mesh_manager.go new file mode 100644 index 0000000..575a9a3 --- /dev/null +++ b/pkg/manager/mesh_manager.go @@ -0,0 +1,95 @@ +package manager + +import ( + "errors" + + crdt "github.com/tim-beatham/wgmesh/pkg/automerge" + "github.com/tim-beatham/wgmesh/pkg/lib" + "github.com/tim-beatham/wgmesh/pkg/wg" + "golang.zx2c4.com/wireguard/wgctrl" + "golang.zx2c4.com/wireguard/wgctrl/wgtypes" +) + +type MeshManger struct { + Meshes map[string]*crdt.CrdtNodeManager + Client *wgctrl.Client +} + +func (m *MeshManger) MeshExists(meshId string) bool { + _, inMesh := m.Meshes[meshId] + return inMesh +} + +// CreateMesh: Creates a new mesh, stores it and returns the mesh id +func (m *MeshManger) CreateMesh(meshId, devName string) (string, error) { + key, err := wgtypes.GenerateKey() + + if err != nil { + return "", err + } + + nodeManager := crdt.NewCrdtNodeManager(key.String(), devName) + m.Meshes[key.String()] = nodeManager + return key.String(), nil +} + +// UpdateMesh: merge the changes and save it to the device +func (m *MeshManger) UpdateMesh(meshId string, changes []byte, client wgctrl.Client) error { + mesh, ok := m.Meshes[meshId] + + if !ok { + return errors.New("mesh does not exist") + } + + mesh.LoadChanges(changes) + + crdt, err := mesh.GetCrdt() + + if err != nil { + return err + } + + wg.UpdateWgConf(m.Meshes[meshId].IfName, crdt.Nodes, client) + return nil +} + +// EnableInterface: Enables the given WireGuard interface. +func (s *MeshManger) EnableInterface(meshId string) error { + mesh, contains := s.Meshes[meshId] + + if !contains { + return errors.New("Mesh does not exist") + } + + crdt, err := mesh.GetCrdt() + + if err != nil { + return err + } + + endPoint := lib.GetOutboundIP().String() + ":8080" + node, contains := crdt.Nodes[endPoint] + + if !contains { + return errors.New("Node does not exist in the mesh") + } + + return wg.EnableInterface(mesh.IfName, node.WgEndpoint) +} + +// GetPublicKey: Gets the public key of the WireGuard mesh +func (s *MeshManger) GetPublicKey(meshId string) (*wgtypes.Key, error) { + mesh, ok := s.Meshes[meshId] + + if !ok { + return nil, errors.New("mesh does not exist") + } + + dev, err := s.Client.Device(mesh.IfName) + + if err != nil { + return nil, err + } + + return &dev.PublicKey, nil +} diff --git a/pkg/robin/robin_requester.go b/pkg/robin/robin_requester.go index bbb508e..8e398fe 100644 --- a/pkg/robin/robin_requester.go +++ b/pkg/robin/robin_requester.go @@ -8,24 +8,30 @@ import ( "time" "github.com/tim-beatham/wgmesh/pkg/ctrlserver" + "github.com/tim-beatham/wgmesh/pkg/ip" "github.com/tim-beatham/wgmesh/pkg/ipc" "github.com/tim-beatham/wgmesh/pkg/lib" logging "github.com/tim-beatham/wgmesh/pkg/log" "github.com/tim-beatham/wgmesh/pkg/rpc" - "github.com/tim-beatham/wgmesh/pkg/slaac" "github.com/tim-beatham/wgmesh/pkg/wg" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" ) type RobinIpc struct { - Server *ctrlserver.MeshCtrlServer + Server *ctrlserver.MeshCtrlServer + ipAllocator ip.IPAllocator } func (n *RobinIpc) CreateMesh(name string, reply *string) error { wg.CreateInterface(n.Server.IfName) mesh, err := n.Server.CreateMesh() - ula, _ := slaac.NewULA(n.Server.GetDevice().PublicKey, "0") + + nodeIP, err := n.ipAllocator.GetIP(n.Server.GetPublicKey(), mesh.SharedKey.String()) + + if err != nil { + return err + } outBoundIp := lib.GetOutboundIP().String() @@ -33,7 +39,7 @@ func (n *RobinIpc) CreateMesh(name string, reply *string) error { HostEndpoint: outBoundIp + ":8080", PublicKey: n.Server.GetDevice().PublicKey.String(), WgEndpoint: outBoundIp + ":51820", - WgIp: ula.CGA.GetIpv6().String() + "/128", + WgIp: nodeIP.String() + "/128", MeshId: mesh.SharedKey.String(), } @@ -190,7 +196,6 @@ func (n *RobinIpc) Authenticate(meshId, endpoint string) error { return err } - err = peerConnection.Connect() return err } @@ -225,16 +230,21 @@ func (n *RobinIpc) JoinMesh(args ipc.JoinMeshArgs, reply *string) error { defer cancel() dev := n.Server.GetDevice() - ula, _ := slaac.NewULA(dev.PublicKey, "0") - logging.InfoLog.Println("WgIP: " + ula.CGA.GetIpv6().String()) + ipAddr, err := n.ipAllocator.GetIP(n.Server.GetPublicKey(), args.MeshId) + + if err != nil { + return err + } + + logging.InfoLog.Println("WgIP: " + ipAddr.String()) joinMeshReq := rpc.JoinMeshRequest{ MeshId: args.MeshId, HostPort: 8080, PublicKey: dev.PublicKey.String(), WgPort: int32(dev.ListenPort), - WgIp: ula.CGA.GetIpv6().String() + "/128", + WgIp: ipAddr.String() + "/128", } r, err := c.JoinMesh(ctx, &joinMeshReq) @@ -283,6 +293,14 @@ func (n *RobinIpc) EnableInterface(meshId string, reply *string) error { return nil } -func NewRobinIpc(ctrlServer *ctrlserver.MeshCtrlServer) *RobinIpc { - return &RobinIpc{Server: ctrlServer} +type RobinIpcParams struct { + CtrlServer *ctrlserver.MeshCtrlServer + Allocator ip.IPAllocator +} + +func NewRobinIpc(ipcParams RobinIpcParams) RobinIpc { + return RobinIpc{ + Server: ipcParams.CtrlServer, + ipAllocator: ipcParams.Allocator, + } } diff --git a/pkg/rpc/ctrlserver.pb.go b/pkg/rpc/ctrlserver.pb.go index 04f4da1..c0d5f7f 100644 --- a/pkg/rpc/ctrlserver.pb.go +++ b/pkg/rpc/ctrlserver.pb.go @@ -143,8 +143,7 @@ type GetMeshReply struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - MeshId string `protobuf:"bytes,1,opt,name=meshId,proto3" json:"meshId,omitempty"` - MeshNode []*MeshNode `protobuf:"bytes,2,rep,name=meshNode,proto3" json:"meshNode,omitempty"` + Mesh []byte `protobuf:"bytes,2,opt,name=mesh,proto3" json:"mesh,omitempty"` } func (x *GetMeshReply) Reset() { @@ -179,16 +178,9 @@ func (*GetMeshReply) Descriptor() ([]byte, []int) { return file_pkg_grpc_ctrlserver_ctrlserver_proto_rawDescGZIP(), []int{2} } -func (x *GetMeshReply) GetMeshId() string { +func (x *GetMeshReply) GetMesh() []byte { if x != nil { - return x.MeshId - } - return "" -} - -func (x *GetMeshReply) GetMeshNode() []*MeshNode { - if x != nil { - return x.MeshNode + return x.Mesh } return nil } @@ -198,11 +190,7 @@ type JoinMeshRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - MeshId string `protobuf:"bytes,1,opt,name=meshId,proto3" json:"meshId,omitempty"` - HostPort int32 `protobuf:"varint,2,opt,name=hostPort,proto3" json:"hostPort,omitempty"` - PublicKey string `protobuf:"bytes,3,opt,name=publicKey,proto3" json:"publicKey,omitempty"` - WgPort int32 `protobuf:"varint,4,opt,name=wgPort,proto3" json:"wgPort,omitempty"` - WgIp string `protobuf:"bytes,5,opt,name=wgIp,proto3" json:"wgIp,omitempty"` + Changes []byte `protobuf:"bytes,1,opt,name=changes,proto3" json:"changes,omitempty"` } func (x *JoinMeshRequest) Reset() { @@ -237,39 +225,11 @@ func (*JoinMeshRequest) Descriptor() ([]byte, []int) { return file_pkg_grpc_ctrlserver_ctrlserver_proto_rawDescGZIP(), []int{3} } -func (x *JoinMeshRequest) GetMeshId() string { +func (x *JoinMeshRequest) GetChanges() []byte { if x != nil { - return x.MeshId + return x.Changes } - return "" -} - -func (x *JoinMeshRequest) GetHostPort() int32 { - if x != nil { - return x.HostPort - } - return 0 -} - -func (x *JoinMeshRequest) GetPublicKey() string { - if x != nil { - return x.PublicKey - } - return "" -} - -func (x *JoinMeshRequest) GetWgPort() int32 { - if x != nil { - return x.WgPort - } - return 0 -} - -func (x *JoinMeshRequest) GetWgIp() string { - if x != nil { - return x.WgIp - } - return "" + return nil } type JoinMeshReply struct { @@ -277,8 +237,7 @@ type JoinMeshReply struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` - MeshIp *string `protobuf:"bytes,2,opt,name=meshIp,proto3,oneof" json:"meshIp,omitempty"` + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` } func (x *JoinMeshReply) Reset() { @@ -320,13 +279,6 @@ func (x *JoinMeshReply) GetSuccess() bool { return false } -func (x *JoinMeshReply) GetMeshIp() string { - if x != nil && x.MeshIp != nil { - return *x.MeshIp - } - return "" -} - var File_pkg_grpc_ctrlserver_ctrlserver_proto protoreflect.FileDescriptor var file_pkg_grpc_ctrlserver_ctrlserver_proto_rawDesc = []byte{ @@ -343,37 +295,25 @@ var file_pkg_grpc_ctrlserver_ctrlserver_proto_rawDesc = []byte{ 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x77, 0x67, 0x48, 0x6f, 0x73, 0x74, 0x22, 0x28, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x73, 0x68, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x06, 0x6d, 0x65, 0x73, 0x68, 0x49, 0x64, 0x22, 0x56, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x4d, - 0x65, 0x73, 0x68, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x73, 0x68, - 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x73, 0x68, 0x49, 0x64, - 0x12, 0x2e, 0x0a, 0x08, 0x6d, 0x65, 0x73, 0x68, 0x4e, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x72, 0x70, 0x63, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4d, 0x65, - 0x73, 0x68, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x08, 0x6d, 0x65, 0x73, 0x68, 0x4e, 0x6f, 0x64, 0x65, - 0x22, 0x8f, 0x01, 0x0a, 0x0f, 0x4a, 0x6f, 0x69, 0x6e, 0x4d, 0x65, 0x73, 0x68, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x73, 0x68, 0x49, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x73, 0x68, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, - 0x68, 0x6f, 0x73, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, - 0x68, 0x6f, 0x73, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, - 0x69, 0x63, 0x4b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x75, 0x62, - 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x77, 0x67, 0x50, 0x6f, 0x72, 0x74, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x77, 0x67, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x12, - 0x0a, 0x04, 0x77, 0x67, 0x49, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x77, 0x67, - 0x49, 0x70, 0x22, 0x51, 0x0a, 0x0d, 0x4a, 0x6f, 0x69, 0x6e, 0x4d, 0x65, 0x73, 0x68, 0x52, 0x65, - 0x70, 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, - 0x06, 0x6d, 0x65, 0x73, 0x68, 0x49, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, - 0x06, 0x6d, 0x65, 0x73, 0x68, 0x49, 0x70, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x6d, - 0x65, 0x73, 0x68, 0x49, 0x70, 0x32, 0x91, 0x01, 0x0a, 0x0e, 0x4d, 0x65, 0x73, 0x68, 0x43, 0x74, - 0x72, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x3d, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x4d, - 0x65, 0x73, 0x68, 0x12, 0x18, 0x2e, 0x72, 0x70, 0x63, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x47, - 0x65, 0x74, 0x4d, 0x65, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, - 0x72, 0x70, 0x63, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x73, 0x68, - 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x40, 0x0a, 0x08, 0x4a, 0x6f, 0x69, 0x6e, 0x4d, - 0x65, 0x73, 0x68, 0x12, 0x19, 0x2e, 0x72, 0x70, 0x63, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4a, - 0x6f, 0x69, 0x6e, 0x4d, 0x65, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, - 0x2e, 0x72, 0x70, 0x63, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x4d, 0x65, - 0x73, 0x68, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, 0x09, 0x5a, 0x07, 0x70, 0x6b, 0x67, - 0x2f, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x52, 0x06, 0x6d, 0x65, 0x73, 0x68, 0x49, 0x64, 0x22, 0x22, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x4d, + 0x65, 0x73, 0x68, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x65, 0x73, 0x68, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x6d, 0x65, 0x73, 0x68, 0x22, 0x2b, 0x0a, 0x0f, + 0x4a, 0x6f, 0x69, 0x6e, 0x4d, 0x65, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x22, 0x29, 0x0a, 0x0d, 0x4a, 0x6f, 0x69, + 0x6e, 0x4d, 0x65, 0x73, 0x68, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x32, 0x91, 0x01, 0x0a, 0x0e, 0x4d, 0x65, 0x73, 0x68, 0x43, 0x74, 0x72, + 0x6c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x3d, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x4d, 0x65, + 0x73, 0x68, 0x12, 0x18, 0x2e, 0x72, 0x70, 0x63, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x47, 0x65, + 0x74, 0x4d, 0x65, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x72, + 0x70, 0x63, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x73, 0x68, 0x52, + 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x40, 0x0a, 0x08, 0x4a, 0x6f, 0x69, 0x6e, 0x4d, 0x65, + 0x73, 0x68, 0x12, 0x19, 0x2e, 0x72, 0x70, 0x63, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4a, 0x6f, + 0x69, 0x6e, 0x4d, 0x65, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, + 0x72, 0x70, 0x63, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x4d, 0x65, 0x73, + 0x68, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, 0x09, 0x5a, 0x07, 0x70, 0x6b, 0x67, 0x2f, + 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -397,16 +337,15 @@ var file_pkg_grpc_ctrlserver_ctrlserver_proto_goTypes = []interface{}{ (*JoinMeshReply)(nil), // 4: rpctypes.JoinMeshReply } var file_pkg_grpc_ctrlserver_ctrlserver_proto_depIdxs = []int32{ - 0, // 0: rpctypes.GetMeshReply.meshNode:type_name -> rpctypes.MeshNode - 1, // 1: rpctypes.MeshCtrlServer.GetMesh:input_type -> rpctypes.GetMeshRequest - 3, // 2: rpctypes.MeshCtrlServer.JoinMesh:input_type -> rpctypes.JoinMeshRequest - 2, // 3: rpctypes.MeshCtrlServer.GetMesh:output_type -> rpctypes.GetMeshReply - 4, // 4: rpctypes.MeshCtrlServer.JoinMesh:output_type -> rpctypes.JoinMeshReply - 3, // [3:5] is the sub-list for method output_type - 1, // [1:3] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name + 1, // 0: rpctypes.MeshCtrlServer.GetMesh:input_type -> rpctypes.GetMeshRequest + 3, // 1: rpctypes.MeshCtrlServer.JoinMesh:input_type -> rpctypes.JoinMeshRequest + 2, // 2: rpctypes.MeshCtrlServer.GetMesh:output_type -> rpctypes.GetMeshReply + 4, // 3: rpctypes.MeshCtrlServer.JoinMesh:output_type -> rpctypes.JoinMeshReply + 2, // [2:4] is the sub-list for method output_type + 0, // [0:2] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name } func init() { file_pkg_grpc_ctrlserver_ctrlserver_proto_init() } @@ -476,7 +415,6 @@ func file_pkg_grpc_ctrlserver_ctrlserver_proto_init() { } } } - file_pkg_grpc_ctrlserver_ctrlserver_proto_msgTypes[4].OneofWrappers = []interface{}{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/pkg/wg/wg.go b/pkg/wg/wg.go index ea17373..34dbf52 100644 --- a/pkg/wg/wg.go +++ b/pkg/wg/wg.go @@ -5,6 +5,7 @@ import ( "net" "os/exec" + crdt "github.com/tim-beatham/wgmesh/pkg/automerge" "golang.zx2c4.com/wireguard/wgctrl" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" ) @@ -81,3 +82,59 @@ func EnableInterface(ifName string, ip string) error { return nil } + +func convertMeshNode(node crdt.MeshNodeCrdt) (*wgtypes.PeerConfig, error) { + peerEndpoint, err := net.ResolveUDPAddr("udp", node.WgEndpoint) + + if err != nil { + return nil, err + } + + peerPublic, err := wgtypes.ParseKey(node.PublicKey) + + if err != nil { + return nil, err + } + + allowedIps := make([]net.IPNet, 1) + _, ipnet, err := net.ParseCIDR(node.WgHost) + + if err != nil { + return nil, err + } + + allowedIps[0] = *ipnet + + peerConfig := wgtypes.PeerConfig{ + PublicKey: peerPublic, + Endpoint: peerEndpoint, + AllowedIPs: allowedIps, + } + + return &peerConfig, nil +} + +func UpdateWgConf(devName string, nodes map[string]crdt.MeshNodeCrdt, client wgctrl.Client) error { + peerConfigs := make([]wgtypes.PeerConfig, len(nodes)) + + var count int = 0 + + for _, n := range nodes { + peer, err := convertMeshNode(n) + + if err != nil { + return err + } + + peerConfigs[count] = *peer + count++ + } + + cfg := wgtypes.Config{ + Peers: peerConfigs, + ReplacePeers: true, + } + + client.ConfigureDevice(devName, cfg) + return nil +}