Add client version to the client app and send it to the management service (#222)

* test: WIP mocking the grpc server for testing the sending of the client information

* WIP: Test_SystemMetaDataFromClient with mocks, todo:

* fix: failing meta data test

* test: add system meta expectation in management client test

* fix: removing deprecated register function, replacing with new one

* fix: removing deprecated register function from mockclient interface impl

* fix: fixing interface declaration

* chore: remove unused commented code

Co-authored-by: braginini <bangvalo@gmail.com>
This commit is contained in:
shatoboar 2022-02-08 18:03:27 +01:00 committed by GitHub
parent 3c959bb178
commit b59fd50226
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 222 additions and 41 deletions

View File

@ -4,19 +4,21 @@ import (
"bufio" "bufio"
"context" "context"
"fmt" "fmt"
"os"
"time"
"github.com/cenkalti/backoff/v4" "github.com/cenkalti/backoff/v4"
"github.com/google/uuid" "github.com/google/uuid"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/wiretrustee/wiretrustee/client/internal" "github.com/wiretrustee/wiretrustee/client/internal"
"github.com/wiretrustee/wiretrustee/client/system"
mgm "github.com/wiretrustee/wiretrustee/management/client" mgm "github.com/wiretrustee/wiretrustee/management/client"
mgmProto "github.com/wiretrustee/wiretrustee/management/proto" mgmProto "github.com/wiretrustee/wiretrustee/management/proto"
"github.com/wiretrustee/wiretrustee/util" "github.com/wiretrustee/wiretrustee/util"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes" "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
"os"
"time"
) )
var ( var (
@ -143,7 +145,8 @@ func registerPeer(serverPublicKey wgtypes.Key, client *mgm.GrpcClient, setupKey
} }
log.Debugf("sending peer registration request to Management Service") 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 { if err != nil {
log.Errorf("failed registering peer %v", err) log.Errorf("failed registering peer %v", err)
return nil, err return nil, err

View File

@ -2,6 +2,8 @@ package cmd
import ( import (
"context" "context"
"time"
"github.com/cenkalti/backoff/v4" "github.com/cenkalti/backoff/v4"
"github.com/kardianos/service" "github.com/kardianos/service"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
@ -13,7 +15,6 @@ import (
"golang.zx2c4.com/wireguard/wgctrl/wgtypes" "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
"time"
) )
var ( var (
@ -186,7 +187,6 @@ func runClient() error {
return err 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) engine := internal.NewEngine(signalClient, mgmClient, engineConfig, cancel, ctx)
err = engine.Start() err = engine.Start()
if err != nil { if err != nil {

View File

@ -3,6 +3,11 @@ package internal
import ( import (
"context" "context"
"fmt" "fmt"
"math/rand"
"strings"
"sync"
"time"
"github.com/pion/ice/v2" "github.com/pion/ice/v2"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/wiretrustee/wiretrustee/client/internal/peer" "github.com/wiretrustee/wiretrustee/client/internal/peer"
@ -14,10 +19,6 @@ import (
sProto "github.com/wiretrustee/wiretrustee/signal/proto" sProto "github.com/wiretrustee/wiretrustee/signal/proto"
"github.com/wiretrustee/wiretrustee/util" "github.com/wiretrustee/wiretrustee/util"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes" "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"math/rand"
"strings"
"sync"
"time"
) )
// PeerConnectionTimeoutMax is a timeout of an initial connection attempt to a remote peer. // PeerConnectionTimeoutMax is a timeout of an initial connection attempt to a remote peer.

View File

@ -3,7 +3,16 @@ package internal
import ( import (
"context" "context"
"fmt" "fmt"
"net"
"os"
"path/filepath"
"runtime"
"sync"
"testing"
"time"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/wiretrustee/wiretrustee/client/system"
mgmt "github.com/wiretrustee/wiretrustee/management/client" mgmt "github.com/wiretrustee/wiretrustee/management/client"
mgmtProto "github.com/wiretrustee/wiretrustee/management/proto" mgmtProto "github.com/wiretrustee/wiretrustee/management/proto"
"github.com/wiretrustee/wiretrustee/management/server" "github.com/wiretrustee/wiretrustee/management/server"
@ -14,13 +23,6 @@ import (
"golang.zx2c4.com/wireguard/wgctrl/wgtypes" "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/keepalive" "google.golang.org/grpc/keepalive"
"net"
"os"
"path/filepath"
"runtime"
"sync"
"testing"
"time"
) )
var ( var (
@ -382,7 +384,8 @@ func createEngine(ctx context.Context, cancel context.CancelFunc, setupKey strin
return nil, err return nil, err
} }
resp, err := mgmtClient.Register(*publicKey, setupKey) info := system.GetInfo()
resp, err := mgmtClient.Register(*publicKey, setupKey, info)
if err != nil { if err != nil {
return nil, err return nil, err
} }

4
go.mod
View File

@ -30,6 +30,8 @@ require (
require ( require (
github.com/cakturk/go-netstat v0.0.0-20200220111822-e5b49efee7a5 github.com/cakturk/go-netstat v0.0.0-20200220111822-e5b49efee7a5
github.com/magiconair/properties v1.8.5 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/rs/xid v1.3.0
github.com/stretchr/testify v1.7.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/mdlayher/socket v0.0.0-20211102153432-57e3fa563ecb // indirect
github.com/nxadm/tail v1.4.8 // indirect github.com/nxadm/tail v1.4.8 // indirect
github.com/pion/dtls/v2 v2.0.12 // 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/mdns v0.0.5 // indirect
github.com/pion/randutil v0.1.0 // indirect github.com/pion/randutil v0.1.0 // indirect
github.com/pion/stun v0.3.5 // indirect github.com/pion/stun v0.3.5 // indirect
github.com/pion/transport v0.12.3 // 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/pion/udp v0.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect

View File

@ -1,15 +1,17 @@
package client package client
import ( import (
"io"
"github.com/wiretrustee/wiretrustee/client/system"
"github.com/wiretrustee/wiretrustee/management/proto" "github.com/wiretrustee/wiretrustee/management/proto"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes" "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"io"
) )
type Client interface { type Client interface {
io.Closer io.Closer
Sync(msgHandler func(msg *proto.SyncResponse) error) error Sync(msgHandler func(msg *proto.SyncResponse) error) error
GetServerPublicKey() (*wgtypes.Key, 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) Login(serverKey wgtypes.Key) (*proto.LoginResponse, error)
} }

View File

@ -2,7 +2,18 @@ package client
import ( import (
"context" "context"
"net"
"path/filepath"
"sync"
"testing"
"time"
"github.com/wiretrustee/wiretrustee/client/system"
log "github.com/sirupsen/logrus" 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" mgmtProto "github.com/wiretrustee/wiretrustee/management/proto"
mgmt "github.com/wiretrustee/wiretrustee/management/server" mgmt "github.com/wiretrustee/wiretrustee/management/server"
"github.com/wiretrustee/wiretrustee/util" "github.com/wiretrustee/wiretrustee/util"
@ -10,14 +21,12 @@ import (
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
"net"
"path/filepath"
"testing"
"time"
) )
var tested *GrpcClient var tested *GrpcClient
var serverAddr string var serverAddr string
var mgmtMockServer *mgmt.ManagementServiceServerMock
var serverKey wgtypes.Key
const ValidKey = "A2C8E62B-38F5-4553-B31E-DD66C696CEBB" 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 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) { func TestClient_GetServerPublicKey(t *testing.T) {
key, err := tested.GetServerPublicKey() key, err := tested.GetServerPublicKey()
@ -109,7 +151,8 @@ func TestClient_LoginRegistered(t *testing.T) {
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
resp, err := tested.Register(*key, ValidKey) info := system.GetInfo()
resp, err := tested.Register(*key, ValidKey, info)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -125,7 +168,8 @@ func TestClient_Sync(t *testing.T) {
t.Error(err) t.Error(err)
} }
_, err = tested.Register(*serverKey, ValidKey) info := system.GetInfo()
_, err = tested.Register(*serverKey, ValidKey, info)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -139,7 +183,9 @@ func TestClient_Sync(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
_, err = remoteClient.Register(*serverKey, ValidKey)
info = system.GetInfo()
_, err = remoteClient.Register(*serverKey, ValidKey, info)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -177,3 +223,82 @@ func TestClient_Sync(t *testing.T) {
t.Error("timeout waiting for test to finish") 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)
}

View File

@ -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 // Register registers peer on Management Server. It actually calls a Login endpoint with a provided setup key
// Takes care of encrypting and decrypting messages. // 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) // 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) { func (c *GrpcClient) Register(serverKey wgtypes.Key, setupKey string, info *system.Info) (*proto.LoginResponse, error) {
gi := system.GetInfo()
meta := &proto.PeerSystemMeta{ meta := &proto.PeerSystemMeta{
Hostname: gi.Hostname, Hostname: info.Hostname,
GoOS: gi.GoOS, GoOS: info.GoOS,
OS: gi.OS, OS: info.OS,
Core: gi.OSVersion, Core: info.OSVersion,
Platform: gi.Platform, Platform: info.Platform,
Kernel: gi.Kernel, Kernel: info.Kernel,
WiretrusteeVersion: gi.WiretrusteeVersion, WiretrusteeVersion: info.WiretrusteeVersion,
} }
log.Debugf("detected system %v", meta)
return c.login(serverKey, &proto.LoginRequest{SetupKey: setupKey, Meta: meta}) return c.login(serverKey, &proto.LoginRequest{SetupKey: setupKey, Meta: meta})
} }

View File

@ -1,6 +1,7 @@
package client package client
import ( import (
"github.com/wiretrustee/wiretrustee/client/system"
"github.com/wiretrustee/wiretrustee/management/proto" "github.com/wiretrustee/wiretrustee/management/proto"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes" "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
) )
@ -9,7 +10,7 @@ type MockClient struct {
CloseFunc func() error CloseFunc func() error
SyncFunc func(msgHandler func(msg *proto.SyncResponse) error) error SyncFunc func(msgHandler func(msg *proto.SyncResponse) error) error
GetServerPublicKeyFunc func() (*wgtypes.Key, 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) LoginFunc func(serverKey wgtypes.Key) (*proto.LoginResponse, error)
} }
@ -34,11 +35,11 @@ func (m *MockClient) GetServerPublicKey() (*wgtypes.Key, error) {
return m.GetServerPublicKeyFunc() 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 { if m.RegisterFunc == nil {
return nil, 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) { func (m *MockClient) Login(serverKey wgtypes.Key) (*proto.LoginResponse, error) {

View File

@ -9,11 +9,13 @@ package management;
service ManagementService { service ManagementService {
// Login logs in peer. In case server returns codes.PermissionDenied this endpoint can be used to register Peer providing LoginRequest.setupKey // 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) {} rpc Login(EncryptedMessage) returns (EncryptedMessage) {}
// Sync enables peer synchronization. Each peer that is connected to this stream will receive updates from the server. // 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 // 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 // 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) {} rpc Sync(EncryptedMessage) returns (stream EncryptedMessage) {}
// Exposes a Wireguard public key of the Management service. // Exposes a Wireguard public key of the Management service.

View File

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