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

Add process posture check #1693

Merged
merged 30 commits into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
5f0eec0
wip: add process check posture
bcmmbaga Mar 12, 2024
9f41a1f
add process posture check to posture checks handlers
bcmmbaga Mar 12, 2024
e66e39c
Extend peer metadata with processes
bcmmbaga Mar 12, 2024
41348bb
Add process validation for peer metadata
bcmmbaga Mar 12, 2024
60f9f08
fix tests
bcmmbaga Mar 13, 2024
cc60df7
Allow set of single unix or windows path check
bcmmbaga Mar 14, 2024
9db450d
Add single Unix/Windows path check in process tests
bcmmbaga Mar 14, 2024
1a5d59b
Refactor
bcmmbaga Mar 14, 2024
4ab993c
Fix tests
bcmmbaga Mar 14, 2024
90ab2f7
Fix linters
bcmmbaga Mar 14, 2024
180f5a1
Refactor posture check validations (#1705)
bcmmbaga Mar 14, 2024
9dcaa51
Merge branch 'main' into add-process-posture-check
bcmmbaga Mar 18, 2024
2727680
Merge branch 'main' into add-process-posture-check
bcmmbaga Mar 21, 2024
36582d1
Merge branch 'refs/heads/main' into add-process-posture-check
bcmmbaga Apr 10, 2024
c6ab215
Extend management to sync meta and posture checks with peer (#1727)
bcmmbaga Apr 15, 2024
8aa32a2
Merge branch 'refs/heads/main' into add-process-posture-check
bcmmbaga Apr 15, 2024
6bfd1b2
fix merge conflicts
bcmmbaga Apr 15, 2024
7745ed7
Merge branch 'refs/heads/main' into add-process-posture-check
bcmmbaga Apr 17, 2024
0d93677
Merge branch 'refs/heads/main' into add-process-posture-check
bcmmbaga Apr 24, 2024
e4f5466
go mod tidy
bcmmbaga Apr 24, 2024
2d3fd1f
split unix path into a linux and mac path (#1893)
bcmmbaga Apr 30, 2024
17aef4f
Merge branch 'refs/heads/main' into add-process-posture-check
bcmmbaga May 13, 2024
0c0925e
Fix peer sync metadata
bcmmbaga May 13, 2024
0939660
Merge branch 'refs/heads/main' into add-process-posture-check
bcmmbaga May 22, 2024
645b9fe
go mod tidy
bcmmbaga May 22, 2024
59b1c0f
merge 0.28.0
pascal-fischer May 31, 2024
1ec5e66
remove comment from merge
pascal-fischer May 31, 2024
e7a0fd8
Fix SonarCloud issues (#2096)
bcmmbaga Jun 6, 2024
aff6ae1
Merge branch 'refs/heads/0.28.0' into add-process-posture-check
bcmmbaga Jun 6, 2024
142a3d0
Regenerate mgmnt protocol messages
bcmmbaga Jun 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
27 changes: 27 additions & 0 deletions management/server/http/api/openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -864,6 +864,8 @@ components:
$ref: '#/components/schemas/GeoLocationCheck'
peer_network_range_check:
$ref: '#/components/schemas/PeerNetworkRangeCheck'
process_check:
$ref: '#/components/schemas/ProcessCheck'
NBVersionCheck:
description: Posture check for the version of NetBird
type: object
Expand Down Expand Up @@ -952,6 +954,31 @@ components:
required:
- ranges
- action
ProcessCheck:
description: Posture Check for binaries exist and are running in the peer’s system
type: object
properties:
processes:
type: array
items:
$ref: '#/components/schemas/Process'
required:
- processes
Process:
description: Describe the operational activity within peer's system.
lixmal marked this conversation as resolved.
Show resolved Hide resolved
type: object
properties:
path:
description: Path to the process executable file in a Unix-like operating system
type: string
example: "/usr/local/bin/netbird"
windows_path:
description: Path to the process executable file in a Windows operating system
type: string
example: "C:\ProgramData\NetBird\netbird.exe"
required:
- path
- windows_path
Location:
description: Describe geographical location information
type: object
Expand Down
17 changes: 17 additions & 0 deletions management/server/http/api/types.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

50 changes: 49 additions & 1 deletion management/server/http/posture_checks_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,10 @@ func (p *PostureChecksHandler) savePostureChecks(
}
}

if processCheck := req.Checks.ProcessCheck; processCheck != nil {
postureChecks.Checks.ProcessCheck = toProcessCheck(processCheck)
}

if err := p.accountManager.SavePostureChecks(account.Id, user.Id, &postureChecks); err != nil {
util.WriteError(err, w)
return
Expand All @@ -235,7 +239,7 @@ func validatePostureChecksUpdate(req api.PostureCheckUpdate) error {
}

if req.Checks == nil || (req.Checks.NbVersionCheck == nil && req.Checks.OsVersionCheck == nil &&
req.Checks.GeoLocationCheck == nil && req.Checks.PeerNetworkRangeCheck == nil) {
req.Checks.GeoLocationCheck == nil && req.Checks.PeerNetworkRangeCheck == nil && req.Checks.ProcessCheck == nil) {
return status.Errorf(status.InvalidArgument, "posture checks shouldn't be empty")
}

Expand Down Expand Up @@ -292,6 +296,21 @@ func validatePostureChecksUpdate(req api.PostureCheckUpdate) error {
}
}

if processCheck := req.Checks.ProcessCheck; processCheck != nil {
if len(processCheck.Processes) == 0 {
return status.Errorf(status.InvalidArgument, "processes for process check shouldn't be empty")
}

for _, process := range processCheck.Processes {
if process.WindowsPath == "" {
return status.Errorf(status.InvalidArgument, "windows path for process check shouldn't be empty")
bcmmbaga marked this conversation as resolved.
Show resolved Hide resolved
}
if process.Path == "" {
return status.Errorf(status.InvalidArgument, "path for process check shouldn't be empty")
}
}
}

return nil
}

Expand Down Expand Up @@ -322,6 +341,10 @@ func toPostureChecksResponse(postureChecks *posture.Checks) *api.PostureCheck {
checks.PeerNetworkRangeCheck = toPeerNetworkRangeCheckResponse(postureChecks.Checks.PeerNetworkRangeCheck)
}

if postureChecks.Checks.ProcessCheck != nil {
checks.ProcessCheck = toProcessCheckResponse(postureChecks.Checks.ProcessCheck)
}

return &api.PostureCheck{
Id: postureChecks.ID,
Name: postureChecks.Name,
Expand Down Expand Up @@ -396,3 +419,28 @@ func toPeerNetworkRangeCheck(check *api.PeerNetworkRangeCheck) (*posture.PeerNet
Action: string(check.Action),
}, nil
}

func toProcessCheckResponse(check *posture.ProcessCheck) *api.ProcessCheck {
processes := make([]api.Process, 0, len(check.Processes))
for _, process := range check.Processes {
processes = append(processes, (api.Process)(process))
}

return &api.ProcessCheck{
Processes: processes,
}
}

func toProcessCheck(check *api.ProcessCheck) *posture.ProcessCheck {
processes := make([]posture.Process, 0, len(check.Processes))
for _, process := range check.Processes {
processes = append(processes, posture.Process{
Path: process.Path,
WindowsPath: process.WindowsPath,
})
}

return &posture.ProcessCheck{
Processes: processes,
}
}
78 changes: 78 additions & 0 deletions management/server/http/posture_checks_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,43 @@ func TestPostureCheckUpdate(t *testing.T) {
handler.geolocationManager = nil
},
},
{
name: "Create Posture Checks Process Check",
requestType: http.MethodPost,
requestPath: "/api/posture-checks",
requestBody: bytes.NewBuffer(
[]byte(`{
"name": "default",
"description": "default",
"checks": {
"process_check": {
"processes": [
{
"path": "/usr/local/bin/netbird",
"windows_path": "C:\\ProgramData\\NetBird\\netbird.exe"
}
]
}
}
}`)),
expectedStatus: http.StatusOK,
expectedBody: true,
expectedPostureCheck: &api.PostureCheck{
Id: "postureCheck",
Name: "default",
Description: str("default"),
Checks: api.Checks{
ProcessCheck: &api.ProcessCheck{
Processes: []api.Process{
{
Path: "/usr/local/bin/netbird",
WindowsPath: "C:\\ProgramData\\NetBird\\netbird.exe",
},
},
},
},
},
},
{
name: "Create Posture Checks Invalid Check",
requestType: http.MethodPost,
Expand Down Expand Up @@ -937,4 +974,45 @@ func TestPostureCheck_validatePostureChecksUpdate(t *testing.T) {
},
)
assert.Error(t, err)

// valid process check
processCheck := api.ProcessCheck{
Processes: []api.Process{
{
Path: "/usr/local/bin/netbird",
WindowsPath: "C:\\ProgramData\\NetBird\\netbird.exe",
},
},
}
err = validatePostureChecksUpdate(api.PostureCheckUpdate{Name: "Default", Checks: &api.Checks{ProcessCheck: &processCheck}})
assert.NoError(t, err)

// invalid process check
processCheck = api.ProcessCheck{
Processes: make([]api.Process, 0),
}
err = validatePostureChecksUpdate(api.PostureCheckUpdate{Name: "Default", Checks: &api.Checks{ProcessCheck: &processCheck}})
assert.Error(t, err)

// invalid process check
processCheck = api.ProcessCheck{
Processes: []api.Process{
{
Path: "/usr/local/bin/netbird",
},
},
}
err = validatePostureChecksUpdate(api.PostureCheckUpdate{Name: "Default", Checks: &api.Checks{ProcessCheck: &processCheck}})
assert.Error(t, err)

// invalid process check
processCheck = api.ProcessCheck{
Processes: []api.Process{
{
WindowsPath: "C:\\ProgramData\\NetBird\\netbird.exe",
},
},
}
err = validatePostureChecksUpdate(api.PostureCheckUpdate{Name: "Default", Checks: &api.Checks{ProcessCheck: &processCheck}})
assert.Error(t, err)
}
6 changes: 6 additions & 0 deletions management/server/peer/peer.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ type Environment struct {
Platform string
}

// Process represents an active process on the peer's system.
type Process struct {
Path string
}

// PeerSystemMeta is a metadata of a Peer machine system
type PeerSystemMeta struct { //nolint:revive
Hostname string
Expand All @@ -96,6 +101,7 @@ type PeerSystemMeta struct { //nolint:revive
SystemProductName string
SystemManufacturer string
Environment Environment `gorm:"serializer:json"`
Processes []Process `gorm:"-"`
}

func (p PeerSystemMeta) isEqual(other PeerSystemMeta) bool {
Expand Down
12 changes: 12 additions & 0 deletions management/server/posture/checks.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const (
OSVersionCheckName = "OSVersionCheck"
GeoLocationCheckName = "GeoLocationCheck"
PeerNetworkRangeCheckName = "PeerNetworkRangeCheck"
ProcessCheckName = "ProcessCheck"

CheckActionAllow string = "allow"
CheckActionDeny string = "deny"
Expand Down Expand Up @@ -48,6 +49,7 @@ type ChecksDefinition struct {
OSVersionCheck *OSVersionCheck `json:",omitempty"`
GeoLocationCheck *GeoLocationCheck `json:",omitempty"`
PeerNetworkRangeCheck *PeerNetworkRangeCheck `json:",omitempty"`
ProcessCheck *ProcessCheck `json:",omitempty"`
}

// Copy returns a copy of a checks definition.
Expand Down Expand Up @@ -93,6 +95,13 @@ func (cd ChecksDefinition) Copy() ChecksDefinition {
}
copy(cdCopy.PeerNetworkRangeCheck.Ranges, peerNetRangeCheck.Ranges)
}
if cd.ProcessCheck != nil {
processCheck := cd.ProcessCheck
cdCopy.ProcessCheck = &ProcessCheck{
Processes: make([]Process, len(processCheck.Processes)),
}
copy(cdCopy.ProcessCheck.Processes, processCheck.Processes)
}
return cdCopy
}

Expand Down Expand Up @@ -133,6 +142,9 @@ func (pc *Checks) GetChecks() []Check {
if pc.Checks.PeerNetworkRangeCheck != nil {
checks = append(checks, pc.Checks.PeerNetworkRangeCheck)
}
if pc.Checks.ProcessCheck != nil {
checks = append(checks, pc.Checks.ProcessCheck)
}
return checks
}

Expand Down
8 changes: 8 additions & 0 deletions management/server/posture/checks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,14 @@ func TestChecks_Copy(t *testing.T) {
},
Action: CheckActionDeny,
},
ProcessCheck: &ProcessCheck{
Processes: []Process{
{
Path: "/Applications/NetBird.app/Contents/MacOS/netbird",
WindowsPath: "C:\\ProgramData\\NetBird\\netbird.exe",
},
},
},
},
}
checkCopy := check.Copy()
Expand Down
49 changes: 49 additions & 0 deletions management/server/posture/process.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package posture

import (
"fmt"
"slices"

nbpeer "github.com/netbirdio/netbird/management/server/peer"
)

type Process struct {
Path string
WindowsPath string
}

type ProcessCheck struct {
Processes []Process
}

var _ Check = (*ProcessCheck)(nil)

func (p *ProcessCheck) Check(peer nbpeer.Peer) (bool, error) {
peerActiveProcesses := make([]string, 0, len(peer.Meta.Processes))
for _, process := range peer.Meta.Processes {
peerActiveProcesses = append(peerActiveProcesses, process.Path)
}

switch peer.Meta.GoOS {
case "darwin", "linux":
for _, process := range p.Processes {
if !slices.Contains(peerActiveProcesses, process.Path) {
return false, nil
}
}
return true, nil
case "windows":
for _, process := range p.Processes {
if !slices.Contains(peerActiveProcesses, process.WindowsPath) {
return false, nil
}
}
return true, nil
default:
return false, fmt.Errorf("unsupported peer's operating system: %s", peer.Meta.GoOS)
}
}

func (p *ProcessCheck) Name() string {
return ProcessCheckName
}