Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend management to sync meta and posture checks with peer #1727

Merged
merged 16 commits into from
Apr 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion client/internal/connect.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,10 @@ func runClient(
return wrapErr(err)
}

engine := NewEngineWithProbes(engineCtx, cancel, signalClient, mgmClient, engineConfig, mobileDependency, statusRecorder, mgmProbe, signalProbe, relayProbe, wgProbe)
checks := loginResp.GetChecks()

engine := NewEngineWithProbes(engineCtx, cancel, signalClient, mgmClient, engineConfig,
mobileDependency, statusRecorder, mgmProbe, signalProbe, relayProbe, wgProbe, checks)
err = engine.Start()
if err != nil {
log.Errorf("error while starting Netbird Connection Engine: %s", err)
Expand Down
51 changes: 49 additions & 2 deletions client/internal/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"net/netip"
"reflect"
"runtime"
"slices"
"strings"
"sync"
"time"
Expand All @@ -27,6 +28,7 @@ import (
"github.com/netbirdio/netbird/client/internal/routemanager"
"github.com/netbirdio/netbird/client/internal/wgproxy"
nbssh "github.com/netbirdio/netbird/client/ssh"
"github.com/netbirdio/netbird/client/system"
nbdns "github.com/netbirdio/netbird/dns"
"github.com/netbirdio/netbird/iface"
"github.com/netbirdio/netbird/iface/bind"
Expand Down Expand Up @@ -138,6 +140,9 @@ type Engine struct {
signalProbe *Probe
relayProbe *Probe
wgProbe *Probe

// checks are the client-applied posture checks that need to be evaluated on the client
checks []*mgmProto.Checks
}

// Peer is an instance of the Connection Peer
Expand All @@ -155,6 +160,7 @@ func NewEngine(
config *EngineConfig,
mobileDep MobileDependency,
statusRecorder *peer.Status,
checks []*mgmProto.Checks,
) *Engine {
return NewEngineWithProbes(
ctx,
Expand All @@ -168,6 +174,7 @@ func NewEngine(
nil,
nil,
nil,
checks,
)
}

Expand All @@ -184,6 +191,7 @@ func NewEngineWithProbes(
signalProbe *Probe,
relayProbe *Probe,
wgProbe *Probe,
checks []*mgmProto.Checks,
) *Engine {
return &Engine{
ctx: ctx,
Expand All @@ -204,6 +212,7 @@ func NewEngineWithProbes(
signalProbe: signalProbe,
relayProbe: relayProbe,
wgProbe: wgProbe,
checks: checks,
}
}

Expand Down Expand Up @@ -486,14 +495,38 @@ func (e *Engine) handleSync(update *mgmProto.SyncResponse) error {
// todo update signal
}

if err := e.updateChecksIfNew(update.Checks); err != nil {
return err
}

if update.GetNetworkMap() != nil {
// only apply new changes and ignore old ones
err := e.updateNetworkMap(update.GetNetworkMap())
if err != nil {
return err
}
}
return nil
}

// updateChecksIfNew updates checks if there are changes and sync new meta with management
func (e *Engine) updateChecksIfNew(checks []*mgmProto.Checks) error {
// if checks are equal, we skip the update
if isChecksEqual(e.checks, checks) {
return nil
}
e.checks = checks

info, err := system.GetInfoWithChecks(e.ctx, checks)
if err != nil {
log.Warnf("failed to get system info with checks: %v", err)
info = system.GetInfo(e.ctx)
}

if err := e.mgmClient.SyncMeta(info); err != nil {
log.Errorf("could not sync meta: error %s", err)
return err
}
return nil
}

Expand Down Expand Up @@ -583,7 +616,13 @@ 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() {
err := e.mgmClient.Sync(e.handleSync)
info, err := system.GetInfoWithChecks(e.ctx, e.checks)
if err != nil {
log.Warnf("failed to get system info with checks: %v", err)
info = system.GetInfo(e.ctx)
}

err = e.mgmClient.Sync(info, e.handleSync)
if err != nil {
// happens if management is unavailable for a long time.
// We want to cancel the operation of the whole client
Expand Down Expand Up @@ -1150,7 +1189,8 @@ func (e *Engine) close() {
}

func (e *Engine) readInitialSettings() ([]*route.Route, *nbdns.Config, error) {
netMap, err := e.mgmClient.GetNetworkMap()
info := system.GetInfo(e.ctx)
netMap, err := e.mgmClient.GetNetworkMap(info)
if err != nil {
return nil, nil, err
}
Expand Down Expand Up @@ -1328,3 +1368,10 @@ func (e *Engine) probeSTUNs() []relay.ProbeResult {
func (e *Engine) probeTURNs() []relay.ProbeResult {
return relay.ProbeAll(e.ctx, relay.ProbeTURN, e.TURNs)
}

// isChecksEqual checks if two slices of checks are equal.
func isChecksEqual(checks []*mgmProto.Checks, oChecks []*mgmProto.Checks) bool {
return slices.EqualFunc(checks, oChecks, func(checks, oChecks *mgmProto.Checks) bool {
return slices.Equal(checks.Files, oChecks.Files)
})
}
14 changes: 7 additions & 7 deletions client/internal/engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func TestEngine_SSH(t *testing.T) {
WgPrivateKey: key,
WgPort: 33100,
ServerSSHAllowed: true,
}, MobileDependency{}, peer.NewRecorder("https://mgm"))
}, MobileDependency{}, peer.NewRecorder("https://mgm"), nil)

engine.dnsServer = &dns.MockServer{
UpdateDNSServerFunc: func(serial uint64, update nbdns.Config) error { return nil },
Expand Down Expand Up @@ -210,7 +210,7 @@ func TestEngine_UpdateNetworkMap(t *testing.T) {
WgAddr: "100.64.0.1/24",
WgPrivateKey: key,
WgPort: 33100,
}, MobileDependency{}, peer.NewRecorder("https://mgm"))
}, MobileDependency{}, peer.NewRecorder("https://mgm"), nil)
newNet, err := stdnet.NewNet()
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -391,7 +391,7 @@ func TestEngine_Sync(t *testing.T) {
// feed updates to Engine via mocked Management client
updates := make(chan *mgmtProto.SyncResponse)
defer close(updates)
syncFunc := func(msgHandler func(msg *mgmtProto.SyncResponse) error) error {
syncFunc := func(info *system.Info, msgHandler func(msg *mgmtProto.SyncResponse) error) error {
for msg := range updates {
err := msgHandler(msg)
if err != nil {
Expand All @@ -406,7 +406,7 @@ func TestEngine_Sync(t *testing.T) {
WgAddr: "100.64.0.1/24",
WgPrivateKey: key,
WgPort: 33100,
}, MobileDependency{}, peer.NewRecorder("https://mgm"))
}, MobileDependency{}, peer.NewRecorder("https://mgm"), nil)

engine.dnsServer = &dns.MockServer{
UpdateDNSServerFunc: func(serial uint64, update nbdns.Config) error { return nil },
Expand Down Expand Up @@ -564,7 +564,7 @@ func TestEngine_UpdateNetworkMapWithRoutes(t *testing.T) {
WgAddr: wgAddr,
WgPrivateKey: key,
WgPort: 33100,
}, MobileDependency{}, peer.NewRecorder("https://mgm"))
}, MobileDependency{}, peer.NewRecorder("https://mgm"), nil)
newNet, err := stdnet.NewNet()
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -733,7 +733,7 @@ func TestEngine_UpdateNetworkMapWithDNSUpdate(t *testing.T) {
WgAddr: wgAddr,
WgPrivateKey: key,
WgPort: 33100,
}, MobileDependency{}, peer.NewRecorder("https://mgm"))
}, MobileDependency{}, peer.NewRecorder("https://mgm"), nil)
newNet, err := stdnet.NewNet()
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -1002,7 +1002,7 @@ func createEngine(ctx context.Context, cancel context.CancelFunc, setupKey strin
WgPort: wgPort,
}

return NewEngine(ctx, cancel, signalClient, mgmtClient, conf, MobileDependency{}, peer.NewRecorder("https://mgm")), nil
return NewEngine(ctx, cancel, signalClient, mgmtClient, conf, MobileDependency{}, peer.NewRecorder("https://mgm"), nil), nil
}

func startSignal() (*grpc.Server, string, error) {
Expand Down
26 changes: 26 additions & 0 deletions client/system/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"google.golang.org/grpc/metadata"

"github.com/netbirdio/netbird/management/proto"
"github.com/netbirdio/netbird/version"
)

Expand All @@ -30,6 +31,12 @@ type Environment struct {
Platform string
}

type File struct {
Path string
Exist bool
ProcessIsRunning bool
}

// Info is an object that contains machine information
// Most of the code is taken from https://github.com/matishsiao/goInfo
type Info struct {
Expand All @@ -48,6 +55,7 @@ type Info struct {
SystemProductName string
SystemManufacturer string
Environment Environment
Files []File
}

// extractUserAgent extracts Netbird's agent (client) name and version from the outgoing context
Expand Down Expand Up @@ -129,3 +137,21 @@ func isDuplicated(addresses []NetworkAddress, addr NetworkAddress) bool {
}
return false
}

// GetInfoWithChecks retrieves and parses the system information with applied checks.
func GetInfoWithChecks(ctx context.Context, checks []*proto.Checks) (*Info, error) {
processCheckPaths := make([]string, 0)
for _, check := range checks {
processCheckPaths = append(processCheckPaths, check.GetFiles()...)
}

files, err := checkFileAndProcess(processCheckPaths)
if err != nil {
return nil, err
}

info := GetInfo(ctx)
info.Files = files

return info, nil
}
5 changes: 5 additions & 0 deletions client/system/info_android.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ func GetInfo(ctx context.Context) *Info {
return gio
}

// checkFileAndProcess checks if the file path exists and if a process is running at that path.
func checkFileAndProcess(paths []string) ([]File, error) {
return []File{}, nil
}

func uname() []string {
res := run("/system/bin/uname", "-a")
return strings.Split(res, " ")
Expand Down
5 changes: 5 additions & 0 deletions client/system/info_ios.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ func GetInfo(ctx context.Context) *Info {
return gio
}

// checkFileAndProcess checks if the file path exists and if a process is running at that path.
func checkFileAndProcess(paths []string) ([]File, error) {
return []File{}, nil
}

// extractOsVersion extracts operating system version from context or returns the default
func extractOsVersion(ctx context.Context, defaultName string) string {
v, ok := ctx.Value(OsVersionCtxKey).(string)
Expand Down
58 changes: 58 additions & 0 deletions client/system/process.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//go:build windows || (linux && !android) || (darwin && !ios)

package system

import (
"os"
"slices"

"github.com/shirou/gopsutil/v3/process"
)

// getRunningProcesses returns a list of running process paths.
func getRunningProcesses() ([]string, error) {
processes, err := process.Processes()
if err != nil {
return nil, err
}

processMap := make(map[string]bool)
for _, p := range processes {
path, _ := p.Exe()
if path != "" {
processMap[path] = true
}
}

uniqueProcesses := make([]string, 0, len(processMap))
for p := range processMap {
uniqueProcesses = append(uniqueProcesses, p)
}

return uniqueProcesses, nil
}

// checkFileAndProcess checks if the file path exists and if a process is running at that path.
func checkFileAndProcess(paths []string) ([]File, error) {
files := make([]File, len(paths))
if len(paths) == 0 {
return files, nil
}

runningProcesses, err := getRunningProcesses()
if err != nil {
return nil, err
}

for i, path := range paths {
file := File{Path: path}

_, err := os.Stat(path)
file.Exist = !os.IsNotExist(err)

file.ProcessIsRunning = slices.Contains(runningProcesses, path)
files[i] = file
}

return files, nil
}
14 changes: 10 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ require (
github.com/spf13/pflag v1.0.5
github.com/vishvananda/netlink v1.1.1-0.20211118161826-650dca95af54
golang.org/x/crypto v0.18.0
golang.org/x/sys v0.16.0
golang.org/x/sys v0.18.0
golang.zx2c4.com/wireguard v0.0.0-20230704135630-469159ecf7d1
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
golang.zx2c4.com/wireguard/windows v0.5.3
Expand All @@ -44,7 +44,7 @@ require (
github.com/gliderlabs/ssh v0.3.4
github.com/godbus/dbus/v5 v5.1.0
github.com/golang/mock v1.6.0
github.com/google/go-cmp v0.5.9
github.com/google/go-cmp v0.6.0
github.com/google/gopacket v1.1.19
github.com/google/martian/v3 v3.0.0
github.com/google/nftables v0.0.0-20220808154552-2eca00135732
Expand All @@ -70,10 +70,11 @@ require (
github.com/pion/turn/v3 v3.0.1
github.com/prometheus/client_golang v1.14.0
github.com/rs/xid v1.3.0
github.com/shirou/gopsutil/v3 v3.24.3
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
github.com/stretchr/testify v1.8.4
github.com/stretchr/testify v1.9.0
github.com/things-go/go-socks5 v0.0.4
github.com/yusufpapurcu/wmi v1.2.3
github.com/yusufpapurcu/wmi v1.2.4
github.com/zcalusic/sysinfo v1.0.2
go.opentelemetry.io/otel v1.11.1
go.opentelemetry.io/otel/exporters/prometheus v0.33.0
Expand Down Expand Up @@ -131,6 +132,7 @@ require (
github.com/jinzhu/now v1.1.5 // indirect
github.com/josharian/native v1.1.0 // indirect
github.com/kelseyhightower/envconfig v1.4.0 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mdlayher/genetlink v1.3.2 // indirect
github.com/mdlayher/netlink v1.7.2 // indirect
Expand All @@ -142,12 +144,16 @@ require (
github.com/pion/randutil v0.1.0 // indirect
github.com/pion/transport/v2 v2.2.4 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564 // indirect
github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9 // indirect
github.com/tklauser/go-sysconf v0.3.13 // indirect
github.com/tklauser/numcpus v0.7.0 // indirect
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
github.com/yuin/goldmark v1.4.13 // indirect
go.opencensus.io v0.24.0 // indirect
Expand Down
Loading
Loading