Add static system info

This commit is contained in:
Maycon Santos 2024-11-28 23:46:29 +01:00
parent c6641be94b
commit 07f0f9fdbd
13 changed files with 108 additions and 65 deletions

View File

@ -106,7 +106,7 @@ func (c *Client) Run(urlOpener URLOpener, dns *DNSList, dnsReadyListener DnsRead
// todo do not throw error in case of cancelled context
ctx = internal.CtxInitState(ctx)
connectClient := internal.NewConnectClient(ctx, cfg, c.recorder)
connectClient := internal.NewConnectClient(ctx, cfg, c.recorder, &system.StaticInfo{})
return connectClient.RunOnAndroid(c.tunAdapter, c.iFaceDiscover, c.networkChangeListener, dns.items, dnsReadyListener)
}
@ -132,7 +132,7 @@ func (c *Client) RunWithoutLogin(dns *DNSList, dnsReadyListener DnsReadyListener
// todo do not throw error in case of cancelled context
ctx = internal.CtxInitState(ctx)
connectClient := internal.NewConnectClient(ctx, cfg, c.recorder)
connectClient := internal.NewConnectClient(ctx, cfg, c.recorder, &system.StaticInfo{})
return connectClient.RunOnAndroid(c.tunAdapter, c.iFaceDiscover, c.networkChangeListener, dns.items, dnsReadyListener)
}

View File

@ -137,9 +137,11 @@ var loginCmd = &cobra.Command{
func foregroundLogin(ctx context.Context, cmd *cobra.Command, config *internal.Config, setupKey string) error {
needsLogin := false
staticInfoChan := system.GetStaticInfoInBackground(ctx)
staticInfo := <-staticInfoChan
err := WithBackOff(func() error {
err := internal.Login(ctx, config, "", "")
err := internal.Login(ctx, config, "", "", staticInfo)
if s, ok := gstatus.FromError(err); ok && (s.Code() == codes.InvalidArgument || s.Code() == codes.PermissionDenied) {
needsLogin = true
return nil
@ -162,7 +164,7 @@ func foregroundLogin(ctx context.Context, cmd *cobra.Command, config *internal.C
var lastError error
err = WithBackOff(func() error {
err := internal.Login(ctx, config, setupKey, jwtToken)
err := internal.Login(ctx, config, setupKey, jwtToken, staticInfo)
if s, ok := gstatus.FromError(err); ok && (s.Code() == codes.InvalidArgument || s.Code() == codes.PermissionDenied) {
lastError = err
return nil

View File

@ -152,6 +152,8 @@ func runInForegroundMode(ctx context.Context, cmd *cobra.Command) error {
return err
}
staticInfoChan := system.GetStaticInfoInBackground(ctx)
config, err := internal.UpdateOrCreateConfig(ic)
if err != nil {
return fmt.Errorf("get config file: %v", err)
@ -171,7 +173,7 @@ func runInForegroundMode(ctx context.Context, cmd *cobra.Command) error {
r := peer.NewRecorder(config.ManagementURL.String())
r.GetFullStatus()
connectClient := internal.NewConnectClient(ctx, config, r)
connectClient := internal.NewConnectClient(ctx, config, r, <-staticInfoChan)
return connectClient.Run()
}

View File

@ -40,19 +40,21 @@ type ConnectClient struct {
statusRecorder *peer.Status
engine *Engine
engineMutex sync.Mutex
staticInfo *system.StaticInfo
}
func NewConnectClient(
ctx context.Context,
config *Config,
statusRecorder *peer.Status,
staticInfo *system.StaticInfo,
) *ConnectClient {
return &ConnectClient{
ctx: ctx,
config: config,
statusRecorder: statusRecorder,
engineMutex: sync.Mutex{},
staticInfo: staticInfo,
}
}
@ -179,7 +181,7 @@ func (c *ConnectClient) run(mobileDependency MobileDependency, probes *ProbeHold
}()
// connect (just a connection, no stream yet) and login to Management Service to get an initial global Wiretrustee config
loginResp, err := loginToManagement(engineCtx, mgmClient, publicSSHKey)
loginResp, err := loginToManagement(engineCtx, mgmClient, publicSSHKey, c.staticInfo)
if err != nil {
log.Debug(err)
if s, ok := gstatus.FromError(err); ok && (s.Code() == codes.PermissionDenied) {
@ -257,7 +259,7 @@ func (c *ConnectClient) run(mobileDependency MobileDependency, probes *ProbeHold
checks := loginResp.GetChecks()
c.engineMutex.Lock()
c.engine = NewEngineWithProbes(engineCtx, cancel, signalClient, mgmClient, relayManager, engineConfig, mobileDependency, c.statusRecorder, probes, checks)
c.engine = NewEngineWithProbes(engineCtx, cancel, signalClient, mgmClient, relayManager, engineConfig, mobileDependency, c.statusRecorder, probes, checks, c.staticInfo)
c.engineMutex.Unlock()
@ -424,14 +426,14 @@ func connectToSignal(ctx context.Context, wtConfig *mgmProto.WiretrusteeConfig,
}
// loginToManagement creates Management Services client, establishes a connection, logs-in and gets a global Wiretrustee config (signal, turn, stun hosts, etc)
func loginToManagement(ctx context.Context, client mgm.Client, pubSSHKey []byte) (*mgmProto.LoginResponse, error) {
func loginToManagement(ctx context.Context, client mgm.Client, pubSSHKey []byte, staticInfo *system.StaticInfo) (*mgmProto.LoginResponse, error) {
serverPublicKey, err := client.GetServerPublicKey()
if err != nil {
return nil, gstatus.Errorf(codes.FailedPrecondition, "failed while getting Management Service public key: %s", err)
}
sysInfo := system.GetInfo(ctx)
sysInfo := system.GetInfo(ctx, staticInfo)
loginResp, err := client.Login(*serverPublicKey, sysInfo, pubSSHKey)
if err != nil {
return nil, err

View File

@ -172,6 +172,8 @@ type Engine struct {
relayManager *relayClient.Manager
stateManager *statemanager.Manager
srWatcher *guard.SRWatcher
staticInfo *system.StaticInfo
}
// Peer is an instance of the Connection Peer
@ -180,8 +182,8 @@ type Peer struct {
WgAllowedIps string
}
// NewEngine creates a new Connection Engine
func NewEngine(
// newEngine creates a new Connection Engine
func newEngine(
clientCtx context.Context,
clientCancel context.CancelFunc,
signalClient signal.Client,
@ -203,6 +205,7 @@ func NewEngine(
statusRecorder,
nil,
checks,
nil,
)
}
@ -218,6 +221,7 @@ func NewEngineWithProbes(
statusRecorder *peer.Status,
probes *ProbeHolder,
checks []*mgmProto.Checks,
staticInfo *system.StaticInfo,
) *Engine {
engine := &Engine{
clientCtx: clientCtx,
@ -237,6 +241,7 @@ func NewEngineWithProbes(
statusRecorder: statusRecorder,
probes: probes,
checks: checks,
staticInfo: staticInfo,
}
if path := statemanager.GetDefaultStatePath(); path != "" {
engine.stateManager = statemanager.New(path)
@ -582,10 +587,10 @@ func (e *Engine) updateChecksIfNew(checks []*mgmProto.Checks) error {
}
e.checks = checks
info, err := system.GetInfoWithChecks(e.ctx, checks)
info, err := system.GetInfoWithChecks(e.ctx, checks, e.staticInfo)
if err != nil {
log.Warnf("failed to get system info with checks: %v", err)
info = system.GetInfo(e.ctx)
info = system.GetInfo(e.ctx, e.staticInfo)
}
if err := e.mgmClient.SyncMeta(info); err != nil {
@ -685,10 +690,10 @@ func (e *Engine) updateConfig(conf *mgmProto.PeerConfig) error {
// E.g. when a new peer has been registered and we are allowed to connect to it.
func (e *Engine) receiveManagementEvents() {
go func() {
info, err := system.GetInfoWithChecks(e.ctx, e.checks)
info, err := system.GetInfoWithChecks(e.ctx, e.checks, e.staticInfo)
if err != nil {
log.Warnf("failed to get system info with checks: %v", err)
info = system.GetInfo(e.ctx)
info = system.GetInfo(e.ctx, e.staticInfo)
}
// err = e.mgmClient.Sync(info, e.handleSync)
@ -1192,7 +1197,7 @@ func (e *Engine) close() {
}
func (e *Engine) readInitialSettings() ([]*route.Route, *nbdns.Config, error) {
info := system.GetInfo(e.ctx)
info := system.GetInfo(e.ctx, e.staticInfo)
netMap, err := e.mgmClient.GetNetworkMap(info)
if err != nil {
return nil, nil, err

View File

@ -84,7 +84,7 @@ func TestEngine_SSH(t *testing.T) {
defer cancel()
relayMgr := relayClient.NewManager(ctx, nil, key.PublicKey().String())
engine := NewEngine(
engine := newEngine(
ctx, cancel,
&signal.MockClient{},
&mgmt.MockClient{},
@ -229,7 +229,7 @@ func TestEngine_UpdateNetworkMap(t *testing.T) {
defer cancel()
relayMgr := relayClient.NewManager(ctx, nil, key.PublicKey().String())
engine := NewEngine(
engine := newEngine(
ctx, cancel,
&signal.MockClient{},
&mgmt.MockClient{},
@ -434,7 +434,7 @@ func TestEngine_Sync(t *testing.T) {
return nil
}
relayMgr := relayClient.NewManager(ctx, nil, key.PublicKey().String())
engine := NewEngine(ctx, cancel, &signal.MockClient{}, &mgmt.MockClient{SyncFunc: syncFunc}, relayMgr, &EngineConfig{
engine := newEngine(ctx, cancel, &signal.MockClient{}, &mgmt.MockClient{SyncFunc: syncFunc}, relayMgr, &EngineConfig{
WgIfaceName: "utun103",
WgAddr: "100.64.0.1/24",
WgPrivateKey: key,
@ -594,7 +594,7 @@ func TestEngine_UpdateNetworkMapWithRoutes(t *testing.T) {
wgAddr := fmt.Sprintf("100.66.%d.1/24", n)
relayMgr := relayClient.NewManager(ctx, nil, key.PublicKey().String())
engine := NewEngine(ctx, cancel, &signal.MockClient{}, &mgmt.MockClient{}, relayMgr, &EngineConfig{
engine := newEngine(ctx, cancel, &signal.MockClient{}, &mgmt.MockClient{}, relayMgr, &EngineConfig{
WgIfaceName: wgIfaceName,
WgAddr: wgAddr,
WgPrivateKey: key,
@ -774,7 +774,7 @@ func TestEngine_UpdateNetworkMapWithDNSUpdate(t *testing.T) {
wgAddr := fmt.Sprintf("100.66.%d.1/24", n)
relayMgr := relayClient.NewManager(ctx, nil, key.PublicKey().String())
engine := NewEngine(ctx, cancel, &signal.MockClient{}, &mgmt.MockClient{}, relayMgr, &EngineConfig{
engine := newEngine(ctx, cancel, &signal.MockClient{}, &mgmt.MockClient{}, relayMgr, &EngineConfig{
WgIfaceName: wgIfaceName,
WgAddr: wgAddr,
WgPrivateKey: key,
@ -1118,7 +1118,7 @@ func createEngine(ctx context.Context, cancel context.CancelFunc, setupKey strin
return nil, err
}
info := system.GetInfo(ctx)
info := system.GetInfo(ctx, nil)
resp, err := mgmtClient.Register(*publicKey, setupKey, "", info, nil)
if err != nil {
return nil, err
@ -1140,7 +1140,7 @@ func createEngine(ctx context.Context, cancel context.CancelFunc, setupKey strin
}
relayMgr := relayClient.NewManager(ctx, nil, key.PublicKey().String())
e, err := NewEngine(ctx, cancel, signalClient, mgmtClient, relayMgr, conf, MobileDependency{}, peer.NewRecorder("https://mgm"), nil), nil
e, err := newEngine(ctx, cancel, signalClient, mgmtClient, relayMgr, conf, MobileDependency{}, peer.NewRecorder("https://mgm"), nil), nil
e.ctx = ctx
return e, err
}

View File

@ -17,7 +17,7 @@ import (
)
// IsLoginRequired check that the server is support SSO or not
func IsLoginRequired(ctx context.Context, privateKey string, mgmURL *url.URL, sshKey string) (bool, error) {
func IsLoginRequired(ctx context.Context, privateKey string, mgmURL *url.URL, sshKey string, staticInfo *system.StaticInfo) (bool, error) {
mgmClient, err := getMgmClient(ctx, privateKey, mgmURL)
if err != nil {
return false, err
@ -38,7 +38,7 @@ func IsLoginRequired(ctx context.Context, privateKey string, mgmURL *url.URL, ss
return false, err
}
_, err = doMgmLogin(ctx, mgmClient, pubSSHKey)
_, err = doMgmLogin(ctx, mgmClient, pubSSHKey, staticInfo)
if isLoginNeeded(err) {
return true, nil
}
@ -46,7 +46,7 @@ func IsLoginRequired(ctx context.Context, privateKey string, mgmURL *url.URL, ss
}
// Login or register the client
func Login(ctx context.Context, config *Config, setupKey string, jwtToken string) error {
func Login(ctx context.Context, config *Config, setupKey string, jwtToken string, staticInfo *system.StaticInfo) error {
mgmClient, err := getMgmClient(ctx, config.PrivateKey, config.ManagementURL)
if err != nil {
return err
@ -67,10 +67,10 @@ func Login(ctx context.Context, config *Config, setupKey string, jwtToken string
return err
}
serverKey, err := doMgmLogin(ctx, mgmClient, pubSSHKey)
serverKey, err := doMgmLogin(ctx, mgmClient, pubSSHKey, staticInfo)
if serverKey != nil && isRegistrationNeeded(err) {
log.Debugf("peer registration required")
_, err = registerPeer(ctx, *serverKey, mgmClient, setupKey, jwtToken, pubSSHKey)
_, err = registerPeer(ctx, *serverKey, mgmClient, setupKey, jwtToken, pubSSHKey, staticInfo)
return err
}
@ -99,28 +99,28 @@ func getMgmClient(ctx context.Context, privateKey string, mgmURL *url.URL) (*mgm
return mgmClient, err
}
func doMgmLogin(ctx context.Context, mgmClient *mgm.GrpcClient, pubSSHKey []byte) (*wgtypes.Key, error) {
func doMgmLogin(ctx context.Context, mgmClient *mgm.GrpcClient, pubSSHKey []byte, staticInfo *system.StaticInfo) (*wgtypes.Key, error) {
serverKey, err := mgmClient.GetServerPublicKey()
if err != nil {
log.Errorf("failed while getting Management Service public key: %v", err)
return nil, err
}
sysInfo := system.GetInfo(ctx)
sysInfo := system.GetInfo(ctx, staticInfo)
_, err = mgmClient.Login(*serverKey, sysInfo, pubSSHKey)
return serverKey, err
}
// registerPeer checks whether setupKey was provided via cmd line and if not then it prompts user to enter a key.
// Otherwise tries to register with the provided setupKey via command line.
func registerPeer(ctx context.Context, serverPublicKey wgtypes.Key, client *mgm.GrpcClient, setupKey string, jwtToken string, pubSSHKey []byte) (*mgmProto.LoginResponse, error) {
func registerPeer(ctx context.Context, serverPublicKey wgtypes.Key, client *mgm.GrpcClient, setupKey string, jwtToken string, pubSSHKey []byte, staticInfo *system.StaticInfo) (*mgmProto.LoginResponse, error) {
validSetupKey, err := uuid.Parse(setupKey)
if err != nil && jwtToken == "" {
return nil, status.Errorf(codes.InvalidArgument, "invalid setup-key or no sso information provided, err: %v", err)
}
log.Debugf("sending peer registration request to Management Service")
info := system.GetInfo(ctx)
info := system.GetInfo(ctx, staticInfo)
loginResp, err := client.Register(serverPublicKey, validSetupKey.String(), jwtToken, info, pubSSHKey)
if err != nil {
log.Errorf("failed registering peer %v,%s", err, validSetupKey.String())

View File

@ -123,7 +123,7 @@ func (c *Client) Run(fd int32, interfaceName string) error {
c.onHostDnsFn = func([]string) {}
cfg.WgIface = interfaceName
c.connectClient = internal.NewConnectClient(ctx, cfg, c.recorder)
c.connectClient = internal.NewConnectClient(ctx, cfg, c.recorder, &system.StaticInfo{})
return c.connectClient.RunOniOS(fd, c.networkChangeListener, c.dnsManager)
}

View File

@ -68,6 +68,7 @@ type Server struct {
relayProbe *internal.Probe
wgProbe *internal.Probe
lastProbe time.Time
staticInfo *system.StaticInfo
}
type oauthAuthFlow struct {
@ -79,6 +80,8 @@ type oauthAuthFlow struct {
// New server instance constructor.
func New(ctx context.Context, configPath, logFile string) *Server {
staticInfoChan := system.GetStaticInfoInBackground(ctx)
staticInfo := <-staticInfoChan
return &Server{
rootCtx: ctx,
latestConfigInput: internal.ConfigInput{
@ -89,6 +92,7 @@ func New(ctx context.Context, configPath, logFile string) *Server {
signalProbe: internal.NewProbe(),
relayProbe: internal.NewProbe(),
wgProbe: internal.NewProbe(),
staticInfo: staticInfo,
}
}
@ -195,7 +199,7 @@ func (s *Server) connectWithRetryRuns(ctx context.Context, config *internal.Conf
runOperation := func() error {
log.Tracef("running client connection")
s.connectClient = internal.NewConnectClient(ctx, config, statusRecorder)
s.connectClient = internal.NewConnectClient(ctx, config, statusRecorder, s.staticInfo)
probes := internal.ProbeHolder{
MgmProbe: s.mgmProbe,
@ -272,7 +276,7 @@ func parseEnvDuration(envVar string, defaultDuration time.Duration) time.Duratio
// loginAttempt attempts to login using the provided information. it returns a status in case something fails
func (s *Server) loginAttempt(ctx context.Context, setupKey, jwtToken string) (internal.StatusType, error) {
var status internal.StatusType
err := internal.Login(ctx, s.config, setupKey, jwtToken)
err := internal.Login(ctx, s.config, setupKey, jwtToken, s.staticInfo)
if err != nil {
if s, ok := gstatus.FromError(err); ok && (s.Code() == codes.InvalidArgument || s.Code() == codes.PermissionDenied) {
log.Warnf("failed login: %v", err)

View File

@ -61,6 +61,14 @@ type Info struct {
Files []File // for posture checks
}
// StaticInfo is an object that contains machine information that does not change
type StaticInfo struct {
SystemSerialNumber string
SystemProductName string
SystemManufacturer string
Environment Environment
}
// extractUserAgent extracts Netbird's agent (client) name and version from the outgoing context
func extractUserAgent(ctx context.Context) string {
md, hasMeta := metadata.FromOutgoingContext(ctx)
@ -142,7 +150,7 @@ func isDuplicated(addresses []NetworkAddress, addr NetworkAddress) bool {
}
// GetInfoWithChecks retrieves and parses the system information with applied checks.
func GetInfoWithChecks(ctx context.Context, checks []*proto.Checks) (*Info, error) {
func GetInfoWithChecks(ctx context.Context, checks []*proto.Checks, staticInfo *StaticInfo) (*Info, error) {
processCheckPaths := make([]string, 0)
for _, check := range checks {
processCheckPaths = append(processCheckPaths, check.GetFiles()...)
@ -153,8 +161,17 @@ func GetInfoWithChecks(ctx context.Context, checks []*proto.Checks) (*Info, erro
return nil, err
}
info := GetInfo(ctx)
info := GetInfo(ctx, staticInfo)
info.Files = files
return info, nil
}
// GetStaticInfoInBackground retrieves and parses the system information in the background
func GetStaticInfoInBackground(ctx context.Context) <-chan *StaticInfo {
ch := make(chan *StaticInfo)
go func() {
ch <- getStaticInfo(ctx)
}()
return ch
}

View File

@ -21,7 +21,7 @@ import (
)
// GetInfo retrieves and parses the system information
func GetInfo(ctx context.Context) *Info {
func GetInfo(ctx context.Context, staticInfo *StaticInfo) *Info {
utsname := unix.Utsname{}
err := unix.Uname(&utsname)
if err != nil {
@ -41,26 +41,22 @@ func GetInfo(ctx context.Context) *Info {
log.Warnf("failed to discover network addresses: %s", err)
}
serialNum, prodName, manufacturer := sysInfo()
env := Environment{
Cloud: detect_cloud.Detect(ctx),
Platform: detect_platform.Detect(ctx),
gio := &Info{
Kernel: sysName,
OSVersion: strings.TrimSpace(string(swVersion)),
Platform: machine,
OS: sysName,
GoOS: runtime.GOOS,
CPUs: runtime.NumCPU(),
KernelVersion: release,
NetworkAddresses: addrs,
}
gio := &Info{
Kernel: sysName,
OSVersion: strings.TrimSpace(string(swVersion)),
Platform: machine,
OS: sysName,
GoOS: runtime.GOOS,
CPUs: runtime.NumCPU(),
KernelVersion: release,
NetworkAddresses: addrs,
SystemSerialNumber: serialNum,
SystemProductName: prodName,
SystemManufacturer: manufacturer,
Environment: env,
if staticInfo != nil {
gio.SystemSerialNumber = staticInfo.SystemSerialNumber
gio.SystemProductName = staticInfo.SystemProductName
gio.SystemManufacturer = staticInfo.SystemManufacturer
gio.Environment = staticInfo.Environment
}
systemHostname, _ := os.Hostname()
@ -71,6 +67,21 @@ func GetInfo(ctx context.Context) *Info {
return gio
}
func getStaticInfo(ctx context.Context) *StaticInfo {
serialNum, prodName, manufacturer := sysInfo()
env := Environment{
Cloud: detect_cloud.Detect(ctx),
Platform: detect_platform.Detect(ctx),
}
return &StaticInfo{
SystemSerialNumber: serialNum,
SystemProductName: prodName,
SystemManufacturer: manufacturer,
Environment: env,
}
}
func sysInfo() (serialNumber string, productName string, manufacturer string) {
out, _ := exec.Command("/usr/sbin/ioreg", "-l").Output() // err ignored for brevity
for _, l := range strings.Split(string(out), "\n") {

View File

@ -9,7 +9,7 @@ import (
)
func Test_LocalWTVersion(t *testing.T) {
got := GetInfo(context.TODO())
got := GetInfo(context.TODO(), nil)
want := "development"
assert.Equal(t, want, got.WiretrusteeVersion)
}
@ -21,7 +21,7 @@ func Test_UIVersion(t *testing.T) {
"user-agent": {want},
})
got := GetInfo(ctx)
got := GetInfo(ctx, nil)
assert.Equal(t, want, got.UIVersion)
}
@ -30,7 +30,7 @@ func Test_CustomHostname(t *testing.T) {
ctx := context.WithValue(context.Background(), DeviceNameCtxKey, "custom-host")
want := "custom-host"
got := GetInfo(ctx)
got := GetInfo(ctx, nil)
assert.Equal(t, want, got.Hostname)
}

View File

@ -174,7 +174,7 @@ func TestClient_LoginUnregistered_ShouldThrow_401(t *testing.T) {
if err != nil {
t.Fatal(err)
}
sysInfo := system.GetInfo(context.TODO())
sysInfo := system.GetInfo(context.TODO(), nil)
_, err = client.Login(*key, sysInfo, nil)
if err == nil {
t.Error("expecting err on unregistered login, got nil")
@ -202,7 +202,7 @@ func TestClient_LoginRegistered(t *testing.T) {
if err != nil {
t.Error(err)
}
info := system.GetInfo(context.TODO())
info := system.GetInfo(context.TODO(), nil)
resp, err := client.Register(*key, ValidKey, "", info, nil)
if err != nil {
t.Error(err)
@ -232,7 +232,7 @@ func TestClient_Sync(t *testing.T) {
t.Error(err)
}
info := system.GetInfo(context.TODO())
info := system.GetInfo(context.TODO(), nil)
_, err = client.Register(*serverKey, ValidKey, "", info, nil)
if err != nil {
t.Error(err)
@ -248,7 +248,7 @@ func TestClient_Sync(t *testing.T) {
t.Fatal(err)
}
info = system.GetInfo(context.TODO())
info = system.GetInfo(context.TODO(), nil)
_, err = remoteClient.Register(*serverKey, ValidKey, "", info, nil)
if err != nil {
t.Fatal(err)
@ -346,7 +346,7 @@ func Test_SystemMetaDataFromClient(t *testing.T) {
}, nil
}
info := system.GetInfo(context.TODO())
info := system.GetInfo(context.TODO(), nil)
_, err = testClient.Register(*key, ValidKey, "", info, nil)
if err != nil {
t.Errorf("error while trying to register client: %v", err)