diff --git a/client/cmd/login.go b/client/cmd/login.go index 5e16848dc65..c763b05cac9 100644 --- a/client/cmd/login.go +++ b/client/cmd/login.go @@ -4,19 +4,21 @@ import ( "bufio" "context" "fmt" + "os" + "time" + "github.com/cenkalti/backoff/v4" "github.com/google/uuid" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/wiretrustee/wiretrustee/client/internal" + "github.com/wiretrustee/wiretrustee/client/system" mgm "github.com/wiretrustee/wiretrustee/management/client" mgmProto "github.com/wiretrustee/wiretrustee/management/proto" "github.com/wiretrustee/wiretrustee/util" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - "os" - "time" ) var ( @@ -143,7 +145,8 @@ func registerPeer(serverPublicKey wgtypes.Key, client *mgm.GrpcClient, setupKey } log.Debugf("sending peer registration request to Management Service") - loginResp, err := client.Register(serverPublicKey, validSetupKey.String()) + info := system.GetInfo() + loginResp, err := client.Register(serverPublicKey, validSetupKey.String(), info) if err != nil { log.Errorf("failed registering peer %v", err) return nil, err diff --git a/client/cmd/up.go b/client/cmd/up.go index 4b333da21a5..e03b7e4dc70 100644 --- a/client/cmd/up.go +++ b/client/cmd/up.go @@ -2,6 +2,8 @@ package cmd import ( "context" + "time" + "github.com/cenkalti/backoff/v4" "github.com/kardianos/service" log "github.com/sirupsen/logrus" @@ -13,7 +15,6 @@ import ( "golang.zx2c4.com/wireguard/wgctrl/wgtypes" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - "time" ) var ( @@ -186,7 +187,6 @@ func runClient() error { return err } - // create start the Wiretrustee Engine that will connect to the Signal and Management streams and manage connections to remote peers. engine := internal.NewEngine(signalClient, mgmClient, engineConfig, cancel, ctx) err = engine.Start() if err != nil { diff --git a/client/internal/engine.go b/client/internal/engine.go index 8b4e3dfec22..9b92df5ad79 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -3,6 +3,11 @@ package internal import ( "context" "fmt" + "math/rand" + "strings" + "sync" + "time" + "github.com/pion/ice/v2" log "github.com/sirupsen/logrus" "github.com/wiretrustee/wiretrustee/client/internal/peer" @@ -14,10 +19,6 @@ import ( sProto "github.com/wiretrustee/wiretrustee/signal/proto" "github.com/wiretrustee/wiretrustee/util" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" - "math/rand" - "strings" - "sync" - "time" ) // PeerConnectionTimeoutMax is a timeout of an initial connection attempt to a remote peer. diff --git a/client/internal/engine_test.go b/client/internal/engine_test.go index bda009a6019..8a9d154b121 100644 --- a/client/internal/engine_test.go +++ b/client/internal/engine_test.go @@ -3,7 +3,16 @@ package internal import ( "context" "fmt" + "net" + "os" + "path/filepath" + "runtime" + "sync" + "testing" + "time" + log "github.com/sirupsen/logrus" + "github.com/wiretrustee/wiretrustee/client/system" mgmt "github.com/wiretrustee/wiretrustee/management/client" mgmtProto "github.com/wiretrustee/wiretrustee/management/proto" "github.com/wiretrustee/wiretrustee/management/server" @@ -14,13 +23,6 @@ import ( "golang.zx2c4.com/wireguard/wgctrl/wgtypes" "google.golang.org/grpc" "google.golang.org/grpc/keepalive" - "net" - "os" - "path/filepath" - "runtime" - "sync" - "testing" - "time" ) var ( @@ -382,7 +384,8 @@ func createEngine(ctx context.Context, cancel context.CancelFunc, setupKey strin return nil, err } - resp, err := mgmtClient.Register(*publicKey, setupKey) + info := system.GetInfo() + resp, err := mgmtClient.Register(*publicKey, setupKey, info) if err != nil { return nil, err } diff --git a/go.mod b/go.mod index d7afe81439f..2b8e04d3e33 100644 --- a/go.mod +++ b/go.mod @@ -30,6 +30,8 @@ require ( require ( github.com/cakturk/go-netstat v0.0.0-20200220111822-e5b49efee7a5 github.com/magiconair/properties v1.8.5 + github.com/pion/logging v0.2.2 + github.com/pion/turn/v2 v2.0.5 github.com/rs/xid v1.3.0 github.com/stretchr/testify v1.7.0 ) @@ -46,12 +48,10 @@ require ( github.com/mdlayher/socket v0.0.0-20211102153432-57e3fa563ecb // indirect github.com/nxadm/tail v1.4.8 // indirect github.com/pion/dtls/v2 v2.0.12 // indirect - github.com/pion/logging v0.2.2 // indirect github.com/pion/mdns v0.0.5 // indirect github.com/pion/randutil v0.1.0 // indirect github.com/pion/stun v0.3.5 // indirect github.com/pion/transport v0.12.3 // indirect - github.com/pion/turn/v2 v2.0.5 // indirect github.com/pion/udp v0.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect diff --git a/management/client/client.go b/management/client/client.go index e881fb40d79..76dc3ad0350 100644 --- a/management/client/client.go +++ b/management/client/client.go @@ -1,15 +1,17 @@ package client import ( + "io" + + "github.com/wiretrustee/wiretrustee/client/system" "github.com/wiretrustee/wiretrustee/management/proto" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" - "io" ) type Client interface { io.Closer Sync(msgHandler func(msg *proto.SyncResponse) error) error GetServerPublicKey() (*wgtypes.Key, error) - Register(serverKey wgtypes.Key, setupKey string) (*proto.LoginResponse, error) + Register(serverKey wgtypes.Key, setupKey string, info *system.Info) (*proto.LoginResponse, error) Login(serverKey wgtypes.Key) (*proto.LoginResponse, error) } diff --git a/management/client/client_test.go b/management/client/client_test.go index c80e4fd1014..a93c253bced 100644 --- a/management/client/client_test.go +++ b/management/client/client_test.go @@ -2,7 +2,18 @@ package client import ( "context" + "net" + "path/filepath" + "sync" + "testing" + "time" + + "github.com/wiretrustee/wiretrustee/client/system" + log "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + "github.com/wiretrustee/wiretrustee/encryption" + "github.com/wiretrustee/wiretrustee/management/proto" mgmtProto "github.com/wiretrustee/wiretrustee/management/proto" mgmt "github.com/wiretrustee/wiretrustee/management/server" "github.com/wiretrustee/wiretrustee/util" @@ -10,14 +21,12 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - "net" - "path/filepath" - "testing" - "time" ) var tested *GrpcClient var serverAddr string +var mgmtMockServer *mgmt.ManagementServiceServerMock +var serverKey wgtypes.Key const ValidKey = "A2C8E62B-38F5-4553-B31E-DD66C696CEBB" @@ -78,6 +87,39 @@ func startManagement(config *mgmt.Config, t *testing.T) (*grpc.Server, net.Liste return s, lis } +func startMockManagement(t *testing.T) (*grpc.Server, net.Listener) { + lis, err := net.Listen("tcp", ":0") + if err != nil { + t.Fatal(err) + } + + s := grpc.NewServer() + + serverKey, err = wgtypes.GenerateKey() + if err != nil { + t.Fatal(err) + } + + mgmtMockServer = &mgmt.ManagementServiceServerMock{ + GetServerKeyFunc: func(context.Context, *proto.Empty) (*proto.ServerKeyResponse, error) { + response := &proto.ServerKeyResponse{ + Key: serverKey.PublicKey().String(), + } + return response, nil + }, + } + + mgmtProto.RegisterManagementServiceServer(s, mgmtMockServer) + go func() { + if err := s.Serve(lis); err != nil { + t.Error(err) + return + } + }() + + return s, lis +} + func TestClient_GetServerPublicKey(t *testing.T) { key, err := tested.GetServerPublicKey() @@ -109,7 +151,8 @@ func TestClient_LoginRegistered(t *testing.T) { if err != nil { t.Error(err) } - resp, err := tested.Register(*key, ValidKey) + info := system.GetInfo() + resp, err := tested.Register(*key, ValidKey, info) if err != nil { t.Error(err) } @@ -125,7 +168,8 @@ func TestClient_Sync(t *testing.T) { t.Error(err) } - _, err = tested.Register(*serverKey, ValidKey) + info := system.GetInfo() + _, err = tested.Register(*serverKey, ValidKey, info) if err != nil { t.Error(err) } @@ -139,7 +183,9 @@ func TestClient_Sync(t *testing.T) { if err != nil { t.Fatal(err) } - _, err = remoteClient.Register(*serverKey, ValidKey) + + info = system.GetInfo() + _, err = remoteClient.Register(*serverKey, ValidKey, info) if err != nil { t.Fatal(err) } @@ -177,3 +223,82 @@ func TestClient_Sync(t *testing.T) { t.Error("timeout waiting for test to finish") } } + +func Test_SystemMetaDataFromClient(t *testing.T) { + _, lis := startMockManagement(t) + + testKey, err := wgtypes.GenerateKey() + if err != nil { + log.Fatal(err) + } + + serverAddr := lis.Addr().String() + ctx := context.Background() + + testClient, err := NewClient(ctx, serverAddr, testKey, false) + if err != nil { + log.Fatalf("error while creating testClient: %v", err) + } + + key, err := testClient.GetServerPublicKey() + if err != nil { + log.Fatalf("error while getting server public key from testclient, %v", err) + } + + var actualMeta *proto.PeerSystemMeta + var actualValidKey string + var wg sync.WaitGroup + wg.Add(1) + + mgmtMockServer.LoginFunc = + func(ctx context.Context, msg *proto.EncryptedMessage) (*proto.EncryptedMessage, error) { + peerKey, err := wgtypes.ParseKey(msg.GetWgPubKey()) + if err != nil { + log.Warnf("error while parsing peer's Wireguard public key %s on Sync request.", msg.WgPubKey) + return nil, status.Errorf(codes.InvalidArgument, "provided wgPubKey %s is invalid", msg.WgPubKey) + } + + loginReq := &proto.LoginRequest{} + err = encryption.DecryptMessage(peerKey, serverKey, msg.Body, loginReq) + if err != nil { + log.Fatal(err) + } + + actualMeta = loginReq.GetMeta() + actualValidKey = loginReq.GetSetupKey() + wg.Done() + + loginResp := &proto.LoginResponse{} + encryptedResp, err := encryption.EncryptMessage(peerKey, serverKey, loginResp) + if err != nil { + return nil, err + } + + return &mgmtProto.EncryptedMessage{ + WgPubKey: serverKey.PublicKey().String(), + Body: encryptedResp, + Version: 0, + }, nil + } + + info := system.GetInfo() + _, err = testClient.Register(*key, ValidKey, info) + if err != nil { + t.Errorf("error while trying to register client: %v", err) + } + + wg.Wait() + + expectedMeta := &proto.PeerSystemMeta{ + Hostname: info.Hostname, + GoOS: info.GoOS, + Kernel: info.Kernel, + Core: info.OSVersion, + Platform: info.Platform, + OS: info.OS, + WiretrusteeVersion: info.WiretrusteeVersion, + } + + assert.Equal(t, ValidKey, actualValidKey) + assert.Equal(t, expectedMeta, actualMeta) +} diff --git a/management/client/grpc.go b/management/client/grpc.go index 1f915b65c53..7c28928c936 100644 --- a/management/client/grpc.go +++ b/management/client/grpc.go @@ -233,18 +233,16 @@ func (c *GrpcClient) login(serverKey wgtypes.Key, req *proto.LoginRequest) (*pro // Register registers peer on Management Server. It actually calls a Login endpoint with a provided setup key // Takes care of encrypting and decrypting messages. // This method will also collect system info and send it with the request (e.g. hostname, os, etc) -func (c *GrpcClient) Register(serverKey wgtypes.Key, setupKey string) (*proto.LoginResponse, error) { - gi := system.GetInfo() +func (c *GrpcClient) Register(serverKey wgtypes.Key, setupKey string, info *system.Info) (*proto.LoginResponse, error) { meta := &proto.PeerSystemMeta{ - Hostname: gi.Hostname, - GoOS: gi.GoOS, - OS: gi.OS, - Core: gi.OSVersion, - Platform: gi.Platform, - Kernel: gi.Kernel, - WiretrusteeVersion: gi.WiretrusteeVersion, + Hostname: info.Hostname, + GoOS: info.GoOS, + OS: info.OS, + Core: info.OSVersion, + Platform: info.Platform, + Kernel: info.Kernel, + WiretrusteeVersion: info.WiretrusteeVersion, } - log.Debugf("detected system %v", meta) return c.login(serverKey, &proto.LoginRequest{SetupKey: setupKey, Meta: meta}) } diff --git a/management/client/mock.go b/management/client/mock.go index af99ba8632f..ac4c64c07e2 100644 --- a/management/client/mock.go +++ b/management/client/mock.go @@ -1,6 +1,7 @@ package client import ( + "github.com/wiretrustee/wiretrustee/client/system" "github.com/wiretrustee/wiretrustee/management/proto" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" ) @@ -9,7 +10,7 @@ type MockClient struct { CloseFunc func() error SyncFunc func(msgHandler func(msg *proto.SyncResponse) error) error GetServerPublicKeyFunc func() (*wgtypes.Key, error) - RegisterFunc func(serverKey wgtypes.Key, setupKey string) (*proto.LoginResponse, error) + RegisterFunc func(serverKey wgtypes.Key, setupKey string, info *system.Info) (*proto.LoginResponse, error) LoginFunc func(serverKey wgtypes.Key) (*proto.LoginResponse, error) } @@ -34,11 +35,11 @@ func (m *MockClient) GetServerPublicKey() (*wgtypes.Key, error) { return m.GetServerPublicKeyFunc() } -func (m *MockClient) Register(serverKey wgtypes.Key, setupKey string) (*proto.LoginResponse, error) { +func (m *MockClient) Register(serverKey wgtypes.Key, setupKey string, info *system.Info) (*proto.LoginResponse, error) { if m.RegisterFunc == nil { return nil, nil } - return m.RegisterFunc(serverKey, setupKey) + return m.RegisterFunc(serverKey, setupKey, info) } func (m *MockClient) Login(serverKey wgtypes.Key) (*proto.LoginResponse, error) { diff --git a/management/proto/management.proto b/management/proto/management.proto index bf3454199b6..67dee0bcb5a 100644 --- a/management/proto/management.proto +++ b/management/proto/management.proto @@ -9,11 +9,13 @@ package management; service ManagementService { // Login logs in peer. In case server returns codes.PermissionDenied this endpoint can be used to register Peer providing LoginRequest.setupKey + // Returns encrypted LoginResponse in EncryptedMessage.Body rpc Login(EncryptedMessage) returns (EncryptedMessage) {} // Sync enables peer synchronization. Each peer that is connected to this stream will receive updates from the server. // For example, if a new peer has been added to an account all other connected peers will receive this peer's Wireguard public key as an update // The initial SyncResponse contains all of the available peers so the local state can be refreshed + // Returns encrypted SyncResponse in EncryptedMessage.Body rpc Sync(EncryptedMessage) returns (stream EncryptedMessage) {} // Exposes a Wireguard public key of the Management service. diff --git a/management/server/management_server_mock.go b/management/server/management_server_mock.go new file mode 100644 index 00000000000..8ec07789406 --- /dev/null +++ b/management/server/management_server_mock.go @@ -0,0 +1,46 @@ +package server + +import ( + "context" + + "github.com/wiretrustee/wiretrustee/management/proto" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +type ManagementServiceServerMock struct { + proto.UnimplementedManagementServiceServer + + LoginFunc func(context.Context, *proto.EncryptedMessage) (*proto.EncryptedMessage, error) + SyncFunc func(*proto.EncryptedMessage, proto.ManagementService_SyncServer) + GetServerKeyFunc func(context.Context, *proto.Empty) (*proto.ServerKeyResponse, error) + IsHealthyFunc func(context.Context, *proto.Empty) (*proto.Empty, error) +} + +func (m ManagementServiceServerMock) Login(ctx context.Context, req *proto.EncryptedMessage) (*proto.EncryptedMessage, error) { + if m.LoginFunc != nil { + return m.LoginFunc(ctx, req) + } + return nil, status.Errorf(codes.Unimplemented, "method Login not implemented") +} + +func (m ManagementServiceServerMock) Sync(msg *proto.EncryptedMessage, sync proto.ManagementService_SyncServer) error { + if m.SyncFunc != nil { + return m.Sync(msg, sync) + } + return status.Errorf(codes.Unimplemented, "method Sync not implemented") +} + +func (m ManagementServiceServerMock) GetServerKey(ctx context.Context, empty *proto.Empty) (*proto.ServerKeyResponse, error) { + if m.GetServerKeyFunc != nil { + return m.GetServerKeyFunc(ctx, empty) + } + return nil, status.Errorf(codes.Unimplemented, "method GetServerKey not implemented") +} + +func (m ManagementServiceServerMock) IsHealthy(ctx context.Context, empty *proto.Empty) (*proto.Empty, error) { + if m.IsHealthyFunc != nil { + return m.IsHealthyFunc(ctx, empty) + } + return nil, status.Errorf(codes.Unimplemented, "method IsHealthy not implemented") +}