diff --git a/management/README.md b/management/README.md index b0b40ef96..9bd41a18e 100644 --- a/management/README.md +++ b/management/README.md @@ -16,9 +16,11 @@ docker run -d --name wiretrustee-management \ -p 33073:33073 \ -p 443:443 \ -v /var/lib/wiretrustee/:/var/lib/wiretrustee/ \ +-v /etc/wiretrustee/:/etc/wiretrustee/ \ wiretrustee/wiretrustee:management-v0.0.8-SNAPSHOT-079d35e-amd64 \ --port 33073 \ --datadir /var/lib/wiretrustee/ \ +--hosts-config /etc/wiretrustee/hosts-config.json \ --letsencrypt-domain \ --log-level info ``` @@ -91,13 +93,47 @@ Certificate: docker run -d --name wiretrustee-management \ -p 33073:33073 \ -v /var/lib/wiretrustee/:/var/lib/wiretrustee/ \ +-v /etc/wiretrustee/:/etc/wiretrustee/ \ wiretrustee/wiretrustee:management-v0.0.8-SNAPSHOT-079d35e-amd64 \ --port 33073 \ --datadir /var/lib/wiretrustee/ \ +--hosts-config /etc/wiretrustee/hosts-config.json \ --letsencrypt-domain app.wiretrustee.com \ --log-level debug ``` +### hosts-config.json file example: + +```json +{ + "Stuns": [ + { + "Proto": 2, + "Host": "stun.wiretrustee.com", + "Port": 3468, + "Username": "", + "Password": null + } + ], + "Turns": [ + { + "Proto": 2, + "Host": "stun.wiretrustee.com", + "Port": 3468, + "Username": "some_user", + "Password": "c29tZV9wYXNzd29yZA==" + } + ], + "Signal": { + "Proto": 2, + "Host": "signal.wiretrustee.com", + "Port": 10000, + "Username": "", + "Password": null + } +} +``` + ## For development purposes: Install golang gRpc tools: diff --git a/management/cmd/management.go b/management/cmd/management.go index d317c54b1..7593778fb 100644 --- a/management/cmd/management.go +++ b/management/cmd/management.go @@ -3,7 +3,8 @@ package cmd import ( "flag" "fmt" - server2 "github.com/wiretrustee/wiretrustee/management/server" + "github.com/wiretrustee/wiretrustee/management/server" + "github.com/wiretrustee/wiretrustee/util" "net" "os" @@ -21,6 +22,7 @@ import ( var ( mgmtPort int mgmtDataDir string + mgmtConfig string mgmtLetsencryptDomain string kaep = keepalive.EnforcementPolicy{ @@ -41,24 +43,29 @@ var ( Run: func(cmd *cobra.Command, args []string) { flag.Parse() - if _, err := os.Stat(mgmtDataDir); os.IsNotExist(err) { - err = os.MkdirAll(mgmtDataDir, os.ModeDir) + config, err := loadConfig() + if err != nil { + log.Fatalf("failed reading provided config file: %s: %v", mgmtConfig, err) + } + + if _, err = os.Stat(config.Datadir); os.IsNotExist(err) { + err = os.MkdirAll(config.Datadir, os.ModeDir) if err != nil { - log.Fatalf("failed creating datadir: %s: %v", mgmtDataDir, err) + log.Fatalf("failed creating datadir: %s: %v", config.Datadir, err) } } var opts []grpc.ServerOption - if mgmtLetsencryptDomain != "" { - transportCredentials := credentials.NewTLS(encryption.EnableLetsEncrypt(mgmtDataDir, mgmtLetsencryptDomain)) + if config.LetsEncryptDomain != "" { + transportCredentials := credentials.NewTLS(encryption.EnableLetsEncrypt(config.Datadir, config.LetsEncryptDomain)) opts = append(opts, grpc.Creds(transportCredentials)) } opts = append(opts, grpc.KeepaliveEnforcementPolicy(kaep), grpc.KeepaliveParams(kasp)) grpcServer := grpc.NewServer(opts...) - server, err := server2.NewServer(mgmtDataDir) + server, err := server.NewServer(config) if err != nil { log.Fatalf("failed creating new server: %v", err) } @@ -83,8 +90,28 @@ var ( } ) +func loadConfig() (*server.Config, error) { + config := &server.Config{} + _, err := util.ReadJson(mgmtConfig, config) + if err != nil { + return nil, err + } + if mgmtLetsencryptDomain != "" { + config.LetsEncryptDomain = mgmtLetsencryptDomain + } + if mgmtDataDir != "" { + config.Datadir = mgmtDataDir + } + + return config, err +} + func init() { mgmtCmd.Flags().IntVar(&mgmtPort, "port", 33073, "server port to listen on") mgmtCmd.Flags().StringVar(&mgmtDataDir, "datadir", "/var/lib/wiretrustee/", "server data directory location") + mgmtCmd.Flags().StringVar(&mgmtConfig, "config", "/etc/wiretrustee/management.json", "Wiretrustee config file location. Config params specified via command line (e.g. datadir) have a precedence over configuration from this file") mgmtCmd.Flags().StringVar(&mgmtLetsencryptDomain, "letsencrypt-domain", "", "a domain to issue Let's Encrypt certificate for. Enables TLS using Let's Encrypt. Will fetch and renew certificate, and run the server with TLS") + + rootCmd.MarkFlagRequired("config") //nolint + } diff --git a/management/proto/management.pb.go b/management/proto/management.pb.go index f9b414fe2..3dc0edd23 100644 --- a/management/proto/management.pb.go +++ b/management/proto/management.pb.go @@ -24,22 +24,28 @@ const ( type HostConfig_Protocol int32 const ( - HostConfig_PLAIN HostConfig_Protocol = 0 - HostConfig_TLS HostConfig_Protocol = 1 - HostConfig_DTLS HostConfig_Protocol = 2 + HostConfig_UDP HostConfig_Protocol = 0 + HostConfig_TCP HostConfig_Protocol = 1 + HostConfig_HTTP HostConfig_Protocol = 2 + HostConfig_HTTPS HostConfig_Protocol = 3 + HostConfig_DTLS HostConfig_Protocol = 4 ) // Enum value maps for HostConfig_Protocol. var ( HostConfig_Protocol_name = map[int32]string{ - 0: "PLAIN", - 1: "TLS", - 2: "DTLS", + 0: "UDP", + 1: "TCP", + 2: "HTTP", + 3: "HTTPS", + 4: "DTLS", } HostConfig_Protocol_value = map[string]int32{ - "PLAIN": 0, - "TLS": 1, - "DTLS": 2, + "UDP": 0, + "TCP": 1, + "HTTP": 2, + "HTTPS": 3, + "DTLS": 4, } ) @@ -175,8 +181,6 @@ type SyncResponse struct { WiretrusteeConfig *WiretrusteeConfig `protobuf:"bytes,1,opt,name=wiretrusteeConfig,proto3" json:"wiretrusteeConfig,omitempty"` PeerConfig *PeerConfig `protobuf:"bytes,2,opt,name=peerConfig,proto3" json:"peerConfig,omitempty"` RemotePeers []*RemotePeerConfig `protobuf:"bytes,3,rep,name=remotePeers,proto3" json:"remotePeers,omitempty"` - // Deprecated: used for compatibility - Peers []string `protobuf:"bytes,4,rep,name=peers,proto3" json:"peers,omitempty"` } func (x *SyncResponse) Reset() { @@ -232,13 +236,6 @@ func (x *SyncResponse) GetRemotePeers() []*RemotePeerConfig { return nil } -func (x *SyncResponse) GetPeers() []string { - if x != nil { - return x.Peers - } - return nil -} - type RegisterPeerRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -502,9 +499,9 @@ type HostConfig struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"` - Port int32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` - Protocol HostConfig_Protocol `protobuf:"varint,3,opt,name=protocol,proto3,enum=management.HostConfig_Protocol" json:"protocol,omitempty"` + // URI of the resource e.g. turns://stun.wiretrustee.com:4430 or signal.wiretrustee.com:10000 + Uri string `protobuf:"bytes,1,opt,name=uri,proto3" json:"uri,omitempty"` + Protocol HostConfig_Protocol `protobuf:"varint,2,opt,name=protocol,proto3,enum=management.HostConfig_Protocol" json:"protocol,omitempty"` } func (x *HostConfig) Reset() { @@ -539,25 +536,18 @@ func (*HostConfig) Descriptor() ([]byte, []int) { return file_management_proto_rawDescGZIP(), []int{8} } -func (x *HostConfig) GetHost() string { +func (x *HostConfig) GetUri() string { if x != nil { - return x.Host + return x.Uri } return "" } -func (x *HostConfig) GetPort() int32 { - if x != nil { - return x.Port - } - return 0 -} - func (x *HostConfig) GetProtocol() HostConfig_Protocol { if x != nil { return x.Protocol } - return HostConfig_PLAIN + return HostConfig_UDP } // ProtectedHostConfig is similar to HostConfig but has additional user and password @@ -755,7 +745,7 @@ var file_management_proto_rawDesc = []byte{ 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x0d, 0x0a, 0x0b, 0x53, 0x79, 0x6e, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x22, 0xe9, 0x01, 0x0a, 0x0c, 0x53, 0x79, 0x6e, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x73, 0x74, 0x22, 0xd3, 0x01, 0x0a, 0x0c, 0x53, 0x79, 0x6e, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x11, 0x77, 0x69, 0x72, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x57, 0x69, 0x72, 0x65, @@ -768,78 +758,76 @@ var file_management_proto_rawDesc = []byte{ 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0b, 0x72, 0x65, 0x6d, - 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x65, 0x65, 0x72, - 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x22, 0x43, - 0x0a, 0x13, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x74, 0x75, 0x70, - 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x74, 0x75, 0x70, - 0x4b, 0x65, 0x79, 0x22, 0x16, 0x0a, 0x14, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x50, - 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5f, 0x0a, 0x11, 0x53, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, - 0x65, 0x79, 0x12, 0x38, 0x0a, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x52, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, 0x22, 0x07, 0x0a, 0x05, - 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0xa8, 0x01, 0x0a, 0x11, 0x57, 0x69, 0x72, 0x65, 0x74, 0x72, - 0x75, 0x73, 0x74, 0x65, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2c, 0x0a, 0x05, 0x73, - 0x74, 0x75, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, + 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x22, 0x43, 0x0a, 0x13, 0x52, 0x65, 0x67, 0x69, + 0x73, 0x74, 0x65, 0x72, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x74, 0x75, 0x70, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x74, 0x75, 0x70, 0x4b, 0x65, 0x79, 0x22, 0x16, 0x0a, + 0x14, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5f, 0x0a, 0x11, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, + 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x38, 0x0a, 0x09, + 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x65, 0x78, 0x70, + 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, + 0xa8, 0x01, 0x0a, 0x11, 0x57, 0x69, 0x72, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x65, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2c, 0x0a, 0x05, 0x73, 0x74, 0x75, 0x6e, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x05, 0x73, 0x74, + 0x75, 0x6e, 0x73, 0x12, 0x35, 0x0a, 0x05, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, + 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x52, 0x05, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x12, 0x2e, 0x0a, 0x06, 0x73, 0x69, + 0x67, 0x6e, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x52, 0x05, 0x73, 0x74, 0x75, 0x6e, 0x73, 0x12, 0x35, 0x0a, 0x05, 0x74, 0x75, 0x72, - 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x48, - 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x05, 0x74, 0x75, 0x72, 0x6e, 0x73, - 0x12, 0x2e, 0x0a, 0x06, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x69, 0x67, 0x52, 0x06, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x22, 0x98, 0x01, 0x0a, 0x0a, 0x48, + 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x69, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x69, 0x12, 0x3b, 0x0a, 0x08, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, + 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x22, 0x3b, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x07, 0x0a, 0x03, 0x55, 0x44, 0x50, 0x10, 0x00, 0x12, 0x07, 0x0a, + 0x03, 0x54, 0x43, 0x50, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x02, + 0x12, 0x09, 0x0a, 0x05, 0x48, 0x54, 0x54, 0x50, 0x53, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x44, + 0x54, 0x4c, 0x53, 0x10, 0x04, 0x22, 0x7d, 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, + 0x65, 0x64, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x36, 0x0a, 0x0a, + 0x68, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x6f, - 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, - 0x22, 0x9b, 0x01, 0x0a, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, - 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, - 0x6f, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x3b, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x63, 0x6f, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x6d, 0x61, 0x6e, 0x61, - 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x63, 0x6f, 0x6c, 0x22, 0x28, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, - 0x12, 0x09, 0x0a, 0x05, 0x50, 0x4c, 0x41, 0x49, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x54, - 0x4c, 0x53, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x54, 0x4c, 0x53, 0x10, 0x02, 0x22, 0x7d, - 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x48, 0x6f, 0x73, 0x74, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x36, 0x0a, 0x0a, 0x68, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, - 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x52, 0x0a, 0x68, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, - 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, - 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x38, 0x0a, - 0x0a, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x64, 0x6e, 0x73, 0x22, 0x4e, 0x0a, 0x10, 0x52, 0x65, 0x6d, 0x6f, 0x74, - 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x77, - 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, - 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, - 0x65, 0x64, 0x49, 0x70, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x6c, 0x6c, - 0x6f, 0x77, 0x65, 0x64, 0x49, 0x70, 0x73, 0x32, 0xa9, 0x02, 0x0a, 0x11, 0x4d, 0x61, 0x6e, 0x61, - 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x53, 0x0a, - 0x0c, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x50, 0x65, 0x65, 0x72, 0x12, 0x1f, 0x2e, - 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x65, 0x72, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, - 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x67, 0x69, - 0x73, 0x74, 0x65, 0x72, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x46, 0x0a, 0x04, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, - 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, - 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x0c, 0x47, 0x65, - 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, - 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1d, 0x2e, - 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, - 0x0a, 0x09, 0x69, 0x73, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, - 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x11, - 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, - 0x79, 0x22, 0x00, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0a, 0x68, 0x6f, 0x73, 0x74, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, + 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, + 0x77, 0x6f, 0x72, 0x64, 0x22, 0x38, 0x0a, 0x0a, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, + 0x64, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x6e, 0x73, 0x22, 0x4e, + 0x0a, 0x10, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1e, + 0x0a, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x49, 0x70, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x49, 0x70, 0x73, 0x32, 0xa9, + 0x02, 0x0a, 0x11, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x12, 0x53, 0x0a, 0x0c, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, + 0x50, 0x65, 0x65, 0x72, 0x12, 0x1f, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x50, 0x65, 0x65, 0x72, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x46, 0x0a, 0x04, 0x53, 0x79, 0x6e, + 0x63, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, + 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, + 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, + 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x30, + 0x01, 0x12, 0x42, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, + 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, + 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x09, 0x69, 0x73, 0x48, 0x65, 0x61, 0x6c, 0x74, + 0x68, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/management/proto/management.proto b/management/proto/management.proto index 61e792e0b..df7ef844f 100644 --- a/management/proto/management.proto +++ b/management/proto/management.proto @@ -41,9 +41,6 @@ message SyncResponse { PeerConfig peerConfig = 2; repeated RemotePeerConfig remotePeers = 3; - - // Deprecated: used for compatibility - repeated string peers = 4; } message RegisterPeerRequest { @@ -78,14 +75,16 @@ message WiretrusteeConfig { // HostConfig describes connection properties of some server (e.g. STUN, Signal, Management) message HostConfig { - string host = 1; - int32 port = 2; - Protocol protocol = 3; + // URI of the resource e.g. turns://stun.wiretrustee.com:4430 or signal.wiretrustee.com:10000 + string uri = 1; + Protocol protocol = 2; enum Protocol { - PLAIN = 0; - TLS = 1; - DTLS = 2; + UDP = 0; + TCP = 1; + HTTP = 2; + HTTPS = 3; + DTLS = 4; } } // ProtectedHostConfig is similar to HostConfig but has additional user and password diff --git a/management/server/account.go b/management/server/account.go new file mode 100644 index 000000000..15eb5d471 --- /dev/null +++ b/management/server/account.go @@ -0,0 +1,119 @@ +package server + +import ( + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "net" + "sync" +) + +type AccountManager struct { + Store Store + // mutex to synchronise account operations (e.g. generating Peer IP address inside the Network) + mux sync.Mutex +} + +// Account represents a unique account of the system +type Account struct { + Id string + SetupKeys map[string]*SetupKey + Network *Network + Peers []*Peer +} + +// SetupKey represents a pre-authorized key used to register machines (peers) +// One key might have multiple machines +type SetupKey struct { + Key string +} + +// Peer represents a machine connected to the network. +// The Peer is a Wireguard peer identified by a public key +type Peer struct { + // Wireguard public key + Key string + // A setup key this peer was registered with + SetupKey *SetupKey + // IP address of the Peer + IP net.IP +} + +// NewManager creates a new AccountManager with a provided Store +func NewManager(store Store) *AccountManager { + return &AccountManager{ + Store: store, + mux: sync.Mutex{}, + } +} + +// GetPeer returns a peer from a Store +func (manager *AccountManager) GetPeer(peerKey string) (*Peer, error) { + manager.mux.Lock() + defer manager.mux.Unlock() + + peer, err := manager.Store.GetPeer(peerKey) + if err != nil { + return nil, status.Errorf(codes.NotFound, "provided peer key doesn't exists %s", peerKey) + } + + return peer, nil +} + +// GetPeersForAPeer returns a list of peers available for a given peer (key) +// Effectively all the peers of the original peer's account except for the peer itself +func (manager *AccountManager) GetPeersForAPeer(peerKey string) ([]*Peer, error) { + manager.mux.Lock() + defer manager.mux.Unlock() + + account, err := manager.Store.GetPeerAccount(peerKey) + if err != nil { + return nil, status.Errorf(codes.Internal, "Invalid peer key %s", peerKey) + } + + var res []*Peer + for _, peer := range account.Peers { + if peer.Key != peerKey { + res = append(res, peer) + } + } + + return res, nil +} + +// AddPeer adds a new peer to the Store. +// Each Account has a list of pre-authorised SetupKey and if no Account has a given key err wit ha code codes.Unauthenticated +// will be returned, meaning the key is invalid +// Each new Peer will be assigned a new next net.IP from the Account.Network and Account.Network.LastIP will be updated (IP's are not reused). +func (manager *AccountManager) AddPeer(setupKey string, peerKey string) (*Peer, error) { + manager.mux.Lock() + defer manager.mux.Unlock() + + account, err := manager.Store.GetAccountBySetupKey(setupKey) + if err != nil { + //todo + return nil, err + } + + var takenIps []net.IP + for _, peer := range account.Peers { + takenIps = append(takenIps, peer.IP) + } + + network := account.Network + nextIp, _ := AllocatePeerIP(network.Net, takenIps) + + newPeer := &Peer{ + Key: peerKey, + SetupKey: &SetupKey{Key: setupKey}, + IP: nextIp, + } + + account.Peers = append(account.Peers, newPeer) + err = manager.Store.SaveAccount(account) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed adding peer") + } + + return newPeer, nil + +} diff --git a/management/server/config.go b/management/server/config.go new file mode 100644 index 000000000..bb8059de6 --- /dev/null +++ b/management/server/config.go @@ -0,0 +1,30 @@ +package server + +type Protocol string + +const ( + UDP Protocol = "udp" + DTLS Protocol = "dtls" + TCP Protocol = "tcp" + HTTP Protocol = "http" + HTTPS Protocol = "https" +) + +// Config of the Management service +type Config struct { + Stuns []*Host + Turns []*Host + Signal *Host + + Datadir string + LetsEncryptDomain string +} + +// Host represents a Wiretrustee host (e.g. STUN, TURN, Signal) +type Host struct { + Proto Protocol + // URI e.g. turns://stun.wiretrustee.com:4430 or signal.wiretrustee.com:10000 + URI string + Username string + Password []byte +} diff --git a/management/store/file_store.go b/management/server/file_store.go similarity index 61% rename from management/store/file_store.go rename to management/server/file_store.go index f13e3b473..683c51a3c 100644 --- a/management/store/file_store.go +++ b/management/server/file_store.go @@ -1,4 +1,4 @@ -package store +package server import ( "os" @@ -26,6 +26,9 @@ type FileStore struct { storeFile string `json:"-"` } +type StoredAccount struct { +} + // NewStore restores a store from the file located in the datadir func NewStore(dataDir string) (*FileStore, error) { return restore(filepath.Join(dataDir, storeFileName)) @@ -59,15 +62,13 @@ func restore(file string) (*FileStore, error) { store := read.(*FileStore) store.storeFile = file store.SetupKeyId2AccountId = make(map[string]string) + store.PeerKeyId2AccountId = make(map[string]string) for accountId, account := range store.Accounts { for setupKeyId := range account.SetupKeys { store.SetupKeyId2AccountId[strings.ToLower(setupKeyId)] = accountId } - } - store.PeerKeyId2AccountId = make(map[string]string) - for accountId, account := range store.Accounts { - for peerId := range account.Peers { - store.PeerKeyId2AccountId[strings.ToLower(peerId)] = accountId + for _, peer := range account.Peers { + store.PeerKeyId2AccountId[strings.ToLower(peer.Key)] = accountId } } @@ -80,47 +81,32 @@ func (s *FileStore) persist(file string) error { return util.WriteJson(file, s) } -// PeerExists checks whether peer exists or not -func (s *FileStore) PeerExists(peerKey string) bool { +// GetPeer returns a peer from a Store +func (s *FileStore) GetPeer(peerKey string) (*Peer, error) { s.mux.Lock() defer s.mux.Unlock() - _, accountIdFound := s.PeerKeyId2AccountId[peerKey] - return accountIdFound -} - -// AddPeer adds peer to the store and associates it with a Account and a SetupKey. Returns related Account -// Each Account has a list of pre-authorised SetupKey and if no Account has a given key err will be returned, meaning the key is invalid -func (s *FileStore) AddPeer(setupKey string, peerKey string) error { - s.mux.Lock() - defer s.mux.Unlock() - - accountId, accountIdFound := s.SetupKeyId2AccountId[strings.ToLower(setupKey)] + accountId, accountIdFound := s.PeerKeyId2AccountId[peerKey] if !accountIdFound { - return status.Errorf(codes.NotFound, "Provided setup key doesn't exists") + return nil, status.Errorf(codes.Internal, "account not found") } - account, accountFound := s.Accounts[accountId] - if !accountFound { - return status.Errorf(codes.Internal, "Invalid setup key") - } - - key, setupKeyFound := account.SetupKeys[strings.ToLower(setupKey)] - if !setupKeyFound { - return status.Errorf(codes.Internal, "Invalid setup key") - } - - account.Peers[peerKey] = &Peer{Key: peerKey, SetupKey: key} - s.PeerKeyId2AccountId[peerKey] = accountId - err := s.persist(s.storeFile) + account, err := s.GetAccount(accountId) if err != nil { - return err + return nil, err } - return nil + + for _, peer := range account.Peers { + if peer.Key == peerKey { + return peer, nil + } + } + + return nil, status.Errorf(codes.NotFound, "peer not found") } -// AddAccount adds new account to the store. -func (s *FileStore) AddAccount(account *Account) error { +// SaveAccount updates an existing account or adds a new one +func (s *FileStore) SaveAccount(account *Account) error { s.mux.Lock() defer s.mux.Unlock() @@ -133,6 +119,10 @@ func (s *FileStore) AddAccount(account *Account) error { s.SetupKeyId2AccountId[strings.ToLower(keyId)] = account.Id } + for _, peer := range account.Peers { + s.PeerKeyId2AccountId[peer.Key] = account.Id + } + err := s.persist(s.storeFile) if err != nil { return err @@ -141,9 +131,32 @@ func (s *FileStore) AddAccount(account *Account) error { return nil } -// GetPeersForAPeer returns a list of peers available for a given peer (key) -// Effectively all the peers of the original peer's account if any -func (s *FileStore) GetPeersForAPeer(peerKey string) ([]string, error) { +func (s *FileStore) GetAccountBySetupKey(setupKey string) (*Account, error) { + + accountId, accountIdFound := s.SetupKeyId2AccountId[strings.ToLower(setupKey)] + if !accountIdFound { + return nil, status.Errorf(codes.NotFound, "provided setup key doesn't exists") + } + + account, err := s.GetAccount(accountId) + if err != nil { + return nil, err + } + + return account, nil +} + +func (s *FileStore) GetAccount(accountId string) (*Account, error) { + + account, accountFound := s.Accounts[accountId] + if !accountFound { + return nil, status.Errorf(codes.Internal, "account not found") + } + + return account, nil +} + +func (s *FileStore) GetPeerAccount(peerKey string) (*Account, error) { s.mux.Lock() defer s.mux.Unlock() @@ -152,16 +165,5 @@ func (s *FileStore) GetPeersForAPeer(peerKey string) ([]string, error) { return nil, status.Errorf(codes.NotFound, "Provided peer key doesn't exists %s", peerKey) } - account, accountFound := s.Accounts[accountId] - if !accountFound { - return nil, status.Errorf(codes.Internal, "Invalid peer key %s", peerKey) - } - peers := make([]string, 0, len(account.Peers)) - for p := range account.Peers { - if p != peerKey { - peers = append(peers, p) - } - } - - return peers, nil + return s.GetAccount(accountId) } diff --git a/management/server/management_test.go b/management/server/management_test.go index 720907b69..288a837f8 100644 --- a/management/server/management_test.go +++ b/management/server/management_test.go @@ -2,7 +2,7 @@ package server_test import ( "context" - server2 "github.com/wiretrustee/wiretrustee/management/server" + server "github.com/wiretrustee/wiretrustee/management/server" "io/ioutil" "math/rand" "net" @@ -33,7 +33,7 @@ var _ = Describe("Management service", func() { var ( addr string - server *grpc.Server + s *grpc.Server dataDir string client mgmtProto.ManagementServiceClient serverPubKey wgtypes.Key @@ -50,11 +50,17 @@ var _ = Describe("Management service", func() { err = util.CopyFileContents("testdata/store.json", filepath.Join(dataDir, "store.json")) Expect(err).NotTo(HaveOccurred()) var listener net.Listener - server, listener = startServer(dataDir) + + config := &server.Config{} + _, err = util.ReadJson("testdata/management.json", config) + Expect(err).NotTo(HaveOccurred()) + config.Datadir = dataDir + + s, listener = startServer(config) addr = listener.Addr().String() client, conn = createRawClient(addr) - // server public key + // s public key resp, err := client.GetServerKey(context.TODO(), &mgmtProto.Empty{}) Expect(err).NotTo(HaveOccurred()) serverPubKey, err = wgtypes.ParseKey(resp.Key) @@ -63,7 +69,7 @@ var _ = Describe("Management service", func() { }) AfterEach(func() { - server.Stop() + s.Stop() err := conn.Close() Expect(err).NotTo(HaveOccurred()) err = os.RemoveAll(dataDir) @@ -82,6 +88,54 @@ var _ = Describe("Management service", func() { Context("when calling Sync endpoint", func() { + Context("when there is a new peer registered", func() { + Specify("a proper configuration is returned", func() { + key, _ := wgtypes.GenerateKey() + registerPeerWithValidSetupKey(key, client) + + encryptedBytes, err := encryption.EncryptMessage(serverPubKey, key, &mgmtProto.SyncRequest{}) + Expect(err).NotTo(HaveOccurred()) + + sync, err := client.Sync(context.TODO(), &mgmtProto.EncryptedMessage{ + WgPubKey: key.PublicKey().String(), + Body: encryptedBytes, + }) + Expect(err).NotTo(HaveOccurred()) + + encryptedResponse := &mgmtProto.EncryptedMessage{} + err = sync.RecvMsg(encryptedResponse) + Expect(err).NotTo(HaveOccurred()) + + resp := &mgmtProto.SyncResponse{} + err = encryption.DecryptMessage(serverPubKey, key, encryptedResponse.Body, resp) + Expect(err).NotTo(HaveOccurred()) + + Expect(resp.PeerConfig.Address).To(BeEquivalentTo("100.64.0.1")) + + expectedSignalConfig := &mgmtProto.HostConfig{ + Uri: "signal.wiretrustee.com:10000", + Protocol: mgmtProto.HostConfig_HTTP, + } + expectedStunsConfig := &mgmtProto.HostConfig{ + Uri: "stun:stun.wiretrustee.com:3468", + Protocol: mgmtProto.HostConfig_UDP, + } + expectedTurnsConfig := &mgmtProto.ProtectedHostConfig{ + HostConfig: &mgmtProto.HostConfig{ + Uri: "turn:stun.wiretrustee.com:3468", + Protocol: mgmtProto.HostConfig_UDP, + }, + User: "some_user", + Password: "some_password", + } + + Expect(resp.WiretrusteeConfig.Signal).To(BeEquivalentTo(expectedSignalConfig)) + Expect(resp.WiretrusteeConfig.Stuns).To(ConsistOf(expectedStunsConfig)) + Expect(resp.WiretrusteeConfig.Turns).To(ConsistOf(expectedTurnsConfig)) + + }) + }) + Context("when there are 3 peers registered under one account", func() { Specify("a list containing other 2 peers is returned", func() { key, _ := wgtypes.GenerateKey() @@ -112,8 +166,9 @@ var _ = Describe("Management service", func() { err = pb.Unmarshal(decryptedBytes, resp) Expect(err).NotTo(HaveOccurred()) - Expect(resp.GetPeers()).To(HaveLen(2)) - Expect(resp.GetPeers()).To(ContainElements(key1.PublicKey().String(), key2.PublicKey().String())) + Expect(resp.GetRemotePeers()).To(HaveLen(2)) + peers := []string{resp.GetRemotePeers()[0].WgPubKey, resp.GetRemotePeers()[1].WgPubKey} + Expect(peers).To(ContainElements(key1.PublicKey().String(), key2.PublicKey().String())) }) }) @@ -143,7 +198,7 @@ var _ = Describe("Management service", func() { Expect(err).NotTo(HaveOccurred()) resp := &mgmtProto.SyncResponse{} err = pb.Unmarshal(decryptedBytes, resp) - Expect(resp.GetPeers()).To(HaveLen(0)) + Expect(resp.GetRemotePeers()).To(HaveLen(0)) wg := sync2.WaitGroup{} wg.Add(1) @@ -167,8 +222,8 @@ var _ = Describe("Management service", func() { wg.Wait() Expect(err).NotTo(HaveOccurred()) - Expect(resp.GetPeers()).To(ContainElements(key1.PublicKey().String())) - Expect(resp.GetPeers()).To(HaveLen(1)) + Expect(resp.GetRemotePeers()).To(HaveLen(1)) + Expect(resp.GetRemotePeers()[0].WgPubKey).To(BeEquivalentTo(key1.PublicKey().String())) }) }) }) @@ -291,6 +346,52 @@ var _ = Describe("Management service", func() { }) }) }) + + Context("when there are peers registered under one account concurrently", func() { + Specify("then there are no duplicate IPs", func() { + + initialPeers := 30 + + ipChannel := make(chan string, 20) + for i := 0; i < initialPeers; i++ { + go func() { + key, _ := wgtypes.GenerateKey() + registerPeerWithValidSetupKey(key, client) + encryptedBytes, err := encryption.EncryptMessage(serverPubKey, key, &mgmtProto.SyncRequest{}) + Expect(err).NotTo(HaveOccurred()) + + // open stream + sync, err := client.Sync(context.TODO(), &mgmtProto.EncryptedMessage{ + WgPubKey: key.PublicKey().String(), + Body: encryptedBytes, + }) + Expect(err).NotTo(HaveOccurred()) + encryptedResponse := &mgmtProto.EncryptedMessage{} + err = sync.RecvMsg(encryptedResponse) + Expect(err).NotTo(HaveOccurred()) + + resp := &mgmtProto.SyncResponse{} + err = encryption.DecryptMessage(serverPubKey, key, encryptedResponse.Body, resp) + Expect(err).NotTo(HaveOccurred()) + + ipChannel <- resp.GetPeerConfig().Address + + }() + } + + ips := make(map[string]struct{}) + for ip := range ipChannel { + if _, ok := ips[ip]; ok { + Fail("found duplicate IP: " + ip) + } + ips[ip] = struct{}{} + if len(ips) == initialPeers { + break + } + } + close(ipChannel) + }) + }) }) func registerPeerWithValidSetupKey(key wgtypes.Key, client mgmtProto.ManagementServiceClient) *mgmtProto.RegisterPeerResponse { @@ -320,13 +421,13 @@ func createRawClient(addr string) (mgmtProto.ManagementServiceClient, *grpc.Clie return mgmtProto.NewManagementServiceClient(conn), conn } -func startServer(dataDir string) (*grpc.Server, net.Listener) { +func startServer(config *server.Config) (*grpc.Server, net.Listener) { lis, err := net.Listen("tcp", ":0") Expect(err).NotTo(HaveOccurred()) s := grpc.NewServer() - server, err := server2.NewServer(dataDir) + mgmtServer, err := server.NewServer(config) Expect(err).NotTo(HaveOccurred()) - mgmtProto.RegisterManagementServiceServer(s, server) + mgmtProto.RegisterManagementServiceServer(s, mgmtServer) go func() { if err := s.Serve(lis); err != nil { Expect(err).NotTo(HaveOccurred()) diff --git a/management/server/network.go b/management/server/network.go new file mode 100644 index 000000000..b24dc8556 --- /dev/null +++ b/management/server/network.go @@ -0,0 +1,69 @@ +package server + +import ( + "encoding/binary" + "fmt" + "net" +) + +var ( + upperIPv4 = []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 255, 255, 255, 255} + upperIPv6 = []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} +) + +type Network struct { + Id string + Net net.IPNet + Dns string +} + +// AllocatePeerIP pics an available IP from an net.IPNet. +// This method considers already taken IPs and reuses IPs if there are gaps in takenIps +// E.g. if ipNet=100.30.0.0/16 and takenIps=[100.30.0.1, 100.30.0.5] then the result would be 100.30.0.2 +func AllocatePeerIP(ipNet net.IPNet, takenIps []net.IP) (net.IP, error) { + takenIpMap := make(map[string]net.IP) + takenIpMap[ipNet.IP.String()] = ipNet.IP + for _, ip := range takenIps { + takenIpMap[ip.String()] = ip + } + for ip := ipNet.IP.Mask(ipNet.Mask); ipNet.Contains(ip); ip = GetNextIP(ip) { + if _, ok := takenIpMap[ip.String()]; !ok { + return ip, nil + } + } + + return nil, fmt.Errorf("failed allocating new IP for the ipNet %s and takenIps %s", ipNet.String(), takenIps) +} + +// GetNextIP returns the next IP from the given IP address. If the given IP is +// the last IP of a v4 or v6 range, the same IP is returned. +// Credits to Cilium team. +// Copyright 2017-2020 Authors of Cilium +func GetNextIP(ip net.IP) net.IP { + if ip.Equal(upperIPv4) || ip.Equal(upperIPv6) { + return ip + } + + nextIP := make(net.IP, len(ip)) + switch len(ip) { + case net.IPv4len: + ipU32 := binary.BigEndian.Uint32(ip) + ipU32++ + binary.BigEndian.PutUint32(nextIP, ipU32) + return nextIP + case net.IPv6len: + ipU64 := binary.BigEndian.Uint64(ip[net.IPv6len/2:]) + ipU64++ + binary.BigEndian.PutUint64(nextIP[net.IPv6len/2:], ipU64) + if ipU64 == 0 { + ipU64 = binary.BigEndian.Uint64(ip[:net.IPv6len/2]) + ipU64++ + binary.BigEndian.PutUint64(nextIP[:net.IPv6len/2], ipU64) + } else { + copy(nextIP[:net.IPv6len/2], ip[:net.IPv6len/2]) + } + return nextIP + default: + return ip + } +} diff --git a/management/server/server.go b/management/server/server.go index f9d9ac760..14e9bebf2 100644 --- a/management/server/server.go +++ b/management/server/server.go @@ -2,7 +2,7 @@ package server import ( "context" - "github.com/wiretrustee/wiretrustee/management/store" + "fmt" "sync" "time" @@ -17,33 +17,38 @@ import ( // Server an instance of a Management server type Server struct { - Store *store.FileStore - wgKey wgtypes.Key + accountManager *AccountManager + wgKey wgtypes.Key proto.UnimplementedManagementServiceServer peerChannels map[string]chan *UpdateChannelMessage channelsMux *sync.Mutex + config *Config } +// AllowedIPsFormat generates Wireguard AllowedIPs format (e.g. 100.30.30.1/32) +const AllowedIPsFormat = "%s/32" + type UpdateChannelMessage struct { Update *proto.SyncResponse } // NewServer creates a new Management server -func NewServer(dataDir string) (*Server, error) { +func NewServer(config *Config) (*Server, error) { key, err := wgtypes.GeneratePrivateKey() if err != nil { return nil, err } - store, err := store.NewStore(dataDir) + store, err := NewStore(config.Datadir) if err != nil { return nil, err } return &Server{ - Store: store, wgKey: key, // peerKey -> event channel - peerChannels: make(map[string]chan *UpdateChannelMessage), - channelsMux: &sync.Mutex{}, + peerChannels: make(map[string]chan *UpdateChannelMessage), + channelsMux: &sync.Mutex{}, + accountManager: NewManager(store), + config: config, }, nil } @@ -73,8 +78,8 @@ func (s *Server) Sync(req *proto.EncryptedMessage, srv proto.ManagementService_S return status.Errorf(codes.InvalidArgument, "provided wgPubKey %s is invalid", peerKey.String()) } - exists := s.Store.PeerExists(peerKey.String()) - if !exists { + peer, err := s.accountManager.GetPeer(peerKey.String()) + if err != nil { return status.Errorf(codes.Unauthenticated, "provided peer with the key wgPubKey %s is not registered", peerKey.String()) } @@ -84,7 +89,7 @@ func (s *Server) Sync(req *proto.EncryptedMessage, srv proto.ManagementService_S return status.Errorf(codes.InvalidArgument, "invalid request message") } - err = s.sendInitialSync(peerKey, srv) + err = s.sendInitialSync(peerKey, peer, srv) if err != nil { return err } @@ -132,28 +137,28 @@ func (s *Server) RegisterPeer(ctx context.Context, req *proto.RegisterPeerReques s.channelsMux.Lock() defer s.channelsMux.Unlock() - err := s.Store.AddPeer(req.SetupKey, req.Key) + peer, err := s.accountManager.AddPeer(req.SetupKey, req.Key) if err != nil { return &proto.RegisterPeerResponse{}, status.Errorf(codes.NotFound, "provided setup key doesn't exists") } - peers, err := s.Store.GetPeersForAPeer(req.Key) + peers, err := s.accountManager.GetPeersForAPeer(peer.Key) if err != nil { //todo return a proper error return nil, err } // notify other peers of our registration - for _, peer := range peers { - if channel, ok := s.peerChannels[peer]; ok { + for _, remotePeer := range peers { + if channel, ok := s.peerChannels[remotePeer.Key]; ok { // exclude notified peer and add ourselves - peersToSend := []string{req.Key} + peersToSend := []*Peer{peer} for _, p := range peers { - if peer != p { + if remotePeer.Key != p.Key { peersToSend = append(peersToSend, p) } } - update := &proto.SyncResponse{Peers: peersToSend} + update := toSyncResponse(s.config, peer, peersToSend) channel <- &UpdateChannelMessage{Update: update} } } @@ -161,6 +166,73 @@ func (s *Server) RegisterPeer(ctx context.Context, req *proto.RegisterPeerReques return &proto.RegisterPeerResponse{}, nil } +func toResponseProto(configProto Protocol) proto.HostConfig_Protocol { + switch configProto { + case UDP: + return proto.HostConfig_UDP + case DTLS: + return proto.HostConfig_DTLS + case HTTP: + return proto.HostConfig_HTTP + case HTTPS: + return proto.HostConfig_HTTPS + case TCP: + return proto.HostConfig_TCP + default: + //mbragin: todo something better? + panic(fmt.Errorf("unexpected config protocol type %v", configProto)) + } +} + +func toSyncResponse(config *Config, peer *Peer, peers []*Peer) *proto.SyncResponse { + + var stuns []*proto.HostConfig + for _, stun := range config.Stuns { + stuns = append(stuns, &proto.HostConfig{ + Uri: stun.URI, + Protocol: toResponseProto(stun.Proto), + }) + } + var turns []*proto.ProtectedHostConfig + for _, turn := range config.Turns { + turns = append(turns, &proto.ProtectedHostConfig{ + HostConfig: &proto.HostConfig{ + Uri: turn.URI, + Protocol: toResponseProto(turn.Proto), + }, + User: turn.Username, + Password: string(turn.Password), + }) + } + + wtConfig := &proto.WiretrusteeConfig{ + Stuns: stuns, + Turns: turns, + Signal: &proto.HostConfig{ + Uri: config.Signal.URI, + Protocol: toResponseProto(config.Signal.Proto), + }, + } + + pConfig := &proto.PeerConfig{ + Address: peer.IP.String(), + } + + remotePeers := make([]*proto.RemotePeerConfig, 0, len(peers)) + for _, rPeer := range peers { + remotePeers = append(remotePeers, &proto.RemotePeerConfig{ + WgPubKey: rPeer.Key, + AllowedIps: []string{fmt.Sprintf(AllowedIPsFormat, rPeer.IP)}, //todo /32 + }) + } + + return &proto.SyncResponse{ + WiretrusteeConfig: wtConfig, + PeerConfig: pConfig, + RemotePeers: remotePeers, + } +} + // IsHealthy indicates whether the service is healthy func (s *Server) IsHealthy(ctx context.Context, req *proto.Empty) (*proto.Empty, error) { return &proto.Empty{}, nil @@ -195,16 +267,14 @@ func (s *Server) closeUpdatesChannel(peerKey string) { } // sendInitialSync sends initial proto.SyncResponse to the peer requesting synchronization -func (s *Server) sendInitialSync(peerKey wgtypes.Key, srv proto.ManagementService_SyncServer) error { +func (s *Server) sendInitialSync(peerKey wgtypes.Key, peer *Peer, srv proto.ManagementService_SyncServer) error { - peers, err := s.Store.GetPeersForAPeer(peerKey.String()) + peers, err := s.accountManager.GetPeersForAPeer(peer.Key) if err != nil { - log.Warnf("error getting a list of peers for a peer %s", peerKey.String()) + log.Warnf("error getting a list of peers for a peer %s", peer.Key) return err } - plainResp := &proto.SyncResponse{ - Peers: peers, - } + plainResp := toSyncResponse(s.config, peer, peers) encryptedResp, err := encryption.EncryptMessage(peerKey, s.wgKey, plainResp) if err != nil { diff --git a/management/server/store.go b/management/server/store.go new file mode 100644 index 000000000..ca5305d61 --- /dev/null +++ b/management/server/store.go @@ -0,0 +1,9 @@ +package server + +type Store interface { + GetPeer(peerId string) (*Peer, error) + GetAccount(accountId string) (*Account, error) + GetPeerAccount(peerId string) (*Account, error) + GetAccountBySetupKey(setupKey string) (*Account, error) + SaveAccount(account *Account) error +} diff --git a/management/server/testdata/management.json b/management/server/testdata/management.json new file mode 100644 index 000000000..8cb4acf10 --- /dev/null +++ b/management/server/testdata/management.json @@ -0,0 +1,25 @@ +{ + "Stuns": [ + { + "Proto": "udp", + "URI": "stun:stun.wiretrustee.com:3468", + "Username": "", + "Password": null + } + ], + "Turns": [ + { + "Proto": "udp", + "URI": "turn:stun.wiretrustee.com:3468", + "Username": "some_user", + "Password": "c29tZV9wYXNzd29yZA==" + } + ], + "Signal": { + "Proto": "http", + "URI": "signal.wiretrustee.com:10000", + "Username": "", + "Password": null + }, + "DataDir": "" +} \ No newline at end of file diff --git a/management/server/testdata/store.json b/management/server/testdata/store.json index 478098f4b..2cf6c6ca0 100644 --- a/management/server/testdata/store.json +++ b/management/server/testdata/store.json @@ -7,7 +7,14 @@ "Key": "a2c8e62b-38f5-4553-b31e-dd66c696cebb" } }, - "Peers": {} + "Network": { + "Id": "af1c8024-ha40-4ce2-9418-34653101fc3c", + "Net": { + "IP": "100.64.0.0", + "Mask": "/8AAAA==" + }, + "Dns": null + } } } } \ No newline at end of file diff --git a/management/store/store.go b/management/store/store.go deleted file mode 100644 index c19ee84e7..000000000 --- a/management/store/store.go +++ /dev/null @@ -1,29 +0,0 @@ -package store - -// Account represents a unique account of the system -type Account struct { - Id string - SetupKeys map[string]*SetupKey - Peers map[string]*Peer -} - -// SetupKey represents a pre-authorized key used to register machines (peers) -// One key might have multiple machines -type SetupKey struct { - Key string -} - -// Peer represents a machine connected to the network. -// The Peer is a Wireguard peer identified by a public key -type Peer struct { - // Wireguard public key - Key string - // A setup key this peer was registered with - SetupKey *SetupKey -} - -type Store interface { - PeerExists(peerKey string) bool - AddPeer(setupKey string, peerKey string) error - GetPeersForAPeer(peerKey string) ([]string, error) -}