add wireguard connectivity between nodes

Signed-off-by: Evan Hazlett <ejhazlett@gmail.com>
This commit is contained in:
Evan Hazlett 2019-10-04 09:46:03 -04:00
parent 2e34c8746e
commit c0515d4802
No known key found for this signature in database
GPG key ID: A519480096146526
11 changed files with 640 additions and 180 deletions

View file

@ -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.

View file

@ -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;
}

View file

@ -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 {

View file

@ -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"),
}

View file

@ -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

187
server/net.go Normal file
View file

@ -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
}

View file

@ -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))
}

190
server/peer.go Normal file
View file

@ -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
}

View file

@ -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
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)
}

View file

@ -32,11 +32,13 @@ import (
"strings"
"text/template"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
v1 "github.com/stellarproject/heimdall/api/v1"
)
const (
defaultInterface = "darknet"
defaultWireguardInterface = "darknet"
wireguardTemplate = `# managed by heimdall
[Interface]
PrivateKey = {{ .PrivateKey }}
@ -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()
}

View file

@ -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{
{
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)
}