From c0515d4802ab21b8522cbc86d96524903e5cb552 Mon Sep 17 00:00:00 2001 From: Evan Hazlett Date: Fri, 4 Oct 2019 09:46:03 -0400 Subject: [PATCH] add wireguard connectivity between nodes Signed-off-by: Evan Hazlett --- api/v1/heimdall.pb.go | 166 ++++++++++++++++++++++------------ api/v1/heimdall.proto | 15 ++-- cmd/heimdall/main.go | 12 ++- cmd/heimdall/run.go | 3 +- config.go | 6 +- server/net.go | 187 ++++++++++++++++++++++++++++++++++++++ server/node.go | 129 ++++++++------------------ server/peer.go | 190 +++++++++++++++++++++++++++++++++++++++ server/server.go | 59 ++++++++++-- server/wireguard.go | 39 ++++++-- server/wireguard_test.go | 14 +-- 11 files changed, 640 insertions(+), 180 deletions(-) create mode 100644 server/net.go create mode 100644 server/peer.go diff --git a/api/v1/heimdall.pb.go b/api/v1/heimdall.pb.go index 968201d..859936a 100644 --- a/api/v1/heimdall.pb.go +++ b/api/v1/heimdall.pb.go @@ -163,12 +163,58 @@ func (m *ConnectResponse) GetMaster() *Master { return nil } +type KeyPair struct { + PrivateKey string `protobuf:"bytes,1,opt,name=private_key,json=privateKey,proto3" json:"private_key,omitempty"` + PublicKey string `protobuf:"bytes,2,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *KeyPair) Reset() { *m = KeyPair{} } +func (m *KeyPair) String() string { return proto.CompactTextString(m) } +func (*KeyPair) ProtoMessage() {} +func (*KeyPair) Descriptor() ([]byte, []int) { + return fileDescriptor_b6184fc395da86b1, []int{3} +} +func (m *KeyPair) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_KeyPair.Unmarshal(m, b) +} +func (m *KeyPair) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_KeyPair.Marshal(b, m, deterministic) +} +func (m *KeyPair) XXX_Merge(src proto.Message) { + xxx_messageInfo_KeyPair.Merge(m, src) +} +func (m *KeyPair) XXX_Size() int { + return xxx_messageInfo_KeyPair.Size(m) +} +func (m *KeyPair) XXX_DiscardUnknown() { + xxx_messageInfo_KeyPair.DiscardUnknown(m) +} + +var xxx_messageInfo_KeyPair proto.InternalMessageInfo + +func (m *KeyPair) GetPrivateKey() string { + if m != nil { + return m.PrivateKey + } + return "" +} + +func (m *KeyPair) GetPublicKey() string { + if m != nil { + return m.PublicKey + } + return "" +} + type Node struct { ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` Addr string `protobuf:"bytes,2,opt,name=addr,proto3" json:"addr,omitempty"` - PrivateKey string `protobuf:"bytes,3,opt,name=private_key,json=privateKey,proto3" json:"private_key,omitempty"` - ListenPort uint64 `protobuf:"varint,4,opt,name=listen_port,json=listenPort,proto3" json:"listen_port,omitempty"` - GatewayAddress string `protobuf:"bytes,5,opt,name=gateway_address,json=gatewayAddress,proto3" json:"gateway_address,omitempty"` + KeyPair *KeyPair `protobuf:"bytes,3,opt,name=keypair,proto3" json:"keypair,omitempty"` + GatewayIP string `protobuf:"bytes,4,opt,name=gateway_ip,json=gatewayIp,proto3" json:"gateway_ip,omitempty"` + GatewayPort uint64 `protobuf:"varint,5,opt,name=gateway_port,json=gatewayPort,proto3" json:"gateway_port,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -178,7 +224,7 @@ func (m *Node) Reset() { *m = Node{} } func (m *Node) String() string { return proto.CompactTextString(m) } func (*Node) ProtoMessage() {} func (*Node) Descriptor() ([]byte, []int) { - return fileDescriptor_b6184fc395da86b1, []int{3} + return fileDescriptor_b6184fc395da86b1, []int{4} } func (m *Node) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Node.Unmarshal(m, b) @@ -212,27 +258,27 @@ func (m *Node) GetAddr() string { return "" } -func (m *Node) GetPrivateKey() string { +func (m *Node) GetKeyPair() *KeyPair { if m != nil { - return m.PrivateKey + return m.KeyPair + } + return nil +} + +func (m *Node) GetGatewayIP() string { + if m != nil { + return m.GatewayIP } return "" } -func (m *Node) GetListenPort() uint64 { +func (m *Node) GetGatewayPort() uint64 { if m != nil { - return m.ListenPort + return m.GatewayPort } return 0 } -func (m *Node) GetGatewayAddress() string { - if m != nil { - return m.GatewayAddress - } - return "" -} - type NodesRequest struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` @@ -243,7 +289,7 @@ func (m *NodesRequest) Reset() { *m = NodesRequest{} } func (m *NodesRequest) String() string { return proto.CompactTextString(m) } func (*NodesRequest) ProtoMessage() {} func (*NodesRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_b6184fc395da86b1, []int{4} + return fileDescriptor_b6184fc395da86b1, []int{5} } func (m *NodesRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_NodesRequest.Unmarshal(m, b) @@ -274,7 +320,7 @@ func (m *NodesResponse) Reset() { *m = NodesResponse{} } func (m *NodesResponse) String() string { return proto.CompactTextString(m) } func (*NodesResponse) ProtoMessage() {} func (*NodesResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_b6184fc395da86b1, []int{5} + return fileDescriptor_b6184fc395da86b1, []int{6} } func (m *NodesResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_NodesResponse.Unmarshal(m, b) @@ -302,8 +348,8 @@ func (m *NodesResponse) GetNodes() []*Node { } type Peer struct { - PrivateKey string `protobuf:"bytes,1,opt,name=private_key,json=privateKey,proto3" json:"private_key,omitempty"` - PublicKey string `protobuf:"bytes,2,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + KeyPair *KeyPair `protobuf:"bytes,2,opt,name=keypair,proto3" json:"keypair,omitempty"` AllowedIPs []string `protobuf:"bytes,3,rep,name=allowed_ips,json=allowedIps,proto3" json:"allowed_ips,omitempty"` Endpoint string `protobuf:"bytes,4,opt,name=endpoint,proto3" json:"endpoint,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` @@ -315,7 +361,7 @@ func (m *Peer) Reset() { *m = Peer{} } func (m *Peer) String() string { return proto.CompactTextString(m) } func (*Peer) ProtoMessage() {} func (*Peer) Descriptor() ([]byte, []int) { - return fileDescriptor_b6184fc395da86b1, []int{6} + return fileDescriptor_b6184fc395da86b1, []int{7} } func (m *Peer) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Peer.Unmarshal(m, b) @@ -335,18 +381,18 @@ func (m *Peer) XXX_DiscardUnknown() { var xxx_messageInfo_Peer proto.InternalMessageInfo -func (m *Peer) GetPrivateKey() string { +func (m *Peer) GetID() string { if m != nil { - return m.PrivateKey + return m.ID } return "" } -func (m *Peer) GetPublicKey() string { +func (m *Peer) GetKeyPair() *KeyPair { if m != nil { - return m.PublicKey + return m.KeyPair } - return "" + return nil } func (m *Peer) GetAllowedIPs() []string { @@ -367,6 +413,7 @@ func init() { proto.RegisterType((*Master)(nil), "io.stellarproject.heimdall.api.v1.Master") proto.RegisterType((*ConnectRequest)(nil), "io.stellarproject.heimdall.api.v1.ConnectRequest") proto.RegisterType((*ConnectResponse)(nil), "io.stellarproject.heimdall.api.v1.ConnectResponse") + proto.RegisterType((*KeyPair)(nil), "io.stellarproject.heimdall.api.v1.KeyPair") proto.RegisterType((*Node)(nil), "io.stellarproject.heimdall.api.v1.Node") proto.RegisterType((*NodesRequest)(nil), "io.stellarproject.heimdall.api.v1.NodesRequest") proto.RegisterType((*NodesResponse)(nil), "io.stellarproject.heimdall.api.v1.NodesResponse") @@ -378,39 +425,42 @@ func init() { } var fileDescriptor_b6184fc395da86b1 = []byte{ - // 504 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x53, 0x51, 0x8b, 0x1a, 0x3d, - 0x14, 0x65, 0xd4, 0xf5, 0xd3, 0x3b, 0x7e, 0x0a, 0x43, 0x29, 0x83, 0x50, 0xb4, 0xf3, 0xb2, 0x2e, - 0x94, 0x19, 0xb4, 0x2f, 0x85, 0xd2, 0x07, 0xdd, 0x42, 0x2b, 0xdb, 0x2e, 0x12, 0xba, 0x2f, 0x7d, - 0x91, 0x71, 0xe6, 0xe2, 0xa6, 0x8d, 0x93, 0x34, 0x89, 0x2e, 0x42, 0xa1, 0x3f, 0xa2, 0xcf, 0xfd, - 0x6b, 0x3e, 0xf8, 0x4b, 0x4a, 0x26, 0x61, 0xe8, 0xb2, 0x2c, 0xb5, 0x6f, 0xb9, 0x27, 0xe7, 0x5c, - 0xce, 0x3d, 0xc9, 0x85, 0x57, 0x6b, 0xaa, 0x6f, 0xb7, 0xab, 0x38, 0xe3, 0x9b, 0x44, 0x69, 0x64, - 0x2c, 0x95, 0x42, 0xf2, 0x2f, 0x98, 0xe9, 0xe4, 0x16, 0xe9, 0x26, 0x4f, 0x19, 0x4b, 0x52, 0x41, - 0x93, 0xdd, 0xb8, 0xaa, 0x63, 0x21, 0xb9, 0xe6, 0xc1, 0x73, 0xca, 0xe3, 0xfb, 0x8a, 0xb8, 0x62, - 0xa4, 0x82, 0xc6, 0xbb, 0x71, 0xff, 0xc9, 0x9a, 0xaf, 0x79, 0xc9, 0x4e, 0xcc, 0xc9, 0x0a, 0xa3, - 0x1f, 0xd0, 0xfc, 0x98, 0x2a, 0x8d, 0x32, 0x78, 0x0a, 0x35, 0x9a, 0x87, 0xde, 0xd0, 0x1b, 0xb5, - 0x67, 0xcd, 0xe3, 0x61, 0x50, 0x9b, 0xbf, 0x25, 0x35, 0x9a, 0x07, 0x13, 0xe8, 0xac, 0xa5, 0xc8, - 0x96, 0x69, 0x9e, 0x4b, 0x54, 0x2a, 0xac, 0x95, 0x8c, 0xde, 0xf1, 0x30, 0xf0, 0xdf, 0x91, 0xc5, - 0xe5, 0xd4, 0xc2, 0xc4, 0x37, 0x24, 0x57, 0x04, 0x17, 0xd0, 0x96, 0x98, 0x53, 0xb5, 0xdc, 0x4a, - 0x16, 0xd6, 0x4b, 0x41, 0xe7, 0x78, 0x18, 0xb4, 0x88, 0x01, 0x6f, 0xc8, 0x07, 0xd2, 0x2a, 0xaf, - 0x6f, 0x24, 0x8b, 0xe6, 0xd0, 0xbd, 0xe4, 0x45, 0x81, 0x99, 0x26, 0xf8, 0x6d, 0x8b, 0x4a, 0x3f, - 0x6a, 0x64, 0x00, 0x7e, 0xc6, 0xb6, 0xc6, 0xeb, 0xf2, 0x2b, 0xee, 0xad, 0x0f, 0x02, 0x0e, 0xba, - 0xc2, 0x7d, 0xf4, 0x09, 0x7a, 0x55, 0x2b, 0x25, 0x78, 0xa1, 0x30, 0x98, 0x42, 0x73, 0x53, 0x8e, - 0x57, 0xf6, 0xf3, 0x27, 0x17, 0xf1, 0x5f, 0x83, 0x8a, 0x6d, 0x1e, 0xc4, 0x09, 0xa3, 0x5f, 0x1e, - 0x34, 0xae, 0x79, 0x8e, 0x8f, 0xfa, 0x0a, 0xa0, 0x61, 0xb2, 0x71, 0x86, 0xca, 0xb3, 0xf1, 0x2a, - 0x24, 0xdd, 0xa5, 0x1a, 0x4b, 0xaf, 0x75, 0xeb, 0xd5, 0x41, 0x57, 0xb8, 0x37, 0x04, 0x46, 0x95, - 0xc6, 0x62, 0x29, 0xb8, 0xd4, 0x61, 0x63, 0xe8, 0x8d, 0x1a, 0x04, 0x2c, 0xb4, 0xe0, 0x52, 0x07, - 0xe7, 0xd0, 0x5b, 0xa7, 0x1a, 0xef, 0xd2, 0x7d, 0x95, 0xfc, 0x59, 0xd9, 0xa5, 0xeb, 0x60, 0x97, - 0x75, 0xd4, 0x85, 0x8e, 0xb1, 0xa7, 0x5c, 0x7c, 0xd1, 0x35, 0xfc, 0xef, 0x6a, 0x97, 0xc1, 0x1b, - 0x38, 0x2b, 0x0c, 0x10, 0x7a, 0xc3, 0xfa, 0xc8, 0x9f, 0x9c, 0x9f, 0x10, 0x81, 0x69, 0x40, 0xac, - 0x2a, 0xfa, 0xe9, 0x41, 0x63, 0x81, 0xf8, 0x60, 0x26, 0xef, 0xc1, 0x4c, 0xcf, 0x00, 0xc4, 0x76, - 0xc5, 0x68, 0xf6, 0xc7, 0xfb, 0xb4, 0x2d, 0x62, 0xae, 0x13, 0xf0, 0x53, 0xc6, 0xf8, 0x1d, 0xe6, - 0x4b, 0x2a, 0x54, 0x58, 0x1f, 0xd6, 0x47, 0xed, 0x59, 0xf7, 0x78, 0x18, 0xc0, 0xd4, 0xc2, 0xf3, - 0x85, 0x22, 0xe0, 0x28, 0x73, 0xa1, 0x82, 0x3e, 0xb4, 0xb0, 0xc8, 0x05, 0xa7, 0x85, 0x0d, 0xa8, - 0x4d, 0xaa, 0x7a, 0xf2, 0x1d, 0x5a, 0xef, 0x9d, 0xe9, 0x40, 0xc0, 0x7f, 0xee, 0xdd, 0x83, 0xf1, - 0x09, 0xc3, 0xdd, 0xff, 0x6e, 0xfd, 0xc9, 0xbf, 0x48, 0x6c, 0xa4, 0xb3, 0xf8, 0xf3, 0x8b, 0x93, - 0x57, 0xf5, 0xf5, 0x6e, 0xbc, 0x6a, 0x96, 0xcb, 0xf6, 0xf2, 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, - 0x41, 0x0d, 0xf8, 0xa1, 0xe1, 0x03, 0x00, 0x00, + // 552 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0xcd, 0x6a, 0xdb, 0x4c, + 0x14, 0x45, 0xb6, 0xe3, 0xd8, 0x57, 0x4e, 0x02, 0xc3, 0xc7, 0x87, 0x08, 0x14, 0x39, 0xda, 0xd4, + 0x29, 0x41, 0xc2, 0xea, 0xa6, 0x50, 0xba, 0x88, 0x53, 0x48, 0x45, 0xda, 0xa0, 0x0e, 0xcd, 0xa6, + 0x1b, 0x33, 0x91, 0x06, 0x67, 0x1a, 0x45, 0x33, 0x9d, 0x19, 0x3b, 0x08, 0x0a, 0x7d, 0xb3, 0x3e, + 0x46, 0x97, 0x5a, 0xe8, 0x49, 0x8a, 0xa4, 0xb1, 0x49, 0x16, 0x69, 0x5d, 0xe8, 0x6e, 0xee, 0xd1, + 0x39, 0x57, 0xe7, 0x9e, 0xf9, 0x81, 0x57, 0x0b, 0xa6, 0x6f, 0x96, 0xd7, 0x7e, 0xc2, 0xef, 0x02, + 0xa5, 0x69, 0x96, 0x11, 0x29, 0x24, 0xff, 0x42, 0x13, 0x1d, 0xdc, 0x50, 0x76, 0x97, 0x92, 0x2c, + 0x0b, 0x88, 0x60, 0xc1, 0x6a, 0xba, 0xa9, 0x7d, 0x21, 0xb9, 0xe6, 0xe8, 0x88, 0x71, 0xff, 0xb1, + 0xc2, 0xdf, 0x30, 0x88, 0x60, 0xfe, 0x6a, 0x7a, 0xf8, 0xdf, 0x82, 0x2f, 0x78, 0xc3, 0x0e, 0xea, + 0x55, 0x2b, 0xf4, 0xbe, 0x43, 0xff, 0x03, 0x51, 0x9a, 0x4a, 0xf4, 0x3f, 0x74, 0x58, 0xea, 0x58, + 0x63, 0x6b, 0x32, 0x9c, 0xf5, 0xab, 0xd2, 0xed, 0x44, 0x6f, 0x71, 0x87, 0xa5, 0x28, 0x84, 0xd1, + 0x42, 0x8a, 0x64, 0x4e, 0xd2, 0x54, 0x52, 0xa5, 0x9c, 0x4e, 0xc3, 0x38, 0xa8, 0x4a, 0xd7, 0x3e, + 0xc7, 0xf1, 0xd9, 0x69, 0x0b, 0x63, 0xbb, 0x26, 0x99, 0x02, 0x1d, 0xc3, 0x50, 0xd2, 0x94, 0xa9, + 0xf9, 0x52, 0x66, 0x4e, 0xb7, 0x11, 0x8c, 0xaa, 0xd2, 0x1d, 0xe0, 0x1a, 0xbc, 0xc2, 0xef, 0xf1, + 0xa0, 0xf9, 0x7c, 0x25, 0x33, 0x2f, 0x82, 0xfd, 0x33, 0x9e, 0xe7, 0x34, 0xd1, 0x98, 0x7e, 0x5d, + 0x52, 0xa5, 0x9f, 0x34, 0xe2, 0x82, 0x9d, 0x64, 0xcb, 0xda, 0xeb, 0xfc, 0x96, 0x16, 0xad, 0x0f, + 0x0c, 0x06, 0xba, 0xa0, 0x85, 0xf7, 0x09, 0x0e, 0x36, 0xad, 0x94, 0xe0, 0xb9, 0xa2, 0xe8, 0x14, + 0xfa, 0x77, 0xcd, 0x78, 0x4d, 0x3f, 0x3b, 0x3c, 0xf6, 0xff, 0x18, 0x94, 0xdf, 0xe6, 0x81, 0x8d, + 0xd0, 0x8b, 0x60, 0xf7, 0x82, 0x16, 0x31, 0x61, 0xb2, 0x76, 0x20, 0x24, 0x5b, 0x11, 0x4d, 0x1b, + 0x07, 0x56, 0xeb, 0xc0, 0x40, 0x17, 0xb4, 0x40, 0xcf, 0x00, 0xc4, 0xf2, 0x3a, 0x63, 0xc9, 0x03, + 0x87, 0xc3, 0x16, 0xa9, 0x0d, 0xfe, 0xb4, 0xa0, 0x77, 0xc9, 0x53, 0xfa, 0xe4, 0x88, 0x08, 0x7a, + 0x75, 0xcc, 0x46, 0xd9, 0xac, 0xd1, 0x47, 0xd8, 0xbd, 0xa5, 0x85, 0x20, 0x4c, 0x36, 0x49, 0xda, + 0xe1, 0x8b, 0x2d, 0x66, 0x30, 0x8e, 0x67, 0x76, 0x55, 0xba, 0x6b, 0xfb, 0x78, 0xdd, 0x07, 0x9d, + 0x00, 0x2c, 0x88, 0xa6, 0xf7, 0xa4, 0x98, 0x33, 0xe1, 0xf4, 0x1a, 0x1b, 0x7b, 0x55, 0xe9, 0x0e, + 0xcf, 0x5b, 0x34, 0x8a, 0xf1, 0xd0, 0x10, 0x22, 0x81, 0x8e, 0x60, 0xb4, 0x66, 0x0b, 0x2e, 0xb5, + 0xb3, 0x33, 0xb6, 0x26, 0x3d, 0x6c, 0x1b, 0x2c, 0xe6, 0x52, 0x7b, 0xfb, 0x30, 0xaa, 0xe7, 0x52, + 0x66, 0x0b, 0xbd, 0x4b, 0xd8, 0x33, 0xb5, 0xd9, 0x87, 0x37, 0xb0, 0x93, 0xd7, 0x80, 0x63, 0x8d, + 0xbb, 0x13, 0x3b, 0x7c, 0xbe, 0xc5, 0x08, 0x75, 0x03, 0xdc, 0xaa, 0xbc, 0x1f, 0x16, 0xf4, 0x62, + 0xfa, 0x9b, 0x43, 0xfa, 0x20, 0xa4, 0xce, 0x3f, 0x0a, 0x29, 0x00, 0x9b, 0x64, 0x19, 0xbf, 0xa7, + 0xe9, 0x9c, 0x09, 0xe5, 0x74, 0xc7, 0xdd, 0xc9, 0x70, 0xb6, 0x5f, 0x95, 0x2e, 0x9c, 0xb6, 0x70, + 0x14, 0x2b, 0x0c, 0x86, 0x12, 0x09, 0x85, 0x0e, 0x61, 0x40, 0xf3, 0x54, 0x70, 0x96, 0xeb, 0x36, + 0x53, 0xbc, 0xa9, 0xc3, 0x6f, 0x30, 0x78, 0x67, 0xfe, 0x8e, 0x04, 0xec, 0x9a, 0x63, 0x8a, 0xa6, + 0x5b, 0xb8, 0x7c, 0x7c, 0x3b, 0x0e, 0xc3, 0xbf, 0x91, 0xb4, 0xe9, 0xcf, 0xfc, 0xcf, 0x27, 0x5b, + 0xbf, 0x2c, 0xaf, 0x57, 0xd3, 0xeb, 0x7e, 0xf3, 0x36, 0xbc, 0xfc, 0x15, 0x00, 0x00, 0xff, 0xff, + 0x5f, 0xf4, 0xa8, 0x6a, 0x90, 0x04, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/api/v1/heimdall.proto b/api/v1/heimdall.proto index 038b3d5..51aca5a 100644 --- a/api/v1/heimdall.proto +++ b/api/v1/heimdall.proto @@ -29,12 +29,17 @@ message ConnectResponse { Master master = 1; } +message KeyPair { + string private_key = 1; + string public_key = 2; +} + message Node { string id = 1 [(gogoproto.customname) = "ID"]; string addr = 2; - string private_key = 3; - uint64 listen_port = 4; - string gateway_address = 5; + KeyPair keypair = 3 [(gogoproto.customname) = "KeyPair"]; + string gateway_ip = 4 [(gogoproto.customname) = "GatewayIP"]; + uint64 gateway_port = 5; } message NodesRequest {} @@ -44,8 +49,8 @@ message NodesResponse { } message Peer { - string private_key = 1; - string public_key = 2; + string id = 1 [(gogoproto.customname) = "ID"]; + KeyPair keypair = 2 [(gogoproto.customname) = "KeyPair"]; repeated string allowed_ips = 3 [(gogoproto.customname) = "AllowedIPs"]; string endpoint = 4; } diff --git a/cmd/heimdall/main.go b/cmd/heimdall/main.go index cc8c88e..c6f54c4 100644 --- a/cmd/heimdall/main.go +++ b/cmd/heimdall/main.go @@ -89,11 +89,17 @@ func main() { Value: "10.254.0.0/16", EnvVar: "HEIMDALL_PEER_NETWORK", }, + cli.StringFlag{ + Name: "gateway-ip", + Usage: "IP used for peer communication", + Value: heimdall.GetIP(), + EnvVar: "HEIMDALL_GATEWAY_IP", + }, cli.IntFlag{ - Name: "wireguard-port", - Usage: "wireguard port for peers", + Name: "gateway-port", + Usage: "port for peer communication", Value: 10100, - EnvVar: "HEIMDALL_WIREGUARD_PORT", + EnvVar: "HEIMDALL_GATEWAY_PORT", }, } app.Before = func(c *cli.Context) error { diff --git a/cmd/heimdall/run.go b/cmd/heimdall/run.go index f751704..76e8fc9 100644 --- a/cmd/heimdall/run.go +++ b/cmd/heimdall/run.go @@ -46,7 +46,8 @@ func runServer(cx *cli.Context) error { GRPCPeerAddress: cx.String("peer"), ClusterKey: cx.String("cluster-key"), PeerNetwork: cx.String("peer-network"), - WireguardPort: cx.Int("wireguard-port"), + GatewayIP: cx.String("gateway-ip"), + GatewayPort: cx.Int("gateway-port"), RedisURL: cx.String("redis-url"), AdvertiseRedisURL: cx.String("advertise-redis-url"), } diff --git a/config.go b/config.go index 04f97a7..d44f0da 100644 --- a/config.go +++ b/config.go @@ -33,8 +33,10 @@ type Config struct { ClusterKey string // PeerNetwork is the subnet that will be used for cluster peers PeerNetwork string - // WireguardPort is the peer port used for Wireguard - WireguardPort int + // GatewayIP is the IP used for peer communication + GatewayIP string + // GatewayPort is the port used for peer communication + GatewayPort int // RedisURL is the uri to the redis backend RedisURL string // AdvertiseRedisURL is the uri to the public redis backend diff --git a/server/net.go b/server/net.go new file mode 100644 index 0000000..242807b --- /dev/null +++ b/server/net.go @@ -0,0 +1,187 @@ +/* + Copyright 2019 Stellar Project + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in the + Software without restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included in all copies + or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE + FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +package server + +import ( + "context" + "fmt" + "net" + "strings" + + "github.com/gomodule/redigo/redis" +) + +type subnetRange struct { + Start net.IP + End net.IP + Subnet *net.IPNet +} + +func (s *Server) getIPs(ctx context.Context) (map[string]net.IP, error) { + values, err := redis.StringMap(s.local(ctx, "HGETALL", ipsKey)) + if err != nil { + return nil, err + } + + ips := make(map[string]net.IP, len(values)) + for id, val := range values { + ip := net.ParseIP(string(val)) + ips[id] = ip + } + return ips, nil +} + +func (s *Server) getIP(ctx context.Context, id string) (net.IP, error) { + allIPs, err := s.getIPs(ctx) + if err != nil { + return nil, err + } + + if ip, exists := allIPs[id]; exists { + return ip, nil + } + return nil, nil +} + +func (s *Server) getOrAllocateIP(ctx context.Context, id string) (net.IP, *net.IPNet, error) { + r, err := s.parseSubnetRange(s.cfg.PeerNetwork) + if err != nil { + return nil, nil, err + } + + ip, err := s.getIP(ctx, id) + if err != nil { + return nil, nil, err + } + + if ip != nil { + return ip, r.Subnet, nil + } + + ip, err = s.allocateIP(ctx, id, r) + if err != nil { + return nil, nil, err + } + + return ip, r.Subnet, nil +} + +func (s *Server) allocateIP(ctx context.Context, id string, r *subnetRange) (net.IP, error) { + reservedIPs, err := s.getIPs(ctx) + if err != nil { + return nil, err + } + + if ip, exists := reservedIPs[id]; exists { + return ip, nil + } + + lookup := map[string]string{} + for id, ip := range reservedIPs { + lookup[ip.String()] = id + } + for ip := r.Start; !ip.Equal(r.End); s.nextIP(ip) { + // filter out network, gateway and broadcast + if !s.validIP(ip) { + continue + } + if _, exists := lookup[ip.String()]; exists { + // ip already reserved + continue + } + + // save + if _, err := s.master(ctx, "HSET", ipsKey, id, ip.String()); err != nil { + return nil, err + } + return ip, nil + } + + return nil, fmt.Errorf("no available IPs") +} + +func (s *Server) releaseIP(ctx context.Context, id string) error { + ip, err := s.getIP(ctx, id) + if err != nil { + return err + } + + if ip != nil { + if _, err := s.master(ctx, "HDEL", ipsKey, id); err != nil { + return err + } + } + return nil +} + +func (s *Server) nextIP(ip net.IP) { + for j := len(ip) - 1; j >= 0; j-- { + ip[j]++ + if ip[j] > 0 { + break + } + } +} + +func (s *Server) validIP(ip net.IP) bool { + v := ip[len(ip)-1] + switch v { + case 0, 1, 255: + return false + } + return true +} + +// parseSubnetRange parses the subnet range +// format can either be a subnet like 10.0.0.0/8 or range like 10.0.0.100-10.0.0.200/24 +func (s *Server) parseSubnetRange(subnet string) (*subnetRange, error) { + parts := strings.Split(subnet, "-") + if len(parts) == 1 { + ip, sub, err := net.ParseCIDR(parts[0]) + if err != nil { + return nil, err + } + + end := make(net.IP, len(ip)) + copy(end, ip) + end[len(end)-1] = 254 + return &subnetRange{ + Start: ip, + End: end, + Subnet: sub, + }, nil + } + if len(parts) > 2 || !strings.Contains(subnet, "/") { + return nil, fmt.Errorf("invalid range specified; expect format 10.0.0.100-10.0.0.200/24") + } + start := net.ParseIP(parts[0]) + end, sub, err := net.ParseCIDR(parts[1]) + if err != nil { + return nil, err + } + + return &subnetRange{ + Start: start, + End: end, + Subnet: sub, + }, nil +} diff --git a/server/node.go b/server/node.go index 1d9bd99..a76e805 100644 --- a/server/node.go +++ b/server/node.go @@ -23,8 +23,6 @@ package server import ( "context" - "crypto/sha256" - "fmt" "net/url" "strings" "time" @@ -33,31 +31,34 @@ import ( "github.com/gomodule/redigo/redis" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/stellarproject/heimdall" v1 "github.com/stellarproject/heimdall/api/v1" ) func (s *Server) configureNode() error { ctx := context.Background() - nodes, err := redis.Strings(s.local(ctx, "KEYS", s.getNodeKey("*"))) + nodeKeys, err := redis.Strings(s.local(ctx, "KEYS", s.getNodeKey("*"))) if err != nil { return err } // attempt to connect to existing - if len(nodes) > 0 { - for _, node := range nodes { - addr, err := redis.String(s.local(ctx, "GET", node)) + if len(nodeKeys) > 0 { + for _, nodeKey := range nodeKeys { + nodeData, err := redis.Bytes(s.local(ctx, "GET", nodeKey)) if err != nil { logrus.Warn(err) continue } + var node v1.Node + if err := proto.Unmarshal(nodeData, &node); err != nil { + return err + } // ignore self - if addr == s.cfg.GRPCAddress { + if node.Addr == s.cfg.GRPCAddress { continue } - logrus.Infof("attempting to join existing node %s", addr) - c, err := s.getClient(addr) + logrus.Infof("attempting to join existing node %s", node.Addr) + c, err := s.getClient(node.Addr) if err != nil { logrus.Warn(err) continue @@ -125,9 +126,9 @@ func (s *Server) disableReplica() { } func (s *Server) replicaMonitor() { - logrus.Debugf("starting replica monitor: ttl=%s", heartbeatInterval) + logrus.Debugf("starting replica monitor: ttl=%s", masterHeartbeatInterval) s.replicaCh = make(chan struct{}, 1) - t := time.NewTicker(heartbeatInterval) + t := time.NewTicker(masterHeartbeatInterval) go func() { for range t.C { if _, err := redis.Bytes(s.local(context.Background(), "GET", masterKey)); err != nil { @@ -149,9 +150,9 @@ func (s *Server) replicaMonitor() { } func (s *Server) masterHeartbeat() { - logrus.Debugf("starting master heartbeat: ttl=%s", heartbeatInterval) + logrus.Debugf("starting master heartbeat: ttl=%s", masterHeartbeatInterval) // initial update - ctx, cancel := context.WithTimeout(context.Background(), heartbeatInterval) + ctx, cancel := context.WithTimeout(context.Background(), masterHeartbeatInterval) defer cancel() logrus.Infof("cluster master key=%s", s.cfg.ClusterKey) @@ -159,7 +160,7 @@ func (s *Server) masterHeartbeat() { logrus.Error(err) } - t := time.NewTicker(heartbeatInterval) + t := time.NewTicker(masterHeartbeatInterval) for range t.C { if err := s.updateMasterInfo(ctx); err != nil { logrus.Error(err) @@ -218,83 +219,37 @@ func (s *Server) updateMasterInfo(ctx context.Context) error { return errors.Wrap(err, "error setting master info") } - if _, err := s.master(ctx, "EXPIRE", masterKey, int(heartbeatInterval.Seconds())); err != nil { + if _, err := s.master(ctx, "EXPIRE", masterKey, int(masterHeartbeatInterval.Seconds())); err != nil { return errors.Wrap(err, "error setting expire for master info") } return nil } -func (s *Server) updatePeerInfo(ctx context.Context) error { - // check for existing key - endpoint := fmt.Sprintf("%s:%d", heimdall.GetIP(), s.cfg.WireguardPort) - - peer, err := s.getPeerInfo(ctx) - if err != nil { - return err - } - - // TODO: build allowedIPs from routes and peer network - allowedIPs := []string{s.cfg.PeerNetwork} - ipHash := hashIPs(allowedIPs) - - // check cached info and validate - if peer != nil { - peerIPHash := hashIPs(peer.AllowedIPs) - // if endpoint is the same assume unchanged - if peer.Endpoint == endpoint && peerIPHash == ipHash { - logrus.Debugf("peer info: public=%s endpoint=%s", peer.PublicKey, peer.Endpoint) - return nil - } - } - - privateKey, publicKey, err := generateWireguardKeys(ctx) - if err != nil { - return err - } - // TODO: allowed IPs - n := &v1.Peer{ - PrivateKey: privateKey, - PublicKey: publicKey, - AllowedIPs: allowedIPs, - Endpoint: endpoint, - } - - logrus.Debugf("peer info: public=%s endpoint=%s", n.PublicKey, n.Endpoint) - data, err := proto.Marshal(n) - if err != nil { - return err - } - key := s.getPeerKey(s.cfg.ID) - if _, err := s.master(ctx, "SET", key, data); err != nil { - return err - } - return nil -} - -func (s *Server) getPeerInfo(ctx context.Context) (*v1.Peer, error) { - key := s.getPeerKey(s.cfg.ID) - data, err := redis.Bytes(s.local(ctx, "GET", key)) - if err != nil { - if err == redis.ErrNil { - return nil, nil - } - return nil, err - } - var peer v1.Peer - if err := proto.Unmarshal(data, &peer); err != nil { - return nil, err - } - - return &peer, nil -} - -func (s *Server) nodeHeartbeat() { +func (s *Server) nodeHeartbeat(ctx context.Context) { logrus.Debugf("starting node heartbeat: ttl=%s", nodeHeartbeatInterval) - ctx := context.Background() t := time.NewTicker(nodeHeartbeatInterval) key := s.getNodeKey(s.cfg.ID) for range t.C { - if _, err := s.master(ctx, "SET", key, s.cfg.GRPCAddress); err != nil { + keyPair, err := s.getOrCreateKeyPair(ctx, s.cfg.ID) + if err != nil { + logrus.Error(err) + continue + } + node := &v1.Node{ + ID: s.cfg.ID, + Addr: s.cfg.GRPCAddress, + KeyPair: keyPair, + GatewayIP: s.cfg.GatewayIP, + GatewayPort: uint64(s.cfg.GatewayPort), + } + + data, err := proto.Marshal(node) + if err != nil { + logrus.Error(err) + continue + } + + if _, err := s.master(ctx, "SET", key, data); err != nil { logrus.Error(err) continue } @@ -305,11 +260,3 @@ func (s *Server) nodeHeartbeat() { } } } - -func hashIPs(ips []string) string { - h := sha256.New() - for _, ip := range ips { - h.Write([]byte(ip)) - } - return fmt.Sprintf("%x", h.Sum(nil)) -} diff --git a/server/peer.go b/server/peer.go new file mode 100644 index 0000000..c3ed9d2 --- /dev/null +++ b/server/peer.go @@ -0,0 +1,190 @@ +/* + Copyright 2019 Stellar Project + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in the + Software without restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included in all copies + or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE + FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +package server + +import ( + "context" + "crypto/sha256" + "fmt" + "io/ioutil" + "os" + "time" + + "github.com/gogo/protobuf/proto" + "github.com/gomodule/redigo/redis" + "github.com/sirupsen/logrus" + v1 "github.com/stellarproject/heimdall/api/v1" +) + +func (s *Server) updatePeerInfo(ctx context.Context) error { + keypair, err := s.getOrCreateKeyPair(ctx, s.cfg.ID) + if err != nil { + return err + } + + endpoint := fmt.Sprintf("%s:%d", s.cfg.GatewayIP, s.cfg.GatewayPort) + + // TODO: build allowedIPs from routes and peer network + allowedIPs := []string{s.cfg.PeerNetwork} + + n := &v1.Peer{ + ID: s.cfg.ID, + KeyPair: keypair, + AllowedIPs: allowedIPs, + Endpoint: endpoint, + } + + data, err := proto.Marshal(n) + if err != nil { + return err + } + key := s.getPeerKey(s.cfg.ID) + if _, err := s.master(ctx, "SET", key, data); err != nil { + return err + } + + logrus.Debugf("peer info: endpoint=%s allowedips=%+v", n.Endpoint, n.Endpoint) + + return nil +} + +func (s *Server) getPeerInfo(ctx context.Context, id string) (*v1.Peer, error) { + key := s.getPeerKey(id) + data, err := redis.Bytes(s.local(ctx, "GET", key)) + if err != nil { + if err == redis.ErrNil { + return nil, nil + } + return nil, err + } + var peer v1.Peer + if err := proto.Unmarshal(data, &peer); err != nil { + return nil, err + } + + return &peer, nil +} + +func (s *Server) updatePeerConfig(ctx context.Context) { + logrus.Debugf("starting peer config updater: ttl=%s", peerConfigUpdateInterval) + t := time.NewTicker(peerConfigUpdateInterval) + + configHash := "" + + for range t.C { + uctx, cancel := context.WithTimeout(ctx, peerConfigUpdateInterval) + peerKeys, err := redis.Strings(s.local(uctx, "KEYS", s.getPeerKey("*"))) + if err != nil { + logrus.Error(err) + cancel() + continue + } + var peers []*v1.Peer + for _, peerKey := range peerKeys { + peerData, err := redis.Bytes(s.local(uctx, "GET", peerKey)) + if err != nil { + logrus.Error(err) + cancel() + continue + } + var p v1.Peer + if err := proto.Unmarshal(peerData, &p); err != nil { + logrus.Error(err) + cancel() + continue + } + + // do not add self as a peer + if p.ID == s.cfg.ID { + continue + } + + peers = append(peers, &p) + } + + keyPair, err := s.getOrCreateKeyPair(ctx, s.cfg.ID) + if err != nil { + logrus.Error(err) + cancel() + continue + } + + gatewayIP, _, err := s.getOrAllocateIP(ctx, s.cfg.ID) + if err != nil { + logrus.Error(err) + cancel() + continue + } + wireguardCfg := &wireguardConfig{ + Iface: defaultWireguardInterface, + PrivateKey: keyPair.PrivateKey, + ListenPort: s.cfg.GatewayPort, + Address: gatewayIP.String() + "/32", + Peers: peers, + } + + tmpCfg, err := generateNodeWireguardConfig(wireguardCfg) + if err != nil { + logrus.Error(err) + cancel() + continue + } + + h, err := hashConfig(tmpCfg) + if err != nil { + logrus.Error(err) + cancel() + continue + } + + // if config has not change skip update + if h == configHash { + continue + } + + logrus.Debugf("updating peer config to version %s", h) + // update wireguard config + if err := os.Rename(tmpCfg, wireguardConfigPath); err != nil { + logrus.Error(err) + cancel() + continue + } + // reload wireguard + if err := restartWireguardTunnel(ctx); err != nil { + logrus.Error(err) + cancel() + continue + } + configHash = h + } +} + +func hashConfig(cfgPath string) (string, error) { + peerData, err := ioutil.ReadFile(cfgPath) + if err != nil { + return "", err + } + + h := sha256.New() + h.Write(peerData) + return fmt.Sprintf("%x", h.Sum(nil)), nil +} diff --git a/server/server.go b/server/server.go index 9ffb4d7..4af1468 100644 --- a/server/server.go +++ b/server/server.go @@ -29,6 +29,7 @@ import ( "runtime/pprof" "time" + "github.com/gogo/protobuf/proto" ptypes "github.com/gogo/protobuf/types" "github.com/gomodule/redigo/redis" "github.com/pkg/errors" @@ -42,18 +43,21 @@ import ( const ( masterKey = "heimdall:master" clusterKey = "heimdall:key" + keypairsKey = "heimdall:keypairs" nodesKey = "heimdall:nodes" nodeJoinKey = "heimdall:join" peersKey = "heimdall:peers" + ipsKey = "heimdall:ips" wireguardConfigPath = "/etc/wireguard/darknet.conf" ) var ( - empty = &ptypes.Empty{} - heartbeatInterval = time.Second * 5 - nodeHeartbeatInterval = time.Second * 60 - nodeHeartbeatExpiry = 86400 + empty = &ptypes.Empty{} + masterHeartbeatInterval = time.Second * 5 + nodeHeartbeatInterval = time.Second * 60 + nodeHeartbeatExpiry = 86400 + peerConfigUpdateInterval = time.Second * 10 ) type Server struct { @@ -122,11 +126,19 @@ func (s *Server) Run() error { } } + if _, err := s.getOrCreateKeyPair(ctx, s.cfg.ID); err != nil { + return err + } + if err := s.updatePeerInfo(ctx); err != nil { return err } - go s.nodeHeartbeat() + // start node heartbeat to update in redis + go s.nodeHeartbeat(ctx) + + // start peer config updater to configure wireguard as peers join + go s.updatePeerConfig(ctx) // start listener for pub/sub errCh := make(chan error, 1) @@ -170,6 +182,39 @@ func getPool(u string) *redis.Pool { return pool } +func (s *Server) getOrCreateKeyPair(ctx context.Context, id string) (*v1.KeyPair, error) { + key := s.getKeyPairKey(id) + keyData, err := redis.Bytes(s.master(ctx, "GET", key)) + if err != nil { + if err != redis.ErrNil { + return nil, err + } + logrus.Debugf("generating new keypair for %s", s.cfg.ID) + privateKey, publicKey, err := generateWireguardKeys(ctx) + if err != nil { + return nil, err + } + keyPair := &v1.KeyPair{ + PrivateKey: privateKey, + PublicKey: publicKey, + } + data, err := proto.Marshal(keyPair) + if err != nil { + return nil, err + } + if _, err := s.master(ctx, "SET", key, data); err != nil { + return nil, err + } + return keyPair, nil + } + + var keyPair v1.KeyPair + if err := proto.Unmarshal(keyData, &keyPair); err != nil { + return nil, err + } + return &keyPair, nil +} + func (s *Server) getNodeKey(id string) string { return fmt.Sprintf("%s:%s", nodesKey, id) } @@ -178,6 +223,10 @@ func (s *Server) getPeerKey(id string) string { return fmt.Sprintf("%s:%s", peersKey, id) } +func (s *Server) getKeyPairKey(id string) string { + return fmt.Sprintf("%s:%s", keypairsKey, id) +} + func (s *Server) getClient(addr string) (*client.Client, error) { return client.NewClient(s.cfg.ID, addr) } diff --git a/server/wireguard.go b/server/wireguard.go index fbff411..e87e252 100644 --- a/server/wireguard.go +++ b/server/wireguard.go @@ -32,12 +32,14 @@ import ( "strings" "text/template" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" v1 "github.com/stellarproject/heimdall/api/v1" ) const ( - defaultInterface = "darknet" - wireguardTemplate = `# managed by heimdall + defaultWireguardInterface = "darknet" + wireguardTemplate = `# managed by heimdall [Interface] PrivateKey = {{ .PrivateKey }} ListenPort = {{ .ListenPort }} @@ -46,7 +48,7 @@ PostUp = iptables -A FORWARD -i {{ .Iface }} -j ACCEPT; iptables -t nat -A POSTR PostDown = iptables -D FORWARD -i {{ .Iface }} -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE; ip6tables -D FORWARD -i {{ .Iface }} -j ACCEPT; ip6tables -t nat -D POSTROUTING -o eth0 -j MASQUERADE {{ range .Peers }} [Peer] -PublicKey = {{ .PublicKey }} +PublicKey = {{ .KeyPair.PublicKey }} AllowedIPs = {{ allowedIPs .AllowedIPs }} Endpoint = {{ .Endpoint }} {{ end }} @@ -65,28 +67,28 @@ type wireguardConfig struct { Peers []*v1.Peer } -func generateNodeWireguardConfig(cfg *wireguardConfig) (*os.File, error) { +func generateNodeWireguardConfig(cfg *wireguardConfig) (string, error) { f, err := ioutil.TempFile("", "heimdall-wireguard-") if err != nil { - return nil, err + return "", err } t, err := template.New("wireguard").Funcs(template.FuncMap{ "allowedIPs": allowedIPs, }).Parse(wireguardTemplate) if err != nil { - return nil, err + return "", err } if err := os.MkdirAll(filepath.Dir(wireguardConfigPath), 0755); err != nil { - return nil, err + return "", err } if err := t.Execute(f, cfg); err != nil { - return nil, err + return "", err } f.Close() - return f, nil + return f.Name(), nil } func generateWireguardKeys(ctx context.Context) (string, string, error) { @@ -105,6 +107,20 @@ func generateWireguardKeys(ctx context.Context) (string, string, error) { return privateKey, publicKey, nil } +func restartWireguardTunnel(ctx context.Context) error { + tunnelName := strings.Replace(filepath.Base(wireguardConfigPath), filepath.Ext(filepath.Base(wireguardConfigPath)), "", 1) + logrus.Infof("restarting tunnel %s", tunnelName) + d, err := wgquick(ctx, "down", tunnelName) + if err != nil { + return errors.Wrap(err, string(d)) + } + u, err := wgquick(ctx, "up", tunnelName) + if err != nil { + return errors.Wrap(err, string(u)) + } + return nil +} + func wg(ctx context.Context, in io.Reader, args ...string) ([]byte, error) { cmd := exec.CommandContext(ctx, "wg", args...) if in != nil { @@ -112,3 +128,8 @@ func wg(ctx context.Context, in io.Reader, args ...string) ([]byte, error) { } return cmd.CombinedOutput() } + +func wgquick(ctx context.Context, args ...string) ([]byte, error) { + cmd := exec.CommandContext(ctx, "wg-quick", args...) + return cmd.CombinedOutput() +} diff --git a/server/wireguard_test.go b/server/wireguard_test.go index bbae951..666cebe 100644 --- a/server/wireguard_test.go +++ b/server/wireguard_test.go @@ -45,25 +45,27 @@ Endpoint = 100.100.100.100:10000 ` cfg := &wireguardConfig{ - Iface: "darknet", + Iface: defaultWireguardInterface, PrivateKey: "SERVER-PRIVATE-KEY", ListenPort: 10000, Address: "1.2.3.4:10000", Peers: []*v1.Peer{ { - PrivateKey: "PEER-PRIVATE-KEY", - PublicKey: "PEER-PUBLIC-KEY", + KeyPair: &v1.KeyPair{ + PrivateKey: "PEER-PRIVATE-KEY", + PublicKey: "PEER-PUBLIC-KEY", + }, AllowedIPs: []string{"10.100.0.0/24", "10.254.0.0/16"}, Endpoint: "100.100.100.100:10000", }, }, } - f, err := generateWireguardConfig(cfg) + configPath, err := generateNodeWireguardConfig(cfg) if err != nil { t.Fatal(err) } - defer os.Remove(f.Name()) - data, err := ioutil.ReadFile(f.Name()) + defer os.Remove(configPath) + data, err := ioutil.ReadFile(configPath) if err != nil { t.Fatal(err) }