From 332378e3ff4c16a15473fa5583da277caf5c3282 Mon Sep 17 00:00:00 2001 From: Evan Hazlett Date: Mon, 7 Oct 2019 10:06:12 -0400 Subject: [PATCH] add hpeer for non-node peers Signed-off-by: Evan Hazlett --- Makefile | 7 +- api/v1/heimdall.pb.go | 562 +++++++++++++++++++++++++++----- api/v1/heimdall.proto | 33 +- client/connect.go | 42 ++- client/join.go | 42 +++ cmd/hctl/peers.go | 58 ++++ cmd/heimdall/main.go | 6 + cmd/heimdall/run.go | 1 + cmd/hpeer/main.go | 96 ++++++ cmd/hpeer/run.go | 89 +++++ config.go | 16 + peer/peer.go | 93 ++++++ peer/sync.go | 99 ++++++ server/connect.go | 96 ++++-- server/join.go | 66 ++++ server/node.go | 2 +- server/peer.go | 87 ++--- server/server.go | 39 ++- utils.go | 27 ++ server/wireguard.go => wg/wg.go | 82 +++-- 20 files changed, 1348 insertions(+), 195 deletions(-) create mode 100644 client/join.go create mode 100644 cmd/hpeer/main.go create mode 100644 cmd/hpeer/run.go create mode 100644 peer/peer.go create mode 100644 peer/sync.go create mode 100644 server/join.go rename server/wireguard.go => wg/wg.go (62%) diff --git a/Makefile b/Makefile index 8af2822..1af68ae 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,7 @@ NAMESPACE?=stellarproject IMAGE_NAMESPACE?=$(NAMESPACE) APP=heimdall CLI=hctl +PEER=hpeer REPO?=$(NAMESPACE)/$(APP) TAG?=dev BUILD?=-dev @@ -53,7 +54,7 @@ generate: bindir: @mkdir -p bin -binaries: cli daemon +binaries: cli daemon peer cli: bindir @>&2 echo " -> building cli ${COMMIT}${BUILD}" @@ -63,6 +64,10 @@ daemon: bindir @>&2 echo " -> building daemon ${COMMIT}${BUILD}" @cd cmd/$(APP) && CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) go build -installsuffix cgo -ldflags "-w -X github.com/$(REPO)/version.GitCommit=$(COMMIT) -X github.com/$(REPO)/version.Build=$(BUILD)" -o ../../bin/$(APP)$(BINARY_SUFFIX) . +peer: bindir + @>&2 echo " -> building peer ${COMMIT}${BUILD}" + @cd cmd/$(PEER) && CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) go build -installsuffix cgo -ldflags "-w -X github.com/$(REPO)/version.GitCommit=$(COMMIT) -X github.com/$(REPO)/version.Build=$(BUILD)" -o ../../bin/$(PEER)$(BINARY_SUFFIX) . + vet: @echo " -> $@" @test -z "$$(go vet ${PACKAGES} 2>&1 | tee /dev/stderr)" diff --git a/api/v1/heimdall.pb.go b/api/v1/heimdall.pb.go index e08dd99..c1530ab 100644 --- a/api/v1/heimdall.pb.go +++ b/api/v1/heimdall.pb.go @@ -82,7 +82,7 @@ func (m *Master) GetRedisURL() string { return "" } -type ConnectRequest struct { +type JoinRequest struct { ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` ClusterKey string `protobuf:"bytes,2,opt,name=cluster_key,json=clusterKey,proto3" json:"cluster_key,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` @@ -90,11 +90,94 @@ type ConnectRequest struct { XXX_sizecache int32 `json:"-"` } +func (m *JoinRequest) Reset() { *m = JoinRequest{} } +func (m *JoinRequest) String() string { return proto.CompactTextString(m) } +func (*JoinRequest) ProtoMessage() {} +func (*JoinRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b6184fc395da86b1, []int{1} +} +func (m *JoinRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_JoinRequest.Unmarshal(m, b) +} +func (m *JoinRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_JoinRequest.Marshal(b, m, deterministic) +} +func (m *JoinRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_JoinRequest.Merge(m, src) +} +func (m *JoinRequest) XXX_Size() int { + return xxx_messageInfo_JoinRequest.Size(m) +} +func (m *JoinRequest) XXX_DiscardUnknown() { + xxx_messageInfo_JoinRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_JoinRequest proto.InternalMessageInfo + +func (m *JoinRequest) GetID() string { + if m != nil { + return m.ID + } + return "" +} + +func (m *JoinRequest) GetClusterKey() string { + if m != nil { + return m.ClusterKey + } + return "" +} + +type JoinResponse struct { + Master *Master `protobuf:"bytes,1,opt,name=master,proto3" json:"master,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *JoinResponse) Reset() { *m = JoinResponse{} } +func (m *JoinResponse) String() string { return proto.CompactTextString(m) } +func (*JoinResponse) ProtoMessage() {} +func (*JoinResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b6184fc395da86b1, []int{2} +} +func (m *JoinResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_JoinResponse.Unmarshal(m, b) +} +func (m *JoinResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_JoinResponse.Marshal(b, m, deterministic) +} +func (m *JoinResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_JoinResponse.Merge(m, src) +} +func (m *JoinResponse) XXX_Size() int { + return xxx_messageInfo_JoinResponse.Size(m) +} +func (m *JoinResponse) XXX_DiscardUnknown() { + xxx_messageInfo_JoinResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_JoinResponse proto.InternalMessageInfo + +func (m *JoinResponse) GetMaster() *Master { + if m != nil { + return m.Master + } + return nil +} + +type ConnectRequest struct { + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + func (m *ConnectRequest) Reset() { *m = ConnectRequest{} } func (m *ConnectRequest) String() string { return proto.CompactTextString(m) } func (*ConnectRequest) ProtoMessage() {} func (*ConnectRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_b6184fc395da86b1, []int{1} + return fileDescriptor_b6184fc395da86b1, []int{3} } func (m *ConnectRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_ConnectRequest.Unmarshal(m, b) @@ -121,15 +204,11 @@ func (m *ConnectRequest) GetID() string { return "" } -func (m *ConnectRequest) GetClusterKey() string { - if m != nil { - return m.ClusterKey - } - return "" -} - type ConnectResponse struct { - Master *Master `protobuf:"bytes,1,opt,name=master,proto3" json:"master,omitempty"` + KeyPair *KeyPair `protobuf:"bytes,1,opt,name=keypair,proto3" json:"keypair,omitempty"` + Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` + Peers []*Peer `protobuf:"bytes,3,rep,name=peers,proto3" json:"peers,omitempty"` + DNS []string `protobuf:"bytes,4,rep,name=dns,proto3" json:"dns,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -139,7 +218,7 @@ func (m *ConnectResponse) Reset() { *m = ConnectResponse{} } func (m *ConnectResponse) String() string { return proto.CompactTextString(m) } func (*ConnectResponse) ProtoMessage() {} func (*ConnectResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_b6184fc395da86b1, []int{2} + return fileDescriptor_b6184fc395da86b1, []int{4} } func (m *ConnectResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_ConnectResponse.Unmarshal(m, b) @@ -159,9 +238,174 @@ func (m *ConnectResponse) XXX_DiscardUnknown() { var xxx_messageInfo_ConnectResponse proto.InternalMessageInfo -func (m *ConnectResponse) GetMaster() *Master { +func (m *ConnectResponse) GetKeyPair() *KeyPair { if m != nil { - return m.Master + return m.KeyPair + } + return nil +} + +func (m *ConnectResponse) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *ConnectResponse) GetPeers() []*Peer { + if m != nil { + return m.Peers + } + return nil +} + +func (m *ConnectResponse) GetDNS() []string { + if m != nil { + return m.DNS + } + return nil +} + +type AuthorizePeerRequest struct { + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *AuthorizePeerRequest) Reset() { *m = AuthorizePeerRequest{} } +func (m *AuthorizePeerRequest) String() string { return proto.CompactTextString(m) } +func (*AuthorizePeerRequest) ProtoMessage() {} +func (*AuthorizePeerRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b6184fc395da86b1, []int{5} +} +func (m *AuthorizePeerRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_AuthorizePeerRequest.Unmarshal(m, b) +} +func (m *AuthorizePeerRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_AuthorizePeerRequest.Marshal(b, m, deterministic) +} +func (m *AuthorizePeerRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_AuthorizePeerRequest.Merge(m, src) +} +func (m *AuthorizePeerRequest) XXX_Size() int { + return xxx_messageInfo_AuthorizePeerRequest.Size(m) +} +func (m *AuthorizePeerRequest) XXX_DiscardUnknown() { + xxx_messageInfo_AuthorizePeerRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_AuthorizePeerRequest proto.InternalMessageInfo + +func (m *AuthorizePeerRequest) GetID() string { + if m != nil { + return m.ID + } + return "" +} + +type DeauthorizePeerRequest struct { + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DeauthorizePeerRequest) Reset() { *m = DeauthorizePeerRequest{} } +func (m *DeauthorizePeerRequest) String() string { return proto.CompactTextString(m) } +func (*DeauthorizePeerRequest) ProtoMessage() {} +func (*DeauthorizePeerRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b6184fc395da86b1, []int{6} +} +func (m *DeauthorizePeerRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DeauthorizePeerRequest.Unmarshal(m, b) +} +func (m *DeauthorizePeerRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DeauthorizePeerRequest.Marshal(b, m, deterministic) +} +func (m *DeauthorizePeerRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_DeauthorizePeerRequest.Merge(m, src) +} +func (m *DeauthorizePeerRequest) XXX_Size() int { + return xxx_messageInfo_DeauthorizePeerRequest.Size(m) +} +func (m *DeauthorizePeerRequest) XXX_DiscardUnknown() { + xxx_messageInfo_DeauthorizePeerRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_DeauthorizePeerRequest proto.InternalMessageInfo + +func (m *DeauthorizePeerRequest) GetID() string { + if m != nil { + return m.ID + } + return "" +} + +type AuthorizedPeersRequest struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *AuthorizedPeersRequest) Reset() { *m = AuthorizedPeersRequest{} } +func (m *AuthorizedPeersRequest) String() string { return proto.CompactTextString(m) } +func (*AuthorizedPeersRequest) ProtoMessage() {} +func (*AuthorizedPeersRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b6184fc395da86b1, []int{7} +} +func (m *AuthorizedPeersRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_AuthorizedPeersRequest.Unmarshal(m, b) +} +func (m *AuthorizedPeersRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_AuthorizedPeersRequest.Marshal(b, m, deterministic) +} +func (m *AuthorizedPeersRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_AuthorizedPeersRequest.Merge(m, src) +} +func (m *AuthorizedPeersRequest) XXX_Size() int { + return xxx_messageInfo_AuthorizedPeersRequest.Size(m) +} +func (m *AuthorizedPeersRequest) XXX_DiscardUnknown() { + xxx_messageInfo_AuthorizedPeersRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_AuthorizedPeersRequest proto.InternalMessageInfo + +type AuthorizedPeersResponse struct { + IDs []string `protobuf:"bytes,1,rep,name=ids,proto3" json:"ids,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *AuthorizedPeersResponse) Reset() { *m = AuthorizedPeersResponse{} } +func (m *AuthorizedPeersResponse) String() string { return proto.CompactTextString(m) } +func (*AuthorizedPeersResponse) ProtoMessage() {} +func (*AuthorizedPeersResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b6184fc395da86b1, []int{8} +} +func (m *AuthorizedPeersResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_AuthorizedPeersResponse.Unmarshal(m, b) +} +func (m *AuthorizedPeersResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_AuthorizedPeersResponse.Marshal(b, m, deterministic) +} +func (m *AuthorizedPeersResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_AuthorizedPeersResponse.Merge(m, src) +} +func (m *AuthorizedPeersResponse) XXX_Size() int { + return xxx_messageInfo_AuthorizedPeersResponse.Size(m) +} +func (m *AuthorizedPeersResponse) XXX_DiscardUnknown() { + xxx_messageInfo_AuthorizedPeersResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_AuthorizedPeersResponse proto.InternalMessageInfo + +func (m *AuthorizedPeersResponse) GetIDs() []string { + if m != nil { + return m.IDs } return nil } @@ -178,7 +422,7 @@ 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} + return fileDescriptor_b6184fc395da86b1, []int{9} } func (m *KeyPair) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_KeyPair.Unmarshal(m, b) @@ -229,7 +473,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{4} + return fileDescriptor_b6184fc395da86b1, []int{10} } func (m *Node) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Node.Unmarshal(m, b) @@ -308,7 +552,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{5} + return fileDescriptor_b6184fc395da86b1, []int{11} } func (m *NodesRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_NodesRequest.Unmarshal(m, b) @@ -339,7 +583,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{6} + return fileDescriptor_b6184fc395da86b1, []int{12} } func (m *NodesResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_NodesResponse.Unmarshal(m, b) @@ -380,7 +624,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{7} + return fileDescriptor_b6184fc395da86b1, []int{13} } func (m *Peer) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Peer.Unmarshal(m, b) @@ -438,7 +682,7 @@ func (m *PeersRequest) Reset() { *m = PeersRequest{} } func (m *PeersRequest) String() string { return proto.CompactTextString(m) } func (*PeersRequest) ProtoMessage() {} func (*PeersRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_b6184fc395da86b1, []int{8} + return fileDescriptor_b6184fc395da86b1, []int{14} } func (m *PeersRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_PeersRequest.Unmarshal(m, b) @@ -469,7 +713,7 @@ func (m *PeersResponse) Reset() { *m = PeersResponse{} } func (m *PeersResponse) String() string { return proto.CompactTextString(m) } func (*PeersResponse) ProtoMessage() {} func (*PeersResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_b6184fc395da86b1, []int{9} + return fileDescriptor_b6184fc395da86b1, []int{15} } func (m *PeersResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_PeersResponse.Unmarshal(m, b) @@ -508,7 +752,7 @@ func (m *Route) Reset() { *m = Route{} } func (m *Route) String() string { return proto.CompactTextString(m) } func (*Route) ProtoMessage() {} func (*Route) Descriptor() ([]byte, []int) { - return fileDescriptor_b6184fc395da86b1, []int{10} + return fileDescriptor_b6184fc395da86b1, []int{16} } func (m *Route) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Route.Unmarshal(m, b) @@ -554,7 +798,7 @@ func (m *CreateRouteRequest) Reset() { *m = CreateRouteRequest{} } func (m *CreateRouteRequest) String() string { return proto.CompactTextString(m) } func (*CreateRouteRequest) ProtoMessage() {} func (*CreateRouteRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_b6184fc395da86b1, []int{11} + return fileDescriptor_b6184fc395da86b1, []int{17} } func (m *CreateRouteRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_CreateRouteRequest.Unmarshal(m, b) @@ -599,7 +843,7 @@ func (m *DeleteRouteRequest) Reset() { *m = DeleteRouteRequest{} } func (m *DeleteRouteRequest) String() string { return proto.CompactTextString(m) } func (*DeleteRouteRequest) ProtoMessage() {} func (*DeleteRouteRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_b6184fc395da86b1, []int{12} + return fileDescriptor_b6184fc395da86b1, []int{18} } func (m *DeleteRouteRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_DeleteRouteRequest.Unmarshal(m, b) @@ -636,7 +880,7 @@ func (m *RoutesRequest) Reset() { *m = RoutesRequest{} } func (m *RoutesRequest) String() string { return proto.CompactTextString(m) } func (*RoutesRequest) ProtoMessage() {} func (*RoutesRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_b6184fc395da86b1, []int{13} + return fileDescriptor_b6184fc395da86b1, []int{19} } func (m *RoutesRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_RoutesRequest.Unmarshal(m, b) @@ -667,7 +911,7 @@ func (m *RoutesResponse) Reset() { *m = RoutesResponse{} } func (m *RoutesResponse) String() string { return proto.CompactTextString(m) } func (*RoutesResponse) ProtoMessage() {} func (*RoutesResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_b6184fc395da86b1, []int{14} + return fileDescriptor_b6184fc395da86b1, []int{20} } func (m *RoutesResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_RoutesResponse.Unmarshal(m, b) @@ -696,8 +940,14 @@ func (m *RoutesResponse) GetRoutes() []*Route { func init() { proto.RegisterType((*Master)(nil), "io.stellarproject.heimdall.api.v1.Master") + proto.RegisterType((*JoinRequest)(nil), "io.stellarproject.heimdall.api.v1.JoinRequest") + proto.RegisterType((*JoinResponse)(nil), "io.stellarproject.heimdall.api.v1.JoinResponse") proto.RegisterType((*ConnectRequest)(nil), "io.stellarproject.heimdall.api.v1.ConnectRequest") proto.RegisterType((*ConnectResponse)(nil), "io.stellarproject.heimdall.api.v1.ConnectResponse") + proto.RegisterType((*AuthorizePeerRequest)(nil), "io.stellarproject.heimdall.api.v1.AuthorizePeerRequest") + proto.RegisterType((*DeauthorizePeerRequest)(nil), "io.stellarproject.heimdall.api.v1.DeauthorizePeerRequest") + proto.RegisterType((*AuthorizedPeersRequest)(nil), "io.stellarproject.heimdall.api.v1.AuthorizedPeersRequest") + proto.RegisterType((*AuthorizedPeersResponse)(nil), "io.stellarproject.heimdall.api.v1.AuthorizedPeersResponse") 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") @@ -717,59 +967,71 @@ func init() { } var fileDescriptor_b6184fc395da86b1 = []byte{ - // 831 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0xdd, 0x8e, 0xe3, 0x34, - 0x14, 0x26, 0x9d, 0x36, 0x6d, 0x4f, 0xe6, 0x47, 0xb2, 0xd0, 0x2a, 0x2a, 0x42, 0x19, 0xb2, 0x17, - 0xcc, 0xa2, 0x55, 0xb2, 0x2d, 0x42, 0x42, 0x42, 0x20, 0xe6, 0x67, 0x59, 0xa2, 0x81, 0x55, 0x31, - 0xbb, 0x37, 0x08, 0xa9, 0x4a, 0x1b, 0xd3, 0xf5, 0x4c, 0x5a, 0x1b, 0xc7, 0xe9, 0xa8, 0x57, 0xbc, - 0x02, 0x4f, 0xc4, 0x35, 0xbc, 0x44, 0x90, 0xfa, 0x22, 0x20, 0x3b, 0x4e, 0x68, 0x67, 0x34, 0x90, - 0x11, 0x7b, 0x97, 0x73, 0x7c, 0xbe, 0xcf, 0x9f, 0xbf, 0x73, 0xec, 0x16, 0x3e, 0x9d, 0x53, 0xf9, - 0x26, 0x9f, 0x06, 0x33, 0xb6, 0x08, 0x33, 0x49, 0xd2, 0x34, 0x16, 0x5c, 0xb0, 0x2b, 0x32, 0x93, - 0xe1, 0x1b, 0x42, 0x17, 0x49, 0x9c, 0xa6, 0x61, 0xcc, 0x69, 0xb8, 0x1a, 0xd6, 0x71, 0xc0, 0x05, - 0x93, 0x0c, 0x7d, 0x40, 0x59, 0xb0, 0x8b, 0x08, 0xea, 0x8a, 0x98, 0xd3, 0x60, 0x35, 0x1c, 0xbc, - 0x3b, 0x67, 0x73, 0xa6, 0xab, 0x43, 0xf5, 0x55, 0x02, 0x07, 0xef, 0xcd, 0x19, 0x9b, 0xa7, 0x24, - 0xd4, 0xd1, 0x34, 0xff, 0x29, 0x24, 0x0b, 0x2e, 0xd7, 0x66, 0xd1, 0xbb, 0xbd, 0x28, 0xe9, 0x82, - 0x64, 0x32, 0x5e, 0xf0, 0xb2, 0xc0, 0xff, 0x05, 0xec, 0x6f, 0xe3, 0x4c, 0x12, 0x81, 0x1e, 0x41, - 0x8b, 0x26, 0xae, 0x75, 0x6c, 0x9d, 0xf4, 0xcf, 0xec, 0x4d, 0xe1, 0xb5, 0xa2, 0x0b, 0xdc, 0xa2, - 0x09, 0x1a, 0xc1, 0xfe, 0x5c, 0xf0, 0xd9, 0x24, 0x4e, 0x12, 0x41, 0xb2, 0xcc, 0x6d, 0xe9, 0x8a, - 0xa3, 0x4d, 0xe1, 0x39, 0x2f, 0xf0, 0xf8, 0xfc, 0xb4, 0x4c, 0x63, 0x47, 0x15, 0x99, 0x00, 0x3d, - 0x81, 0xbe, 0x20, 0x09, 0xcd, 0x26, 0xb9, 0x48, 0xdd, 0x3d, 0x0d, 0xd8, 0xdf, 0x14, 0x5e, 0x0f, - 0xab, 0xe4, 0x6b, 0xfc, 0x0d, 0xee, 0xe9, 0xe5, 0xd7, 0x22, 0xf5, 0x23, 0x38, 0x3c, 0x67, 0xcb, - 0x25, 0x99, 0x49, 0x4c, 0x7e, 0xce, 0x49, 0x26, 0xef, 0x15, 0xe2, 0x81, 0x33, 0x4b, 0x73, 0xa5, - 0x75, 0x72, 0x4d, 0xd6, 0xa5, 0x0e, 0x0c, 0x26, 0x75, 0x49, 0xd6, 0xfe, 0x2b, 0x38, 0xaa, 0xa9, - 0x32, 0xce, 0x96, 0x19, 0x41, 0xa7, 0x60, 0x2f, 0xf4, 0xf1, 0x34, 0x9f, 0x33, 0x7a, 0x12, 0xfc, - 0xa7, 0xcd, 0x41, 0xe9, 0x07, 0x36, 0x40, 0x3f, 0x82, 0xee, 0x25, 0x59, 0x8f, 0x63, 0x2a, 0x94, - 0x02, 0x2e, 0xe8, 0x2a, 0x96, 0x44, 0x2b, 0xb0, 0x4a, 0x05, 0x26, 0x75, 0x49, 0xd6, 0xe8, 0x7d, - 0x00, 0x9e, 0x4f, 0x53, 0x3a, 0xdb, 0x52, 0xd8, 0x2f, 0x33, 0x4a, 0xe0, 0x1f, 0x2d, 0x68, 0xbf, - 0x64, 0x09, 0xb9, 0xf7, 0x88, 0x08, 0xda, 0xca, 0x66, 0x83, 0xd4, 0xdf, 0xe8, 0x3b, 0xe8, 0x5e, - 0x93, 0x35, 0x8f, 0xa9, 0xd0, 0x4e, 0x3a, 0xa3, 0x8f, 0x1a, 0x9c, 0xc1, 0x28, 0x3e, 0x73, 0x36, - 0x85, 0x57, 0xc9, 0xc7, 0x15, 0x0f, 0x0a, 0xc1, 0x21, 0xcb, 0x84, 0x33, 0xba, 0x94, 0x13, 0xca, - 0xdd, 0xb6, 0xd6, 0x71, 0xb8, 0x29, 0x3c, 0x78, 0x6e, 0xd2, 0xd1, 0x18, 0x43, 0x55, 0x12, 0x71, - 0xf4, 0x18, 0x0e, 0x6a, 0x00, 0x67, 0x42, 0xba, 0x9d, 0x63, 0xeb, 0xa4, 0x8d, 0xf7, 0xab, 0xe4, - 0x98, 0x09, 0x89, 0x9e, 0x02, 0xcc, 0x63, 0x49, 0x6e, 0xe2, 0xb5, 0x22, 0xb5, 0x35, 0xe9, 0xc1, - 0xa6, 0xf0, 0xfa, 0x2f, 0xca, 0x6c, 0x34, 0xc6, 0x7d, 0x53, 0x10, 0x71, 0xf4, 0x05, 0x74, 0x73, - 0x9e, 0xc4, 0x92, 0x24, 0x6e, 0x57, 0x1f, 0x6b, 0x10, 0x94, 0xb3, 0x1a, 0x54, 0xb3, 0x1a, 0xbc, - 0xaa, 0x66, 0xf5, 0xac, 0xf7, 0x7b, 0xe1, 0xbd, 0xf3, 0xeb, 0x9f, 0x9e, 0x85, 0x2b, 0x90, 0x7f, - 0x08, 0xfb, 0xca, 0xca, 0xcc, 0x4c, 0x8d, 0xff, 0x12, 0x0e, 0x4c, 0x6c, 0x5a, 0xff, 0x39, 0x74, - 0x96, 0x2a, 0xe1, 0x5a, 0xc7, 0x7b, 0x27, 0xce, 0xe8, 0xc3, 0x06, 0xae, 0x29, 0x02, 0x5c, 0xa2, - 0xfc, 0xdf, 0x2c, 0x68, 0x8f, 0xc9, 0xbf, 0xdc, 0x8b, 0xad, 0xbe, 0xb4, 0xde, 0x5e, 0x5f, 0xe2, - 0x34, 0x65, 0x37, 0x24, 0x99, 0x50, 0x9e, 0xb9, 0x7b, 0xc7, 0x7b, 0x55, 0x5f, 0x4e, 0xcb, 0x74, - 0x34, 0xce, 0x30, 0x98, 0x92, 0x88, 0x67, 0x68, 0x00, 0xbd, 0xaa, 0x05, 0x65, 0x17, 0x71, 0x1d, - 0x2b, 0x83, 0x94, 0xfe, 0x6d, 0x83, 0x4c, 0xfc, 0x8f, 0x41, 0x5c, 0x25, 0x1e, 0x60, 0x90, 0x22, - 0xc0, 0x25, 0xca, 0xff, 0x0a, 0x3a, 0x98, 0xe5, 0x92, 0xa0, 0xc7, 0xd0, 0x55, 0x96, 0x4d, 0x6a, - 0x97, 0x60, 0x53, 0x78, 0xb6, 0xf2, 0x32, 0xba, 0xc0, 0xb6, 0x5a, 0x8a, 0x12, 0xe4, 0x42, 0x77, - 0x49, 0xe4, 0x0d, 0x13, 0xd7, 0x66, 0xb8, 0xab, 0xd0, 0xff, 0x1e, 0xd0, 0xb9, 0x20, 0xb1, 0x24, - 0x9a, 0xad, 0x7a, 0x04, 0xfe, 0x27, 0x69, 0x00, 0xe8, 0x82, 0xa4, 0xe4, 0x16, 0xe9, 0x56, 0xbd, - 0xb5, 0x5b, 0x7f, 0x04, 0x07, 0xba, 0xb2, 0x76, 0x0b, 0xc3, 0x61, 0x95, 0x30, 0x76, 0x7d, 0x09, - 0xb6, 0xd0, 0x19, 0xe3, 0xd7, 0x49, 0x03, 0xbf, 0xca, 0xdd, 0x0d, 0x6e, 0xf4, 0x57, 0x1b, 0x7a, - 0x5f, 0x9b, 0x0a, 0xc4, 0xa1, 0x6b, 0x1e, 0x2b, 0x34, 0x6c, 0xc0, 0xb4, 0xfb, 0x46, 0x0e, 0x46, - 0x0f, 0x81, 0x98, 0x03, 0x2c, 0xc0, 0x2e, 0x8f, 0x84, 0x9e, 0x35, 0x95, 0x5e, 0xd9, 0x31, 0x18, - 0x3e, 0x00, 0x61, 0xb6, 0xfb, 0x11, 0x9c, 0xad, 0xbe, 0xa2, 0x4f, 0x9a, 0x28, 0xbe, 0x33, 0x07, - 0x83, 0x47, 0x77, 0x5e, 0x85, 0xe7, 0xea, 0xe7, 0x4d, 0xb1, 0x6f, 0x35, 0xb8, 0x11, 0xfb, 0xdd, - 0x81, 0xb8, 0x97, 0xfd, 0x0a, 0x3a, 0xfa, 0x31, 0x41, 0x61, 0xc3, 0x57, 0xa3, 0x36, 0xea, 0x59, - 0x73, 0x80, 0xf1, 0xe9, 0x0a, 0x3a, 0xfa, 0x5e, 0x36, 0xda, 0x6b, 0xfb, 0x46, 0x37, 0xda, 0x6b, - 0xe7, 0xca, 0x9f, 0x05, 0x3f, 0x3c, 0x6d, 0xfc, 0x07, 0xe5, 0xb3, 0xd5, 0x70, 0x6a, 0x6b, 0x5f, - 0x3e, 0xfe, 0x3b, 0x00, 0x00, 0xff, 0xff, 0x46, 0x38, 0x83, 0x35, 0xd7, 0x08, 0x00, 0x00, + // 1014 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x57, 0x5d, 0x6f, 0xe3, 0x44, + 0x17, 0x7e, 0x9d, 0xef, 0x1c, 0x27, 0x8d, 0x34, 0x5a, 0xf5, 0xf5, 0x06, 0x21, 0x17, 0xef, 0x05, + 0x59, 0xb4, 0xb2, 0x9b, 0x00, 0xe2, 0x4b, 0x8b, 0x68, 0x9a, 0xdd, 0xc5, 0x14, 0xaa, 0xec, 0x2c, + 0x7b, 0x83, 0x56, 0x0a, 0x6e, 0x3c, 0xa4, 0x6e, 0x9d, 0xcc, 0x60, 0x4f, 0x5a, 0x85, 0x1b, 0x6e, + 0xb9, 0xe4, 0xff, 0x20, 0x71, 0x0d, 0xff, 0x80, 0xab, 0x20, 0xe5, 0x97, 0xa0, 0x19, 0x8f, 0x4d, + 0xd2, 0x36, 0xaa, 0xbb, 0xec, 0x5d, 0xe6, 0xcc, 0x79, 0xce, 0x39, 0x7e, 0xce, 0x73, 0x8e, 0x1d, + 0xf8, 0x78, 0x12, 0xf0, 0xd3, 0xf9, 0x89, 0x3d, 0xa6, 0x53, 0x27, 0xe6, 0x24, 0x0c, 0xbd, 0x88, + 0x45, 0xf4, 0x8c, 0x8c, 0xb9, 0x73, 0x4a, 0x82, 0xa9, 0xef, 0x85, 0xa1, 0xe3, 0xb1, 0xc0, 0xb9, + 0xe8, 0x66, 0x67, 0x9b, 0x45, 0x94, 0x53, 0xf4, 0x4e, 0x40, 0xed, 0x4d, 0x84, 0x9d, 0x79, 0x78, + 0x2c, 0xb0, 0x2f, 0xba, 0xed, 0x7b, 0x13, 0x3a, 0xa1, 0xd2, 0xdb, 0x11, 0xbf, 0x12, 0x60, 0xfb, + 0xad, 0x09, 0xa5, 0x93, 0x90, 0x38, 0xf2, 0x74, 0x32, 0xff, 0xc1, 0x21, 0x53, 0xc6, 0x17, 0xea, + 0xd2, 0xbc, 0x7a, 0xc9, 0x83, 0x29, 0x89, 0xb9, 0x37, 0x65, 0x89, 0x83, 0xf5, 0x33, 0x54, 0xbe, + 0xf1, 0x62, 0x4e, 0x22, 0xb4, 0x0b, 0x85, 0xc0, 0x37, 0xb4, 0x3d, 0xad, 0x53, 0xef, 0x57, 0x56, + 0x4b, 0xb3, 0xe0, 0x0e, 0x70, 0x21, 0xf0, 0x51, 0x0f, 0x1a, 0x93, 0x88, 0x8d, 0x47, 0x9e, 0xef, + 0x47, 0x24, 0x8e, 0x8d, 0x82, 0xf4, 0x68, 0xad, 0x96, 0xa6, 0xfe, 0x0c, 0x0f, 0x0f, 0x0f, 0x12, + 0x33, 0xd6, 0x85, 0x93, 0x3a, 0xa0, 0x87, 0x50, 0x8f, 0x88, 0x1f, 0xc4, 0xa3, 0x79, 0x14, 0x1a, + 0x45, 0x09, 0x68, 0xac, 0x96, 0x66, 0x0d, 0x0b, 0xe3, 0x4b, 0xfc, 0x35, 0xae, 0xc9, 0xeb, 0x97, + 0x51, 0x68, 0x3d, 0x05, 0xfd, 0x2b, 0x1a, 0xcc, 0x30, 0xf9, 0x71, 0x4e, 0x62, 0xbe, 0xb5, 0x0a, + 0x13, 0xf4, 0x71, 0x38, 0x17, 0x85, 0x8e, 0xce, 0xc9, 0x22, 0x29, 0x02, 0x83, 0x32, 0x1d, 0x91, + 0x85, 0xf5, 0x1c, 0x1a, 0x49, 0x9c, 0x98, 0xd1, 0x59, 0x4c, 0xd0, 0x01, 0x54, 0xa6, 0xf2, 0xc1, + 0x64, 0x30, 0xbd, 0xf7, 0xd0, 0xbe, 0x95, 0x60, 0x3b, 0x61, 0x02, 0x2b, 0xa0, 0xd5, 0x81, 0x9d, + 0x43, 0x3a, 0x9b, 0x91, 0x31, 0xbf, 0xa5, 0x3a, 0xeb, 0x2f, 0x0d, 0x5a, 0x99, 0xab, 0x2a, 0xe0, + 0x39, 0x54, 0xcf, 0xc9, 0x82, 0x79, 0x41, 0x5a, 0xc1, 0x7b, 0x39, 0x2a, 0x38, 0x22, 0x8b, 0xa1, + 0x17, 0x44, 0x7d, 0x7d, 0xb5, 0x34, 0xab, 0xea, 0x80, 0xd3, 0x38, 0xc8, 0x80, 0xea, 0x46, 0x17, + 0x70, 0x7a, 0x44, 0x8f, 0xa1, 0xcc, 0x08, 0x89, 0x62, 0xa3, 0xb8, 0x57, 0xec, 0xe8, 0xbd, 0x77, + 0x73, 0xa4, 0x1a, 0x12, 0x12, 0xe1, 0x04, 0x85, 0xee, 0x43, 0xd1, 0x9f, 0xc5, 0x46, 0x69, 0xaf, + 0xd8, 0xa9, 0xf7, 0xab, 0xab, 0xa5, 0x59, 0x1c, 0x1c, 0xbf, 0xc0, 0xc2, 0x66, 0xd9, 0x70, 0xef, + 0x60, 0xce, 0x4f, 0x69, 0x14, 0xfc, 0x44, 0x24, 0xe4, 0x16, 0x2a, 0xf6, 0x61, 0x77, 0x40, 0xbc, + 0xbb, 0x20, 0x0c, 0xd8, 0xcd, 0x32, 0xf8, 0x02, 0x10, 0x2b, 0x84, 0xf5, 0x01, 0xfc, 0xff, 0xda, + 0x8d, 0x62, 0xf7, 0x3e, 0x14, 0x03, 0x3f, 0x36, 0xb4, 0x7f, 0x2b, 0x76, 0x07, 0x31, 0x16, 0x36, + 0xcb, 0x85, 0x94, 0x39, 0xa1, 0x1a, 0x16, 0x05, 0x17, 0x1e, 0x27, 0x52, 0x35, 0x5a, 0xa2, 0x1a, + 0x65, 0x3a, 0x22, 0x0b, 0xf4, 0x36, 0x00, 0x9b, 0x9f, 0x84, 0xc1, 0x78, 0x4d, 0x55, 0xf5, 0xc4, + 0x22, 0x44, 0xf5, 0x67, 0x01, 0x4a, 0xc7, 0xd4, 0x27, 0x5b, 0x65, 0x89, 0xa0, 0x24, 0x5a, 0xa0, + 0x90, 0xf2, 0xf7, 0x7a, 0xe3, 0x8b, 0x6f, 0xa8, 0xf1, 0x0e, 0xe8, 0x64, 0xe6, 0x33, 0x1a, 0xcc, + 0xf8, 0x28, 0x60, 0x46, 0x49, 0xd6, 0xb1, 0xb3, 0x5a, 0x9a, 0xf0, 0x44, 0x99, 0xdd, 0x21, 0x86, + 0xd4, 0xc5, 0x65, 0xe8, 0x01, 0x34, 0x33, 0x00, 0xa3, 0x11, 0x37, 0xca, 0x7b, 0x5a, 0xa7, 0x84, + 0x1b, 0xa9, 0x71, 0x48, 0x23, 0x8e, 0x1e, 0x01, 0x4c, 0x3c, 0x4e, 0x2e, 0xbd, 0x85, 0x08, 0x5a, + 0x91, 0x41, 0x9b, 0xab, 0xa5, 0x59, 0x7f, 0x96, 0x58, 0xdd, 0x21, 0xae, 0x2b, 0x07, 0x97, 0xa1, + 0xcf, 0xa1, 0x3a, 0x67, 0xbe, 0xc7, 0x89, 0x6f, 0x54, 0xe5, 0x63, 0xb5, 0xed, 0x64, 0xb9, 0xd8, + 0xe9, 0x72, 0xb1, 0xbf, 0x4d, 0x97, 0x4b, 0xbf, 0xf6, 0xc7, 0xd2, 0xfc, 0xdf, 0xaf, 0x7f, 0x9b, + 0x1a, 0x4e, 0x41, 0xd6, 0x0e, 0x34, 0x04, 0x95, 0x59, 0x73, 0x8f, 0xa1, 0xa9, 0xce, 0xaa, 0xa5, + 0x8f, 0xa1, 0x3c, 0x13, 0x06, 0xd9, 0xd4, 0x7c, 0x1a, 0x16, 0x01, 0x70, 0x82, 0xb2, 0x7e, 0xd7, + 0xa0, 0x24, 0x34, 0xb2, 0xb5, 0x57, 0x6b, 0x7d, 0x29, 0xbc, 0xb9, 0xbe, 0x78, 0x61, 0x48, 0x2f, + 0x89, 0x3f, 0x0a, 0x58, 0x32, 0x7c, 0xaa, 0x2f, 0x07, 0x89, 0xd9, 0x1d, 0xc6, 0x18, 0x94, 0x8b, + 0xcb, 0x62, 0xd4, 0x86, 0x5a, 0xda, 0x82, 0xa4, 0x8b, 0x38, 0x3b, 0x0b, 0x82, 0x36, 0xd4, 0x7f, + 0x0c, 0xcd, 0x4d, 0xcd, 0x67, 0x43, 0xae, 0xbd, 0xce, 0x90, 0x5b, 0x4f, 0xa1, 0x8c, 0xe9, 0x9c, + 0x13, 0xf4, 0x00, 0xaa, 0x82, 0xb2, 0x51, 0xc6, 0x12, 0xac, 0x96, 0x66, 0x45, 0x70, 0xe9, 0x0e, + 0x70, 0x45, 0x5c, 0xb9, 0xbe, 0xd8, 0x35, 0x33, 0xc2, 0x2f, 0x69, 0x74, 0x9e, 0xee, 0x1a, 0x75, + 0xb4, 0x5e, 0x00, 0x3a, 0x8c, 0x88, 0xc7, 0x89, 0x8c, 0x96, 0x4e, 0xf7, 0x7f, 0x0c, 0x6a, 0x03, + 0x1a, 0x90, 0x90, 0x5c, 0x09, 0xba, 0xe6, 0xaf, 0x6d, 0xfa, 0xb7, 0xa0, 0x29, 0x3d, 0x33, 0xb6, + 0x30, 0xec, 0xa4, 0x06, 0x45, 0xd7, 0x17, 0x50, 0x89, 0xa4, 0x45, 0xf1, 0xd5, 0xc9, 0xc1, 0x57, + 0x92, 0x5d, 0xe1, 0x7a, 0xbf, 0xd5, 0xa0, 0xf6, 0xa5, 0xf2, 0x40, 0x0c, 0xaa, 0x6a, 0xc5, 0xa3, + 0x6e, 0x8e, 0x48, 0x9b, 0x6f, 0x8e, 0x76, 0xef, 0x2e, 0x10, 0xf5, 0x00, 0xdf, 0x43, 0x73, 0x63, + 0xf5, 0xa2, 0x8f, 0x72, 0x04, 0xb9, 0x69, 0x59, 0xb7, 0x77, 0xaf, 0x8d, 0xea, 0x13, 0xf1, 0x91, + 0x80, 0x7c, 0x68, 0x5d, 0x59, 0xd6, 0xe8, 0x93, 0x1c, 0x39, 0x6e, 0x5e, 0xf0, 0x5b, 0xb3, 0xfc, + 0xa2, 0x41, 0xeb, 0xca, 0x1e, 0xcf, 0x95, 0xe6, 0xe6, 0xb7, 0x42, 0xfb, 0xd3, 0xd7, 0x81, 0x2a, + 0x4a, 0x27, 0x50, 0x12, 0x5f, 0x09, 0xc8, 0xce, 0x11, 0x63, 0xed, 0xb3, 0xa4, 0xed, 0xe4, 0xf6, + 0x57, 0x89, 0xa6, 0x50, 0x49, 0xe4, 0x88, 0xf6, 0xf3, 0xca, 0x2e, 0x7b, 0xc0, 0xee, 0x1d, 0x10, + 0x2a, 0xdd, 0x2b, 0xd0, 0xd7, 0x66, 0x12, 0x7d, 0x98, 0x47, 0x6d, 0xd7, 0x66, 0x78, 0x6b, 0x03, + 0x5f, 0x81, 0xbe, 0x36, 0x9c, 0xb9, 0xa2, 0x5f, 0x1f, 0xe6, 0xad, 0xd1, 0xcf, 0xa0, 0x2c, 0x5f, + 0x04, 0xc8, 0xc9, 0xb9, 0xf1, 0x33, 0xa2, 0xf6, 0xf3, 0x03, 0x14, 0x4f, 0x67, 0x50, 0x4e, 0xf4, + 0xe7, 0xe4, 0x5c, 0x9e, 0x77, 0xca, 0xb5, 0xa1, 0xb5, 0xbe, 0xfd, 0xdd, 0xa3, 0xdc, 0xff, 0x06, + 0x3e, 0xbb, 0xe8, 0x9e, 0x54, 0x24, 0x2f, 0xef, 0xff, 0x13, 0x00, 0x00, 0xff, 0xff, 0xcd, 0x85, + 0x12, 0x11, 0x44, 0x0c, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -785,6 +1047,10 @@ const _ = grpc.SupportPackageIsVersion4 // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type HeimdallClient interface { Connect(ctx context.Context, in *ConnectRequest, opts ...grpc.CallOption) (*ConnectResponse, error) + AuthorizePeer(ctx context.Context, in *AuthorizePeerRequest, opts ...grpc.CallOption) (*types.Empty, error) + DeauthorizePeer(ctx context.Context, in *DeauthorizePeerRequest, opts ...grpc.CallOption) (*types.Empty, error) + AuthorizedPeers(ctx context.Context, in *AuthorizedPeersRequest, opts ...grpc.CallOption) (*AuthorizedPeersResponse, error) + Join(ctx context.Context, in *JoinRequest, opts ...grpc.CallOption) (*JoinResponse, error) Routes(ctx context.Context, in *RoutesRequest, opts ...grpc.CallOption) (*RoutesResponse, error) CreateRoute(ctx context.Context, in *CreateRouteRequest, opts ...grpc.CallOption) (*types.Empty, error) DeleteRoute(ctx context.Context, in *DeleteRouteRequest, opts ...grpc.CallOption) (*types.Empty, error) @@ -809,6 +1075,42 @@ func (c *heimdallClient) Connect(ctx context.Context, in *ConnectRequest, opts . return out, nil } +func (c *heimdallClient) AuthorizePeer(ctx context.Context, in *AuthorizePeerRequest, opts ...grpc.CallOption) (*types.Empty, error) { + out := new(types.Empty) + err := c.cc.Invoke(ctx, "/io.stellarproject.heimdall.api.v1.Heimdall/AuthorizePeer", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *heimdallClient) DeauthorizePeer(ctx context.Context, in *DeauthorizePeerRequest, opts ...grpc.CallOption) (*types.Empty, error) { + out := new(types.Empty) + err := c.cc.Invoke(ctx, "/io.stellarproject.heimdall.api.v1.Heimdall/DeauthorizePeer", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *heimdallClient) AuthorizedPeers(ctx context.Context, in *AuthorizedPeersRequest, opts ...grpc.CallOption) (*AuthorizedPeersResponse, error) { + out := new(AuthorizedPeersResponse) + err := c.cc.Invoke(ctx, "/io.stellarproject.heimdall.api.v1.Heimdall/AuthorizedPeers", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *heimdallClient) Join(ctx context.Context, in *JoinRequest, opts ...grpc.CallOption) (*JoinResponse, error) { + out := new(JoinResponse) + err := c.cc.Invoke(ctx, "/io.stellarproject.heimdall.api.v1.Heimdall/Join", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *heimdallClient) Routes(ctx context.Context, in *RoutesRequest, opts ...grpc.CallOption) (*RoutesResponse, error) { out := new(RoutesResponse) err := c.cc.Invoke(ctx, "/io.stellarproject.heimdall.api.v1.Heimdall/Routes", in, out, opts...) @@ -857,6 +1159,10 @@ func (c *heimdallClient) Peers(ctx context.Context, in *PeersRequest, opts ...gr // HeimdallServer is the server API for Heimdall service. type HeimdallServer interface { Connect(context.Context, *ConnectRequest) (*ConnectResponse, error) + AuthorizePeer(context.Context, *AuthorizePeerRequest) (*types.Empty, error) + DeauthorizePeer(context.Context, *DeauthorizePeerRequest) (*types.Empty, error) + AuthorizedPeers(context.Context, *AuthorizedPeersRequest) (*AuthorizedPeersResponse, error) + Join(context.Context, *JoinRequest) (*JoinResponse, error) Routes(context.Context, *RoutesRequest) (*RoutesResponse, error) CreateRoute(context.Context, *CreateRouteRequest) (*types.Empty, error) DeleteRoute(context.Context, *DeleteRouteRequest) (*types.Empty, error) @@ -871,6 +1177,18 @@ type UnimplementedHeimdallServer struct { func (*UnimplementedHeimdallServer) Connect(ctx context.Context, req *ConnectRequest) (*ConnectResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Connect not implemented") } +func (*UnimplementedHeimdallServer) AuthorizePeer(ctx context.Context, req *AuthorizePeerRequest) (*types.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method AuthorizePeer not implemented") +} +func (*UnimplementedHeimdallServer) DeauthorizePeer(ctx context.Context, req *DeauthorizePeerRequest) (*types.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeauthorizePeer not implemented") +} +func (*UnimplementedHeimdallServer) AuthorizedPeers(ctx context.Context, req *AuthorizedPeersRequest) (*AuthorizedPeersResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AuthorizedPeers not implemented") +} +func (*UnimplementedHeimdallServer) Join(ctx context.Context, req *JoinRequest) (*JoinResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Join not implemented") +} func (*UnimplementedHeimdallServer) Routes(ctx context.Context, req *RoutesRequest) (*RoutesResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Routes not implemented") } @@ -909,6 +1227,78 @@ func _Heimdall_Connect_Handler(srv interface{}, ctx context.Context, dec func(in return interceptor(ctx, in, info, handler) } +func _Heimdall_AuthorizePeer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AuthorizePeerRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HeimdallServer).AuthorizePeer(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/io.stellarproject.heimdall.api.v1.Heimdall/AuthorizePeer", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HeimdallServer).AuthorizePeer(ctx, req.(*AuthorizePeerRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Heimdall_DeauthorizePeer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeauthorizePeerRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HeimdallServer).DeauthorizePeer(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/io.stellarproject.heimdall.api.v1.Heimdall/DeauthorizePeer", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HeimdallServer).DeauthorizePeer(ctx, req.(*DeauthorizePeerRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Heimdall_AuthorizedPeers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AuthorizedPeersRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HeimdallServer).AuthorizedPeers(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/io.stellarproject.heimdall.api.v1.Heimdall/AuthorizedPeers", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HeimdallServer).AuthorizedPeers(ctx, req.(*AuthorizedPeersRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Heimdall_Join_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(JoinRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HeimdallServer).Join(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/io.stellarproject.heimdall.api.v1.Heimdall/Join", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HeimdallServer).Join(ctx, req.(*JoinRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _Heimdall_Routes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(RoutesRequest) if err := dec(in); err != nil { @@ -1007,6 +1397,22 @@ var _Heimdall_serviceDesc = grpc.ServiceDesc{ MethodName: "Connect", Handler: _Heimdall_Connect_Handler, }, + { + MethodName: "AuthorizePeer", + Handler: _Heimdall_AuthorizePeer_Handler, + }, + { + MethodName: "DeauthorizePeer", + Handler: _Heimdall_DeauthorizePeer_Handler, + }, + { + MethodName: "AuthorizedPeers", + Handler: _Heimdall_AuthorizedPeers_Handler, + }, + { + MethodName: "Join", + Handler: _Heimdall_Join_Handler, + }, { MethodName: "Routes", Handler: _Heimdall_Routes_Handler, diff --git a/api/v1/heimdall.proto b/api/v1/heimdall.proto index e7c5353..50e5a9d 100644 --- a/api/v1/heimdall.proto +++ b/api/v1/heimdall.proto @@ -10,6 +10,10 @@ option go_package = "github.com/stellarproject/heimdall/api/v1;v1"; service Heimdall { rpc Connect(ConnectRequest) returns (ConnectResponse); + rpc AuthorizePeer(AuthorizePeerRequest) returns (google.protobuf.Empty); + rpc DeauthorizePeer(DeauthorizePeerRequest) returns (google.protobuf.Empty); + rpc AuthorizedPeers(AuthorizedPeersRequest) returns (AuthorizedPeersResponse); + rpc Join(JoinRequest) returns (JoinResponse); rpc Routes(RoutesRequest) returns (RoutesResponse); rpc CreateRoute(CreateRouteRequest) returns (google.protobuf.Empty); rpc DeleteRoute(DeleteRouteRequest) returns (google.protobuf.Empty); @@ -23,15 +27,40 @@ message Master { string redis_url = 3 [(gogoproto.customname) = "RedisURL"]; } -message ConnectRequest { +message JoinRequest { string id = 1 [(gogoproto.customname) = "ID"]; string cluster_key = 2; } -message ConnectResponse { +message JoinResponse { Master master = 1; } +message ConnectRequest { + string id = 1 [(gogoproto.customname) = "ID"]; +} + +message ConnectResponse { + KeyPair keypair = 1 [(gogoproto.customname) = "KeyPair"]; + string address = 2; + repeated Peer peers = 3; + repeated string dns = 4 [(gogoproto.customname) = "DNS"]; +} + +message AuthorizePeerRequest { + string id = 1 [(gogoproto.customname) = "ID"]; +} + +message DeauthorizePeerRequest { + string id = 1 [(gogoproto.customname) = "ID"]; +} + +message AuthorizedPeersRequest {} + +message AuthorizedPeersResponse { + repeated string ids = 1 [(gogoproto.customname) = "IDs"]; +} + message KeyPair { string private_key = 1; string public_key = 2; diff --git a/client/connect.go b/client/connect.go index 6ecd53d..2c891a6 100644 --- a/client/connect.go +++ b/client/connect.go @@ -27,16 +27,42 @@ import ( v1 "github.com/stellarproject/heimdall/api/v1" ) -// Connect attempts to connect to the peer and returns the master info -func (c *Client) Connect(key string) (*v1.Master, error) { +// AuthorizePeer authorizes a new peer to the cluster +func (c *Client) AuthorizePeer(id string) error { ctx := context.Background() - resp, err := c.heimdallClient.Connect(ctx, &v1.ConnectRequest{ - ID: c.id, - ClusterKey: key, - }) + if _, err := c.heimdallClient.AuthorizePeer(ctx, &v1.AuthorizePeerRequest{ + ID: id, + }); err != nil { + return err + } + return nil +} + +// DeauthorizePeer removes a peer from the cluster +func (c *Client) DeauthorizePeer(id string) error { + ctx := context.Background() + if _, err := c.heimdallClient.DeauthorizePeer(ctx, &v1.DeauthorizePeerRequest{ + ID: id, + }); err != nil { + return err + } + return nil +} + +// AuthorizedPeers returns a list of authorized peers +func (c *Client) AuthorizedPeers() ([]string, error) { + ctx := context.Background() + resp, err := c.heimdallClient.AuthorizedPeers(ctx, &v1.AuthorizedPeersRequest{}) if err != nil { return nil, err } - - return resp.Master, nil + return resp.IDs, nil +} + +// Connect requests to connect a peer to the cluster +func (c *Client) Connect() (*v1.ConnectResponse, error) { + ctx := context.Background() + return c.heimdallClient.Connect(ctx, &v1.ConnectRequest{ + ID: c.id, + }) } diff --git a/client/join.go b/client/join.go new file mode 100644 index 0000000..ad18ce6 --- /dev/null +++ b/client/join.go @@ -0,0 +1,42 @@ +/* + 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 client + +import ( + "context" + + v1 "github.com/stellarproject/heimdall/api/v1" +) + +// Join attempts to connect to the peer and returns the master info +func (c *Client) Join(key string) (*v1.Master, error) { + ctx := context.Background() + resp, err := c.heimdallClient.Join(ctx, &v1.JoinRequest{ + ID: c.id, + ClusterKey: key, + }) + if err != nil { + return nil, err + } + + return resp.Master, nil +} diff --git a/cmd/hctl/peers.go b/cmd/hctl/peers.go index a92fa5a..18b1386 100644 --- a/cmd/hctl/peers.go +++ b/cmd/hctl/peers.go @@ -34,8 +34,12 @@ var peersCommand = cli.Command{ Usage: "peer management", Subcommands: []cli.Command{ listPeersCommand, + authorizedPeersCommand, + authorizePeerCommand, + deauthorizePeerCommand, }, } + var listPeersCommand = cli.Command{ Name: "list", Usage: "list peers", @@ -61,3 +65,57 @@ var listPeersCommand = cli.Command{ return nil }, } + +var authorizedPeersCommand = cli.Command{ + Name: "authorized", + Usage: "authorized peers in the cluster", + Action: func(cx *cli.Context) error { + c, err := getClient(cx) + if err != nil { + return err + } + defer c.Close() + + ids, err := c.AuthorizedPeers() + if err != nil { + return err + } + w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0) + fmt.Fprintf(w, "ID\n") + for _, id := range ids { + fmt.Fprintf(w, "%s\n", id) + } + w.Flush() + return nil + }, +} + +var authorizePeerCommand = cli.Command{ + Name: "authorize", + Usage: "authorize peer to cluster", + Action: func(cx *cli.Context) error { + c, err := getClient(cx) + if err != nil { + return err + } + defer c.Close() + + id := cx.Args().First() + return c.AuthorizePeer(id) + }, +} + +var deauthorizePeerCommand = cli.Command{ + Name: "deauthorize", + Usage: "deauthorize peer from cluster", + Action: func(cx *cli.Context) error { + c, err := getClient(cx) + if err != nil { + return err + } + defer c.Close() + + id := cx.Args().First() + return c.DeauthorizePeer(id) + }, +} diff --git a/cmd/heimdall/main.go b/cmd/heimdall/main.go index bebb444..dc4f779 100644 --- a/cmd/heimdall/main.go +++ b/cmd/heimdall/main.go @@ -107,6 +107,12 @@ func main() { Value: 10100, EnvVar: "HEIMDALL_ENDPOINT_PORT", }, + cli.StringFlag{ + Name: "interface-name", + Usage: "interface name to use for peer communication (must not exist)", + Value: "darknet", + EnvVar: "HEIMDALL_INTERFACE_NAME", + }, } app.Before = func(c *cli.Context) error { if c.Bool("debug") { diff --git a/cmd/heimdall/run.go b/cmd/heimdall/run.go index 5df07b0..1eccbdf 100644 --- a/cmd/heimdall/run.go +++ b/cmd/heimdall/run.go @@ -49,6 +49,7 @@ func runServer(cx *cli.Context) error { PeerNetwork: cx.String("peer-network"), EndpointIP: cx.String("endpoint-ip"), EndpointPort: cx.Int("endpoint-port"), + InterfaceName: cx.String("interface-name"), RedisURL: cx.String("redis-url"), AdvertiseRedisURL: cx.String("advertise-redis-url"), } diff --git a/cmd/hpeer/main.go b/cmd/hpeer/main.go new file mode 100644 index 0000000..f870cd1 --- /dev/null +++ b/cmd/hpeer/main.go @@ -0,0 +1,96 @@ +/* + 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 main + +import ( + "os" + "time" + + "github.com/sirupsen/logrus" + "github.com/stellarproject/heimdall" + "github.com/stellarproject/heimdall/version" + "github.com/urfave/cli" +) + +func main() { + app := cli.NewApp() + app.Name = "hpeer" + app.Version = version.BuildVersion() + app.Author = "@stellarproject" + app.Email = "" + app.Usage = version.Description + app.Flags = []cli.Flag{ + cli.BoolFlag{ + Name: "debug, D", + Usage: "Enable debug logging", + }, + cli.StringFlag{ + Name: "id", + Usage: "peer id for cluster", + Value: heimdall.NodeID(), + EnvVar: "HEIMDALL_ID", + }, + cli.StringFlag{ + Name: "addr", + Usage: "heimdall peer address to join", + Value: "tcp://127.0.0.1:9000", + EnvVar: "HEIMDALL_ADDR", + }, + cli.DurationFlag{ + Name: "update-interval", + Usage: "interval in which to update with the cluster", + Value: time.Second * 10, + EnvVar: "HEIMDALL_UPDATE_INTERVAL", + }, + cli.StringFlag{ + Name: "interface-name", + Usage: "interface name to use for peer communication (must not exist)", + Value: "darknet", + EnvVar: "HEIMDALL_INTERFACE_NAME", + }, + cli.StringFlag{ + Name: "cert, c", + Usage: "heimdall client certificate", + Value: "", + }, + cli.StringFlag{ + Name: "key, k", + Usage: "heimdall client key", + Value: "", + }, + cli.BoolFlag{ + Name: "skip-verify", + Usage: "skip TLS verification", + }, + } + app.Before = func(c *cli.Context) error { + if c.Bool("debug") { + logrus.SetLevel(logrus.DebugLevel) + } + return nil + } + app.Action = run + + if err := app.Run(os.Args); err != nil { + logrus.Fatal(err) + } +} diff --git a/cmd/hpeer/run.go b/cmd/hpeer/run.go new file mode 100644 index 0000000..e248f1c --- /dev/null +++ b/cmd/hpeer/run.go @@ -0,0 +1,89 @@ +/* + 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 main + +import ( + "os" + "os/signal" + "syscall" + + "github.com/sirupsen/logrus" + "github.com/stellarproject/heimdall" + "github.com/stellarproject/heimdall/peer" + "github.com/stellarproject/heimdall/version" + "github.com/urfave/cli" +) + +func run(cx *cli.Context) error { + cfg := &heimdall.PeerConfig{ + ID: cx.String("id"), + Address: cx.String("addr"), + UpdateInterval: cx.Duration("update-interval"), + InterfaceName: cx.String("interface-name"), + } + p, err := peer.NewPeer(cfg) + if err != nil { + return err + } + + errCh := make(chan error, 1) + + signals := make(chan os.Signal) + signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM, syscall.SIGUSR1) + doneCh := make(chan bool, 1) + go func() { + for { + select { + case sig := <-signals: + switch sig { + case syscall.SIGTERM, syscall.SIGINT: + logrus.Info("shutting down") + if err := p.Stop(); err != nil { + errCh <- err + } + doneCh <- true + default: + logrus.Warnf("unhandled signal %s", sig) + } + } + } + }() + + logrus.WithFields(logrus.Fields{ + "version": version.Version, + "commit": version.GitCommit, + }).Infof("starting %s", version.Name) + go func() { + if err := p.Run(); err != nil { + errCh <- err + } + }() + + select { + case <-doneCh: + return nil + case err := <-errCh: + return err + } + + return nil +} diff --git a/config.go b/config.go index 813f5fd..c8915cd 100644 --- a/config.go +++ b/config.go @@ -21,6 +21,8 @@ package heimdall +import "time" + // Config is the configuration used for the stellar server type Config struct { // ID is the id of the node @@ -39,6 +41,8 @@ type Config struct { EndpointIP string // GatewayPort is the port used for peer communication EndpointPort int + // InterfaceName is the interface used for peer communication + InterfaceName string // RedisURL is the uri to the redis backend RedisURL string // AdvertiseRedisURL is the uri to the public redis backend @@ -54,3 +58,15 @@ type Config struct { // TLSInsecureSkipVerify disables certificate verification TLSInsecureSkipVerify bool } + +// PeerConfig is the configuration of the peer +type PeerConfig struct { + // ID is the id of the peer + ID string + // Address is the GRPC address of the peer to join + Address string + // UpdateInterval is the interval in which to update with the cluster + UpdateInterval time.Duration + // InterfaceName is the interface used for peer communication + InterfaceName string +} diff --git a/peer/peer.go b/peer/peer.go new file mode 100644 index 0000000..21426ce --- /dev/null +++ b/peer/peer.go @@ -0,0 +1,93 @@ +/* + 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 peer + +import ( + "path/filepath" + "time" + + "github.com/sirupsen/logrus" + "github.com/stellarproject/heimdall" + "github.com/stellarproject/heimdall/client" +) + +const ( + wireguardConfigDir = "/etc/wireguard" +) + +// Peer is the non-node peer +type Peer struct { + cfg *heimdall.PeerConfig +} + +// NewPeer returns a new peer +func NewPeer(cfg *heimdall.PeerConfig) (*Peer, error) { + return &Peer{ + cfg: cfg, + }, nil +} + +// Run starts the peer +func (p *Peer) Run() error { + // initial sync + logrus.Infof("connecting to peer %s", p.cfg.Address) + if err := p.sync(); err != nil { + return err + } + + doneCh := make(chan bool) + errCh := make(chan error) + + t := time.NewTicker(p.cfg.UpdateInterval) + go func() { + for range t.C { + if err := p.sync(); err != nil { + errCh <- err + return + } + } + }() + select { + case <-doneCh: + case err := <-errCh: + return err + } + return nil +} + +// Stop stops the peer +func (p *Peer) Stop() error { + // TODO + return nil +} + +func (p *Peer) getWireguardConfigPath() string { + return filepath.Join(wireguardConfigDir, p.cfg.InterfaceName+".conf") +} + +func (p *Peer) getTunnelName() string { + return p.cfg.InterfaceName +} + +func (p *Peer) getClient(addr string) (*client.Client, error) { + return client.NewClient(p.cfg.ID, addr) +} diff --git a/peer/sync.go b/peer/sync.go new file mode 100644 index 0000000..7edf8ad --- /dev/null +++ b/peer/sync.go @@ -0,0 +1,99 @@ +/* + 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 peer + +import ( + "context" + "os" + + "github.com/sirupsen/logrus" + "github.com/stellarproject/heimdall" + v1 "github.com/stellarproject/heimdall/api/v1" + "github.com/stellarproject/heimdall/wg" +) + +func (p *Peer) sync() error { + c, err := p.getClient(p.cfg.Address) + if err != nil { + return err + } + defer c.Close() + + ctx := context.Background() + + resp, err := c.Connect() + if err != nil { + return err + } + + peers := []*v1.Peer{} + for _, peer := range resp.Peers { + // don't add self + if peer.ID == p.cfg.ID { + continue + } + peers = append(peers, peer) + } + + // generate wireguard config + wireguardCfg := &wg.Config{ + Iface: p.cfg.InterfaceName, + Address: resp.Address, + PrivateKey: resp.KeyPair.PrivateKey, + Peers: peers, + DNS: resp.DNS, + } + + wireguardConfigPath := p.getWireguardConfigPath() + tmpCfg, err := wg.GeneratePeerConfig(wireguardCfg, wireguardConfigPath) + if err != nil { + return err + } + + h, err := heimdall.HashConfig(tmpCfg) + if err != nil { + return err + } + + e, err := heimdall.HashConfig(wireguardConfigPath) + if err != nil { + return err + } + + // if config has not change skip update + if h == e { + return nil + } + + logrus.Debugf("updating peer config to version %s", h) + // update wireguard config + if err := os.Rename(tmpCfg, wireguardConfigPath); err != nil { + return err + } + + // reload wireguard + if err := wg.RestartTunnel(ctx, p.getTunnelName()); err != nil { + return err + } + + return nil +} diff --git a/server/connect.go b/server/connect.go index 7472e24..93bf971 100644 --- a/server/connect.go +++ b/server/connect.go @@ -25,42 +25,86 @@ import ( "context" "errors" - "github.com/gogo/protobuf/proto" + ptypes "github.com/gogo/protobuf/types" "github.com/gomodule/redigo/redis" "github.com/sirupsen/logrus" v1 "github.com/stellarproject/heimdall/api/v1" ) var ( - // ErrInvalidAuth is returned when an invalid cluster key is specified upon connect - ErrInvalidAuth = errors.New("invalid cluster key specified") - // ErrNoMaster is returned if there is no configured master yet - ErrNoMaster = errors.New("no configured master") + // ErrAccessDenied is returned when an unauthorized non-node peer attempts to join + ErrAccessDenied = errors.New("access denied") ) -// Connect is called when a peer wants to connect to the node -func (s *Server) Connect(ctx context.Context, req *v1.ConnectRequest) (*v1.ConnectResponse, error) { - logrus.Debugf("connect request from %s", req.ID) - key, err := s.getClusterKey(ctx) +func (s *Server) AuthorizedPeers(ctx context.Context, req *v1.AuthorizedPeersRequest) (*v1.AuthorizedPeersResponse, error) { + authorized, err := redis.Strings(s.local(ctx, "SMEMBERS", authorizedPeersKey)) if err != nil { return nil, err } - if req.ClusterKey != key { - return nil, ErrInvalidAuth - } - data, err := redis.Bytes(s.local(ctx, "GET", masterKey)) - if err != nil { - if err == redis.ErrNil { - return nil, ErrNoMaster - } - return nil, err - } - var master v1.Master - if err := proto.Unmarshal(data, &master); err != nil { - return nil, err - } - - return &v1.ConnectResponse{ - Master: &master, + return &v1.AuthorizedPeersResponse{ + IDs: authorized, + }, nil +} + +// AuthorizePeer authorizes a peer to the cluster +func (s *Server) AuthorizePeer(ctx context.Context, req *v1.AuthorizePeerRequest) (*ptypes.Empty, error) { + logrus.Debugf("authorizing peer %s", req.ID) + if _, err := s.master(ctx, "SADD", authorizedPeersKey, req.ID); err != nil { + return nil, err + } + logrus.Infof("authorized peer %s", req.ID) + return empty, nil +} + +// DeauthorizePeer deauthorizes a peer from the cluster +func (s *Server) DeauthorizePeer(ctx context.Context, req *v1.DeauthorizePeerRequest) (*ptypes.Empty, error) { + logrus.Debugf("deauthorizing peer %s", req.ID) + if _, err := s.master(ctx, "SREM", authorizedPeersKey, req.ID); err != nil { + return nil, err + } + logrus.Infof("deauthorized peer %s", req.ID) + return empty, nil +} + +// Connect is called when a non-node peer wants to connect to the cluster +func (s *Server) Connect(ctx context.Context, req *v1.ConnectRequest) (*v1.ConnectResponse, error) { + authorized, err := redis.Bool(s.local(ctx, "SISMEMBER", authorizedPeersKey, req.ID)) + if err != nil { + return nil, err + } + if !authorized { + logrus.Warnf("unauthorized request attempt from %s", req.ID) + return nil, ErrAccessDenied + } + keyPair, err := s.getOrCreateKeyPair(ctx, req.ID) + if err != nil { + return nil, err + } + nodes, err := s.getNodes(ctx) + if err != nil { + return nil, err + } + dnsAddrs := []string{} + for _, n := range nodes { + dnsAddrs = append(dnsAddrs, n.GatewayIP) + } + + peers, err := s.getPeers(ctx) + if err != nil { + return nil, err + } + ip, _, err := s.getOrAllocatePeerIP(ctx, req.ID) + if err != nil { + return nil, err + } + if err := s.updatePeerInfo(ctx, req.ID); err != nil { + return nil, err + } + // save peer + return &v1.ConnectResponse{ + KeyPair: keyPair, + Address: ip.String() + "/32", + Peers: peers, + DNS: dnsAddrs, }, nil } diff --git a/server/join.go b/server/join.go new file mode 100644 index 0000000..5de5666 --- /dev/null +++ b/server/join.go @@ -0,0 +1,66 @@ +/* + 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" + "errors" + + "github.com/gogo/protobuf/proto" + "github.com/gomodule/redigo/redis" + "github.com/sirupsen/logrus" + v1 "github.com/stellarproject/heimdall/api/v1" +) + +var ( + // ErrInvalidAuth is returned when an invalid cluster key is specified upon connect + ErrInvalidAuth = errors.New("invalid cluster key specified") + // ErrNoMaster is returned if there is no configured master yet + ErrNoMaster = errors.New("no configured master") +) + +// Join is called when a peer wants to join the cluster +func (s *Server) Join(ctx context.Context, req *v1.JoinRequest) (*v1.JoinResponse, error) { + logrus.Debugf("join request from %s", req.ID) + key, err := s.getClusterKey(ctx) + if err != nil { + return nil, err + } + if req.ClusterKey != key { + return nil, ErrInvalidAuth + } + data, err := redis.Bytes(s.local(ctx, "GET", masterKey)) + if err != nil { + if err == redis.ErrNil { + return nil, ErrNoMaster + } + return nil, err + } + var master v1.Master + if err := proto.Unmarshal(data, &master); err != nil { + return nil, err + } + + return &v1.JoinResponse{ + Master: &master, + }, nil +} diff --git a/server/node.go b/server/node.go index 546f882..e8ceba4 100644 --- a/server/node.go +++ b/server/node.go @@ -109,7 +109,7 @@ func (s *Server) configureNode() error { logrus.Warn(err) continue } - m, err := c.Connect(s.cfg.ClusterKey) + m, err := c.Join(s.cfg.ClusterKey) if err != nil { c.Close() logrus.Warn(err) diff --git a/server/peer.go b/server/peer.go index b46858d..c2c8b72 100644 --- a/server/peer.go +++ b/server/peer.go @@ -23,16 +23,16 @@ 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" + "github.com/stellarproject/heimdall" v1 "github.com/stellarproject/heimdall/api/v1" + "github.com/stellarproject/heimdall/wg" ) // Peers returns a list of known peers @@ -74,8 +74,8 @@ func (s *Server) peerUpdater(ctx context.Context) { for range t.C { uctx, cancel := context.WithTimeout(ctx, peerConfigUpdateInterval) - if err := s.updatePeerInfo(uctx); err != nil { - logrus.Errorf("updatePeerInfo: %s", err) + if err := s.updatePeerInfo(uctx, s.cfg.ID); err != nil { + logrus.Errorf("updateLocalPeerInfo: %s", err) cancel() continue } @@ -89,16 +89,24 @@ func (s *Server) peerUpdater(ctx context.Context) { } } -func (s *Server) updatePeerInfo(ctx context.Context) error { - keypair, err := s.getOrCreateKeyPair(ctx, s.cfg.ID) +func (s *Server) updatePeerInfo(ctx context.Context, id string) error { + keypair, err := s.getOrCreateKeyPair(ctx, id) if err != nil { return err } - endpoint := fmt.Sprintf("%s:%d", s.cfg.EndpointIP, s.cfg.EndpointPort) + endpoint, err := s.getPeerEndpoint(ctx, id) + if err != nil { + return err + } // build allowedIPs from routes and peer network allowedIPs := []string{} + + // add peer net + if endpoint == "" { + allowedIPs = append(allowedIPs, s.cfg.PeerNetwork) + } nodes, err := s.getNodes(ctx) if err != nil { return err @@ -106,7 +114,7 @@ func (s *Server) updatePeerInfo(ctx context.Context) error { for _, node := range nodes { // only add the route if a peer to prevent route duplicate - if node.ID != s.cfg.ID { + if node.ID != id { continue } @@ -124,17 +132,16 @@ func (s *Server) updatePeerInfo(ctx context.Context) error { } for _, route := range routes { - // only add the route if a peer to prevent route duplicate - if route.NodeID != s.cfg.ID { + // only add the route if a peer to prevent route blackhole + if route.NodeID != id { continue } - logrus.Debugf("adding route to allowed IPs: %s", route.Network) allowedIPs = append(allowedIPs, route.Network) } n := &v1.Peer{ - ID: s.cfg.ID, + ID: id, KeyPair: keypair, AllowedIPs: allowedIPs, Endpoint: endpoint, @@ -144,9 +151,9 @@ func (s *Server) updatePeerInfo(ctx context.Context) error { if err != nil { return err } - pHash := hashData(data) + pHash := heimdall.HashData(data) - key := s.getPeerKey(s.cfg.ID) + key := s.getPeerKey(id) peerData, err := redis.Bytes(s.local(ctx, "GET", key)) if err != nil { if err != redis.ErrNil { @@ -154,7 +161,7 @@ func (s *Server) updatePeerInfo(ctx context.Context) error { } } - eHash := hashData(peerData) + eHash := heimdall.HashData(peerData) // skip update if same if pHash == eHash { @@ -165,11 +172,25 @@ func (s *Server) updatePeerInfo(ctx context.Context) error { return err } - logrus.Debugf("peer info: endpoint=%s allowedips=%+v", n.Endpoint, n.Endpoint) + logrus.Debugf("peer info updated: id=%s", id) return nil } +func (s *Server) getPeerEndpoint(ctx context.Context, id string) (string, error) { + node, err := s.getNode(ctx, id) + if err != nil { + if err == redis.ErrNil { + return "", nil + } + return "", err + } + if node == nil { + return "", nil + } + return fmt.Sprintf("%s:%d", node.EndpointIP, node.EndpointPort), 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)) @@ -222,25 +243,26 @@ func (s *Server) updatePeerConfig(ctx context.Context) error { } size, _ := gatewayNet.Mask.Size() - wireguardCfg := &wireguardConfig{ - Iface: defaultWireguardInterface, + wireguardCfg := &wg.Config{ + Iface: s.cfg.InterfaceName, PrivateKey: keyPair.PrivateKey, ListenPort: s.cfg.EndpointPort, Address: fmt.Sprintf("%s/%d", gatewayIP.To4().String(), size), Peers: peers, } - tmpCfg, err := generateNodeWireguardConfig(wireguardCfg) + wireguardConfigPath := s.getWireguardConfigPath() + tmpCfg, err := wg.GenerateNodeConfig(wireguardCfg, wireguardConfigPath) if err != nil { return err } - h, err := hashConfig(tmpCfg) + h, err := heimdall.HashConfig(tmpCfg) if err != nil { return err } - e, err := hashConfig(wireguardConfigPath) + e, err := heimdall.HashConfig(wireguardConfigPath) if err != nil { return err } @@ -257,30 +279,9 @@ func (s *Server) updatePeerConfig(ctx context.Context) error { } // reload wireguard - if err := restartWireguardTunnel(ctx); err != nil { + if err := wg.RestartTunnel(ctx, s.getTunnelName()); err != nil { return err } return nil } - -func hashData(data []byte) string { - h := sha256.New() - h.Write(data) - return fmt.Sprintf("%x", h.Sum(nil)) -} - -func hashConfig(cfgPath string) (string, error) { - if _, err := os.Stat(cfgPath); err != nil { - if os.IsNotExist(err) { - return "", nil - } - return "", err - } - peerData, err := ioutil.ReadFile(cfgPath) - if err != nil { - return "", err - } - - return hashData(peerData), nil -} diff --git a/server/server.go b/server/server.go index 6f586af..ec6b087 100644 --- a/server/server.go +++ b/server/server.go @@ -25,6 +25,7 @@ import ( "context" "fmt" "io/ioutil" + "path/filepath" "runtime" "runtime/pprof" "time" @@ -37,22 +38,24 @@ import ( "github.com/stellarproject/heimdall" v1 "github.com/stellarproject/heimdall/api/v1" "github.com/stellarproject/heimdall/client" + "github.com/stellarproject/heimdall/wg" "google.golang.org/grpc" ) const ( - masterKey = "heimdall:master" - clusterKey = "heimdall:key" - keypairsKey = "heimdall:keypairs" - nodesKey = "heimdall:nodes" - nodeJoinKey = "heimdall:join" - peersKey = "heimdall:peers" - routesKey = "heimdall:routes" - peerIPsKey = "heimdall:peerips" - nodeIPsKey = "heimdall:nodeips" - nodeNetworksKey = "heimdall:nodenetworks" + masterKey = "heimdall:master" + clusterKey = "heimdall:key" + keypairsKey = "heimdall:keypairs" + nodesKey = "heimdall:nodes" + nodeJoinKey = "heimdall:join" + peersKey = "heimdall:peers" + routesKey = "heimdall:routes" + peerIPsKey = "heimdall:peerips" + nodeIPsKey = "heimdall:nodeips" + nodeNetworksKey = "heimdall:nodenetworks" + authorizedPeersKey = "heimdall:authorized" - wireguardConfigPath = "/etc/wireguard/darknet.conf" + wireguardConfigDir = "/etc/wireguard" ) var ( @@ -118,7 +121,7 @@ func (s *Server) Run() error { } defer c.Close() - master, err := c.Connect(s.cfg.ClusterKey) + master, err := c.Join(s.cfg.ClusterKey) if err != nil { return err } @@ -149,7 +152,7 @@ func (s *Server) Run() error { go s.nodeHeartbeat(ctx) // initial peer info update - if err := s.updatePeerInfo(ctx); err != nil { + if err := s.updatePeerInfo(ctx, s.cfg.ID); err != nil { return err } @@ -262,7 +265,7 @@ func (s *Server) getOrCreateKeyPair(ctx context.Context, id string) (*v1.KeyPair return nil, err } logrus.Debugf("generating new keypair for %s", s.cfg.ID) - privateKey, publicKey, err := generateWireguardKeys(ctx) + privateKey, publicKey, err := wg.GenerateWireguardKeys(ctx) if err != nil { return nil, err } @@ -315,6 +318,14 @@ func (s *Server) getClusterKey(ctx context.Context) (string, error) { return redis.String(s.local(ctx, "GET", clusterKey)) } +func (s *Server) getWireguardConfigPath() string { + return filepath.Join(wireguardConfigDir, s.cfg.InterfaceName+".conf") +} + +func (s *Server) getTunnelName() string { + return s.cfg.InterfaceName +} + func (s *Server) local(ctx context.Context, cmd string, args ...interface{}) (interface{}, error) { return s.do(ctx, s.rpool, cmd, args...) } diff --git a/utils.go b/utils.go index d784367..011fa0d 100644 --- a/utils.go +++ b/utils.go @@ -22,7 +22,11 @@ package heimdall import ( + "crypto/sha256" + "fmt" + "io/ioutil" "net" + "os" "sort" "github.com/google/uuid" @@ -96,3 +100,26 @@ func getInterfaceIP(iface net.Interface) string { return "" } + +// HashData returns a sha256 sum of the specified data +func HashData(data []byte) string { + h := sha256.New() + h.Write(data) + return fmt.Sprintf("%x", h.Sum(nil)) +} + +// HashConfig returns the sha256 hash of the specified path +func HashConfig(cfgPath string) (string, error) { + if _, err := os.Stat(cfgPath); err != nil { + if os.IsNotExist(err) { + return "", nil + } + return "", err + } + peerData, err := ioutil.ReadFile(cfgPath) + if err != nil { + return "", err + } + + return HashData(peerData), nil +} diff --git a/server/wireguard.go b/wg/wg.go similarity index 62% rename from server/wireguard.go rename to wg/wg.go index 7ce2886..14c3a37 100644 --- a/server/wireguard.go +++ b/wg/wg.go @@ -19,7 +19,7 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package server +package wg import ( "bytes" @@ -38,8 +38,7 @@ import ( ) const ( - defaultWireguardInterface = "darknet" - wireguardTemplate = `# managed by heimdall + wireguardNodeTemplate = `# managed by heimdall [Interface] PrivateKey = {{ .PrivateKey }} ListenPort = {{ .ListenPort }} @@ -47,39 +46,56 @@ Address = {{ .Address }} PostUp = iptables -A FORWARD -i {{ .Iface }} -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; ip6tables -A FORWARD -i {{ .Iface }} -j ACCEPT; ip6tables -t nat -A POSTROUTING -o eth0 -j MASQUERADE 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 }} +# {{ .ID }} [Peer] PublicKey = {{ .KeyPair.PublicKey }} -{{ if .AllowedIPs }}AllowedIPs = {{ allowedIPs .AllowedIPs }}{{ end }} -Endpoint = {{ .Endpoint }} +{{ if .AllowedIPs }}AllowedIPs = {{ csvList .AllowedIPs }}{{ end }}{{ if ne .Endpoint "" }} +Endpoint = {{ .Endpoint }}{{ end }} +{{ end }} +` + wireguardPeerTemplate = `# managed by heimdall +[Interface] +PrivateKey = {{ .PrivateKey }} +Address = {{ .Address }} +DNS = {{ csvList .DNS }} +{{ range .Peers }} +# {{ .ID }} +[Peer] +PublicKey = {{ .KeyPair.PublicKey }} +{{ if .AllowedIPs }}AllowedIPs = {{ csvList .AllowedIPs }}{{ end }}{{ if ne .Endpoint "" }} +Endpoint = {{ .Endpoint }}{{ end }} {{ end }} ` ) -func allowedIPs(s []string) string { +func csvList(s []string) string { return strings.Join(s, ", ") } -type wireguardConfig struct { +// Config is the Wireguard configuration +type Config struct { Iface string PrivateKey string ListenPort int Address string Peers []*v1.Peer + DNS []string } -func generateNodeWireguardConfig(cfg *wireguardConfig) (string, error) { +// GenerateNodeConfig generates the configuration for a node (server) +func GenerateNodeConfig(cfg *Config, cfgPath string) (string, error) { f, err := ioutil.TempFile("", "heimdall-wireguard-") if err != nil { return "", err } - t, err := template.New("wireguard").Funcs(template.FuncMap{ - "allowedIPs": allowedIPs, - }).Parse(wireguardTemplate) + t, err := template.New("wireguard-node").Funcs(template.FuncMap{ + "csvList": csvList, + }).Parse(wireguardNodeTemplate) if err != nil { return "", err } - if err := os.MkdirAll(filepath.Dir(wireguardConfigPath), 0755); err != nil { + if err := os.MkdirAll(filepath.Dir(cfgPath), 0755); err != nil { return "", err } @@ -91,7 +107,33 @@ func generateNodeWireguardConfig(cfg *wireguardConfig) (string, error) { return f.Name(), nil } -func generateWireguardKeys(ctx context.Context) (string, string, error) { +// GeneratePeerConfig generates the configuration for a peer +func GeneratePeerConfig(cfg *Config, cfgPath string) (string, error) { + f, err := ioutil.TempFile("", "heimdall-wireguard-") + if err != nil { + return "", err + } + t, err := template.New("wireguard-peer").Funcs(template.FuncMap{ + "csvList": csvList, + }).Parse(wireguardPeerTemplate) + if err != nil { + return "", err + } + + if err := os.MkdirAll(filepath.Dir(cfgPath), 0755); err != nil { + return "", err + } + + if err := t.Execute(f, cfg); err != nil { + return "", err + } + f.Close() + + return f.Name(), nil +} + +// GenerateWireguardKeys generates a new private/public Wireguard keypair +func GenerateWireguardKeys(ctx context.Context) (string, string, error) { kData, err := wg(ctx, nil, "genkey") if err != nil { return "", "", err @@ -107,25 +149,21 @@ func generateWireguardKeys(ctx context.Context) (string, string, error) { return privateKey, publicKey, nil } -func getTunnelName() string { - return strings.Replace(filepath.Base(wireguardConfigPath), filepath.Ext(filepath.Base(wireguardConfigPath)), "", 1) -} - -func restartWireguardTunnel(ctx context.Context) error { - tunnelName := getTunnelName() - logrus.Infof("restarting tunnel %s", tunnelName) +// RestartTunnel restarts the named tunnel +func RestartTunnel(ctx context.Context, name string) error { + logrus.Infof("restarting tunnel %s", name) d, err := wg(ctx, nil) if err != nil { return err } // only stop if running if string(d) != "" { - d, err := wgquick(ctx, "down", tunnelName) + d, err := wgquick(ctx, "down", name) if err != nil { return errors.Wrap(err, string(d)) } } - u, err := wgquick(ctx, "up", tunnelName) + u, err := wgquick(ctx, "up", name) if err != nil { return errors.Wrap(err, string(u)) }