mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-01 23:23:54 -04:00
Compare commits
1 Commits
prototype/
...
feature/di
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
279e96e6b1 |
22
client/system/disk_encryption.go
Normal file
22
client/system/disk_encryption.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package system
|
||||
|
||||
// DiskEncryptionVolume represents encryption status of a single volume.
|
||||
type DiskEncryptionVolume struct {
|
||||
Path string
|
||||
Encrypted bool
|
||||
}
|
||||
|
||||
// DiskEncryptionInfo holds disk encryption detection results.
|
||||
type DiskEncryptionInfo struct {
|
||||
Volumes []DiskEncryptionVolume
|
||||
}
|
||||
|
||||
// IsEncrypted returns true if the volume at the given path is encrypted.
|
||||
func (d DiskEncryptionInfo) IsEncrypted(path string) bool {
|
||||
for _, v := range d.Volumes {
|
||||
if v.Path == path {
|
||||
return v.Encrypted
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
35
client/system/disk_encryption_darwin.go
Normal file
35
client/system/disk_encryption_darwin.go
Normal file
@@ -0,0 +1,35 @@
|
||||
//go:build darwin && !ios
|
||||
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// detectDiskEncryption detects FileVault encryption status on macOS.
|
||||
func detectDiskEncryption(ctx context.Context) DiskEncryptionInfo {
|
||||
info := DiskEncryptionInfo{}
|
||||
|
||||
cmdCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
cmd := exec.CommandContext(cmdCtx, "fdesetup", "status")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
log.Debugf("execute fdesetup: %v", err)
|
||||
return info
|
||||
}
|
||||
|
||||
encrypted := strings.Contains(string(output), "FileVault is On")
|
||||
info.Volumes = append(info.Volumes, DiskEncryptionVolume{
|
||||
Path: "/",
|
||||
Encrypted: encrypted,
|
||||
})
|
||||
|
||||
return info
|
||||
}
|
||||
98
client/system/disk_encryption_linux.go
Normal file
98
client/system/disk_encryption_linux.go
Normal file
@@ -0,0 +1,98 @@
|
||||
//go:build linux && !android
|
||||
|
||||
package system
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// detectDiskEncryption detects LUKS encryption status on Linux by reading sysfs.
|
||||
func detectDiskEncryption(ctx context.Context) DiskEncryptionInfo {
|
||||
info := DiskEncryptionInfo{}
|
||||
|
||||
encryptedDevices := findEncryptedDevices()
|
||||
mountPoints := parseMounts(encryptedDevices)
|
||||
|
||||
info.Volumes = mountPoints
|
||||
return info
|
||||
}
|
||||
|
||||
// findEncryptedDevices scans /sys/block for dm-crypt (LUKS) encrypted devices.
|
||||
func findEncryptedDevices() map[string]bool {
|
||||
encryptedDevices := make(map[string]bool)
|
||||
|
||||
sysBlock := "/sys/block"
|
||||
entries, err := os.ReadDir(sysBlock)
|
||||
if err != nil {
|
||||
log.Debugf("read /sys/block: %v", err)
|
||||
return encryptedDevices
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
dmUuidPath := filepath.Join(sysBlock, entry.Name(), "dm", "uuid")
|
||||
data, err := os.ReadFile(dmUuidPath)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
uuid := strings.TrimSpace(string(data))
|
||||
if strings.HasPrefix(uuid, "CRYPT-") {
|
||||
dmNamePath := filepath.Join(sysBlock, entry.Name(), "dm", "name")
|
||||
if nameData, err := os.ReadFile(dmNamePath); err == nil {
|
||||
dmName := strings.TrimSpace(string(nameData))
|
||||
encryptedDevices["/dev/mapper/"+dmName] = true
|
||||
}
|
||||
encryptedDevices["/dev/"+entry.Name()] = true
|
||||
}
|
||||
}
|
||||
|
||||
return encryptedDevices
|
||||
}
|
||||
|
||||
// parseMounts reads /proc/mounts and maps devices to mount points with encryption status.
|
||||
func parseMounts(encryptedDevices map[string]bool) []DiskEncryptionVolume {
|
||||
var volumes []DiskEncryptionVolume
|
||||
|
||||
mountsFile, err := os.Open("/proc/mounts")
|
||||
if err != nil {
|
||||
log.Debugf("open /proc/mounts: %v", err)
|
||||
return volumes
|
||||
}
|
||||
defer func() {
|
||||
if err := mountsFile.Close(); err != nil {
|
||||
log.Debugf("close /proc/mounts: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
scanner := bufio.NewScanner(mountsFile)
|
||||
for scanner.Scan() {
|
||||
fields := strings.Fields(scanner.Text())
|
||||
if len(fields) < 2 {
|
||||
continue
|
||||
}
|
||||
device, mountPoint := fields[0], fields[1]
|
||||
|
||||
encrypted := encryptedDevices[device]
|
||||
|
||||
if !encrypted && strings.HasPrefix(device, "/dev/mapper/") {
|
||||
for encDev := range encryptedDevices {
|
||||
if device == encDev {
|
||||
encrypted = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
volumes = append(volumes, DiskEncryptionVolume{
|
||||
Path: mountPoint,
|
||||
Encrypted: encrypted,
|
||||
})
|
||||
}
|
||||
|
||||
return volumes
|
||||
}
|
||||
10
client/system/disk_encryption_stub.go
Normal file
10
client/system/disk_encryption_stub.go
Normal file
@@ -0,0 +1,10 @@
|
||||
//go:build android || ios || freebsd || js
|
||||
|
||||
package system
|
||||
|
||||
import "context"
|
||||
|
||||
// detectDiskEncryption is a stub for unsupported platforms.
|
||||
func detectDiskEncryption(_ context.Context) DiskEncryptionInfo {
|
||||
return DiskEncryptionInfo{}
|
||||
}
|
||||
41
client/system/disk_encryption_windows.go
Normal file
41
client/system/disk_encryption_windows.go
Normal file
@@ -0,0 +1,41 @@
|
||||
//go:build windows
|
||||
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/yusufpapurcu/wmi"
|
||||
)
|
||||
|
||||
// Win32EncryptableVolume represents the WMI class for BitLocker status.
|
||||
type Win32EncryptableVolume struct {
|
||||
DriveLetter string
|
||||
ProtectionStatus uint32
|
||||
}
|
||||
|
||||
// detectDiskEncryption detects BitLocker encryption status on Windows via WMI.
|
||||
func detectDiskEncryption(_ context.Context) DiskEncryptionInfo {
|
||||
info := DiskEncryptionInfo{}
|
||||
|
||||
var volumes []Win32EncryptableVolume
|
||||
query := "SELECT DriveLetter, ProtectionStatus FROM Win32_EncryptableVolume"
|
||||
|
||||
err := wmi.QueryNamespace(query, &volumes, `root\CIMV2\Security\MicrosoftVolumeEncryption`)
|
||||
if err != nil {
|
||||
log.Debugf("query BitLocker status: %v", err)
|
||||
return info
|
||||
}
|
||||
|
||||
for _, vol := range volumes {
|
||||
driveLetter := strings.TrimSuffix(vol.DriveLetter, "\\")
|
||||
info.Volumes = append(info.Volumes, DiskEncryptionVolume{
|
||||
Path: driveLetter,
|
||||
Encrypted: vol.ProtectionStatus == 1,
|
||||
})
|
||||
}
|
||||
|
||||
return info
|
||||
}
|
||||
@@ -59,6 +59,7 @@ type Info struct {
|
||||
SystemManufacturer string
|
||||
Environment Environment
|
||||
Files []File // for posture checks
|
||||
DiskEncryption DiskEncryptionInfo
|
||||
|
||||
RosenpassEnabled bool
|
||||
RosenpassPermissive bool
|
||||
|
||||
@@ -44,6 +44,7 @@ func GetInfo(ctx context.Context) *Info {
|
||||
SystemSerialNumber: serial(),
|
||||
SystemProductName: productModel(),
|
||||
SystemManufacturer: productManufacturer(),
|
||||
DiskEncryption: detectDiskEncryption(ctx),
|
||||
}
|
||||
|
||||
return gio
|
||||
|
||||
@@ -62,6 +62,7 @@ func GetInfo(ctx context.Context) *Info {
|
||||
SystemProductName: si.SystemProductName,
|
||||
SystemManufacturer: si.SystemManufacturer,
|
||||
Environment: si.Environment,
|
||||
DiskEncryption: detectDiskEncryption(ctx),
|
||||
}
|
||||
|
||||
systemHostname, _ := os.Hostname()
|
||||
|
||||
@@ -55,6 +55,7 @@ func GetInfo(ctx context.Context) *Info {
|
||||
UIVersion: extractUserAgent(ctx),
|
||||
KernelVersion: osInfo[1],
|
||||
Environment: env,
|
||||
DiskEncryption: detectDiskEncryption(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ func GetInfo(ctx context.Context) *Info {
|
||||
sysName := extractOsName(ctx, "sysName")
|
||||
swVersion := extractOsVersion(ctx, "swVersion")
|
||||
|
||||
gio := &Info{Kernel: sysName, OSVersion: swVersion, Platform: "unknown", OS: sysName, GoOS: runtime.GOOS, CPUs: runtime.NumCPU(), KernelVersion: swVersion}
|
||||
gio := &Info{Kernel: sysName, OSVersion: swVersion, Platform: "unknown", OS: sysName, GoOS: runtime.GOOS, CPUs: runtime.NumCPU(), KernelVersion: swVersion, DiskEncryption: detectDiskEncryption(ctx)}
|
||||
gio.Hostname = extractDeviceName(ctx, "hostname")
|
||||
gio.NetbirdVersion = version.NetbirdVersion()
|
||||
gio.UIVersion = extractUserAgent(ctx)
|
||||
|
||||
@@ -15,7 +15,7 @@ func UpdateStaticInfoAsync() {
|
||||
}
|
||||
|
||||
// GetInfo retrieves system information for WASM environment
|
||||
func GetInfo(_ context.Context) *Info {
|
||||
func GetInfo(ctx context.Context) *Info {
|
||||
info := &Info{
|
||||
GoOS: runtime.GOOS,
|
||||
Kernel: runtime.GOARCH,
|
||||
@@ -25,6 +25,7 @@ func GetInfo(_ context.Context) *Info {
|
||||
Hostname: "wasm-client",
|
||||
CPUs: runtime.NumCPU(),
|
||||
NetbirdVersion: version.NetbirdVersion(),
|
||||
DiskEncryption: detectDiskEncryption(ctx),
|
||||
}
|
||||
|
||||
collectBrowserInfo(info)
|
||||
|
||||
@@ -73,6 +73,7 @@ func GetInfo(ctx context.Context) *Info {
|
||||
SystemProductName: si.SystemProductName,
|
||||
SystemManufacturer: si.SystemManufacturer,
|
||||
Environment: si.Environment,
|
||||
DiskEncryption: detectDiskEncryption(ctx),
|
||||
}
|
||||
|
||||
return gio
|
||||
|
||||
@@ -35,6 +35,7 @@ func GetInfo(ctx context.Context) *Info {
|
||||
SystemProductName: si.SystemProductName,
|
||||
SystemManufacturer: si.SystemManufacturer,
|
||||
Environment: si.Environment,
|
||||
DiskEncryption: detectDiskEncryption(ctx),
|
||||
}
|
||||
|
||||
addrs, err := networkAddresses()
|
||||
|
||||
@@ -470,6 +470,16 @@ func extractPeerMeta(ctx context.Context, meta *proto.PeerSystemMeta) nbpeer.Pee
|
||||
})
|
||||
}
|
||||
|
||||
diskEncryptionVolumes := make([]nbpeer.DiskEncryptionVolume, 0)
|
||||
if meta.GetDiskEncryption() != nil {
|
||||
for _, vol := range meta.GetDiskEncryption().GetVolumes() {
|
||||
diskEncryptionVolumes = append(diskEncryptionVolumes, nbpeer.DiskEncryptionVolume{
|
||||
Path: vol.GetPath(),
|
||||
Encrypted: vol.GetEncrypted(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return nbpeer.PeerSystemMeta{
|
||||
Hostname: meta.GetHostname(),
|
||||
GoOS: meta.GetGoOS(),
|
||||
@@ -501,6 +511,9 @@ func extractPeerMeta(ctx context.Context, meta *proto.PeerSystemMeta) nbpeer.Pee
|
||||
LazyConnectionEnabled: meta.GetFlags().GetLazyConnectionEnabled(),
|
||||
},
|
||||
Files: files,
|
||||
DiskEncryption: nbpeer.DiskEncryptionInfo{
|
||||
Volumes: diskEncryptionVolumes,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -95,6 +95,27 @@ type File struct {
|
||||
ProcessIsRunning bool
|
||||
}
|
||||
|
||||
// DiskEncryptionVolume represents encryption status of a volume.
|
||||
type DiskEncryptionVolume struct {
|
||||
Path string
|
||||
Encrypted bool
|
||||
}
|
||||
|
||||
// DiskEncryptionInfo holds encryption info for all volumes.
|
||||
type DiskEncryptionInfo struct {
|
||||
Volumes []DiskEncryptionVolume `gorm:"serializer:json"`
|
||||
}
|
||||
|
||||
// IsEncrypted returns true if the volume at path is encrypted.
|
||||
func (d DiskEncryptionInfo) IsEncrypted(path string) bool {
|
||||
for _, v := range d.Volumes {
|
||||
if v.Path == path {
|
||||
return v.Encrypted
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Flags defines a set of options to control feature behavior
|
||||
type Flags struct {
|
||||
RosenpassEnabled bool
|
||||
@@ -127,9 +148,10 @@ type PeerSystemMeta struct { //nolint:revive
|
||||
SystemSerialNumber string
|
||||
SystemProductName string
|
||||
SystemManufacturer string
|
||||
Environment Environment `gorm:"serializer:json"`
|
||||
Flags Flags `gorm:"serializer:json"`
|
||||
Files []File `gorm:"serializer:json"`
|
||||
Environment Environment `gorm:"serializer:json"`
|
||||
Flags Flags `gorm:"serializer:json"`
|
||||
Files []File `gorm:"serializer:json"`
|
||||
DiskEncryption DiskEncryptionInfo `gorm:"serializer:json"`
|
||||
}
|
||||
|
||||
func (p PeerSystemMeta) isEqual(other PeerSystemMeta) bool {
|
||||
@@ -159,6 +181,19 @@ func (p PeerSystemMeta) isEqual(other PeerSystemMeta) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
sort.Slice(p.DiskEncryption.Volumes, func(i, j int) bool {
|
||||
return p.DiskEncryption.Volumes[i].Path < p.DiskEncryption.Volumes[j].Path
|
||||
})
|
||||
sort.Slice(other.DiskEncryption.Volumes, func(i, j int) bool {
|
||||
return other.DiskEncryption.Volumes[i].Path < other.DiskEncryption.Volumes[j].Path
|
||||
})
|
||||
equalDiskEncryption := slices.EqualFunc(p.DiskEncryption.Volumes, other.DiskEncryption.Volumes, func(vol DiskEncryptionVolume, oVol DiskEncryptionVolume) bool {
|
||||
return vol.Path == oVol.Path && vol.Encrypted == oVol.Encrypted
|
||||
})
|
||||
if !equalDiskEncryption {
|
||||
return false
|
||||
}
|
||||
|
||||
return p.Hostname == other.Hostname &&
|
||||
p.GoOS == other.GoOS &&
|
||||
p.Kernel == other.Kernel &&
|
||||
|
||||
@@ -18,6 +18,7 @@ const (
|
||||
GeoLocationCheckName = "GeoLocationCheck"
|
||||
PeerNetworkRangeCheckName = "PeerNetworkRangeCheck"
|
||||
ProcessCheckName = "ProcessCheck"
|
||||
DiskEncryptionCheckName = "DiskEncryptionCheck"
|
||||
|
||||
CheckActionAllow string = "allow"
|
||||
CheckActionDeny string = "deny"
|
||||
@@ -58,6 +59,7 @@ type ChecksDefinition struct {
|
||||
GeoLocationCheck *GeoLocationCheck `json:",omitempty"`
|
||||
PeerNetworkRangeCheck *PeerNetworkRangeCheck `json:",omitempty"`
|
||||
ProcessCheck *ProcessCheck `json:",omitempty"`
|
||||
DiskEncryptionCheck *DiskEncryptionCheck `json:",omitempty"`
|
||||
}
|
||||
|
||||
// Copy returns a copy of a checks definition.
|
||||
@@ -110,6 +112,13 @@ func (cd ChecksDefinition) Copy() ChecksDefinition {
|
||||
}
|
||||
copy(cdCopy.ProcessCheck.Processes, processCheck.Processes)
|
||||
}
|
||||
if cd.DiskEncryptionCheck != nil {
|
||||
cdCopy.DiskEncryptionCheck = &DiskEncryptionCheck{
|
||||
LinuxPath: cd.DiskEncryptionCheck.LinuxPath,
|
||||
DarwinPath: cd.DiskEncryptionCheck.DarwinPath,
|
||||
WindowsPath: cd.DiskEncryptionCheck.WindowsPath,
|
||||
}
|
||||
}
|
||||
return cdCopy
|
||||
}
|
||||
|
||||
@@ -153,6 +162,9 @@ func (pc *Checks) GetChecks() []Check {
|
||||
if pc.Checks.ProcessCheck != nil {
|
||||
checks = append(checks, pc.Checks.ProcessCheck)
|
||||
}
|
||||
if pc.Checks.DiskEncryptionCheck != nil {
|
||||
checks = append(checks, pc.Checks.DiskEncryptionCheck)
|
||||
}
|
||||
return checks
|
||||
}
|
||||
|
||||
@@ -208,6 +220,10 @@ func buildPostureCheck(postureChecksID string, name string, description string,
|
||||
postureChecks.Checks.ProcessCheck = toProcessCheck(processCheck)
|
||||
}
|
||||
|
||||
if diskEncryptionCheck := checks.DiskEncryptionCheck; diskEncryptionCheck != nil {
|
||||
postureChecks.Checks.DiskEncryptionCheck = toDiskEncryptionCheck(diskEncryptionCheck)
|
||||
}
|
||||
|
||||
return &postureChecks, nil
|
||||
}
|
||||
|
||||
@@ -242,6 +258,10 @@ func (pc *Checks) ToAPIResponse() *api.PostureCheck {
|
||||
checks.ProcessCheck = toProcessCheckResponse(pc.Checks.ProcessCheck)
|
||||
}
|
||||
|
||||
if pc.Checks.DiskEncryptionCheck != nil {
|
||||
checks.DiskEncryptionCheck = toDiskEncryptionCheckResponse(pc.Checks.DiskEncryptionCheck)
|
||||
}
|
||||
|
||||
return &api.PostureCheck{
|
||||
Id: pc.ID,
|
||||
Name: pc.Name,
|
||||
@@ -386,3 +406,25 @@ func toProcessCheck(check *api.ProcessCheck) *ProcessCheck {
|
||||
Processes: processes,
|
||||
}
|
||||
}
|
||||
|
||||
func toDiskEncryptionCheck(check *api.DiskEncryptionCheck) *DiskEncryptionCheck {
|
||||
d := &DiskEncryptionCheck{}
|
||||
if check.LinuxPath != nil {
|
||||
d.LinuxPath = *check.LinuxPath
|
||||
}
|
||||
if check.DarwinPath != nil {
|
||||
d.DarwinPath = *check.DarwinPath
|
||||
}
|
||||
if check.WindowsPath != nil {
|
||||
d.WindowsPath = *check.WindowsPath
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
func toDiskEncryptionCheckResponse(check *DiskEncryptionCheck) *api.DiskEncryptionCheck {
|
||||
return &api.DiskEncryptionCheck{
|
||||
LinuxPath: &check.LinuxPath,
|
||||
DarwinPath: &check.DarwinPath,
|
||||
WindowsPath: &check.WindowsPath,
|
||||
}
|
||||
}
|
||||
|
||||
52
management/server/posture/disk_encryption.go
Normal file
52
management/server/posture/disk_encryption.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package posture
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
||||
)
|
||||
|
||||
// DiskEncryptionCheck verifies that specified volumes are encrypted.
|
||||
type DiskEncryptionCheck struct {
|
||||
LinuxPath string
|
||||
DarwinPath string
|
||||
WindowsPath string
|
||||
}
|
||||
|
||||
var _ Check = (*DiskEncryptionCheck)(nil)
|
||||
|
||||
// Name returns the name of the check.
|
||||
func (d *DiskEncryptionCheck) Name() string {
|
||||
return DiskEncryptionCheckName
|
||||
}
|
||||
|
||||
// Check performs the disk encryption verification for the given peer.
|
||||
func (d *DiskEncryptionCheck) Check(_ context.Context, peer nbpeer.Peer) (bool, error) {
|
||||
var pathToCheck string
|
||||
|
||||
switch peer.Meta.GoOS {
|
||||
case "linux":
|
||||
pathToCheck = d.LinuxPath
|
||||
case "darwin":
|
||||
pathToCheck = d.DarwinPath
|
||||
case "windows":
|
||||
pathToCheck = d.WindowsPath
|
||||
default:
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if pathToCheck == "" {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return peer.Meta.DiskEncryption.IsEncrypted(pathToCheck), nil
|
||||
}
|
||||
|
||||
// Validate checks the configuration of the disk encryption check.
|
||||
func (d *DiskEncryptionCheck) Validate() error {
|
||||
if d.LinuxPath == "" && d.DarwinPath == "" && d.WindowsPath == "" {
|
||||
return fmt.Errorf("%s at least one path must be configured", d.Name())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
306
management/server/posture/disk_encryption_test.go
Normal file
306
management/server/posture/disk_encryption_test.go
Normal file
@@ -0,0 +1,306 @@
|
||||
package posture
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server/peer"
|
||||
)
|
||||
|
||||
func TestDiskEncryptionCheck_Check(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input peer.Peer
|
||||
check DiskEncryptionCheck
|
||||
wantErr bool
|
||||
isValid bool
|
||||
}{
|
||||
{
|
||||
name: "linux with encrypted root",
|
||||
input: peer.Peer{
|
||||
Meta: peer.PeerSystemMeta{
|
||||
GoOS: "linux",
|
||||
DiskEncryption: peer.DiskEncryptionInfo{
|
||||
Volumes: []peer.DiskEncryptionVolume{
|
||||
{Path: "/", Encrypted: true},
|
||||
{Path: "/home", Encrypted: true},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
check: DiskEncryptionCheck{
|
||||
LinuxPath: "/",
|
||||
},
|
||||
wantErr: false,
|
||||
isValid: true,
|
||||
},
|
||||
{
|
||||
name: "linux with unencrypted root",
|
||||
input: peer.Peer{
|
||||
Meta: peer.PeerSystemMeta{
|
||||
GoOS: "linux",
|
||||
DiskEncryption: peer.DiskEncryptionInfo{
|
||||
Volumes: []peer.DiskEncryptionVolume{
|
||||
{Path: "/", Encrypted: false},
|
||||
{Path: "/home", Encrypted: true},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
check: DiskEncryptionCheck{
|
||||
LinuxPath: "/",
|
||||
},
|
||||
wantErr: false,
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
name: "linux with no volume info",
|
||||
input: peer.Peer{
|
||||
Meta: peer.PeerSystemMeta{
|
||||
GoOS: "linux",
|
||||
DiskEncryption: peer.DiskEncryptionInfo{},
|
||||
},
|
||||
},
|
||||
check: DiskEncryptionCheck{
|
||||
LinuxPath: "/",
|
||||
},
|
||||
wantErr: false,
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
name: "darwin with encrypted root",
|
||||
input: peer.Peer{
|
||||
Meta: peer.PeerSystemMeta{
|
||||
GoOS: "darwin",
|
||||
DiskEncryption: peer.DiskEncryptionInfo{
|
||||
Volumes: []peer.DiskEncryptionVolume{
|
||||
{Path: "/", Encrypted: true},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
check: DiskEncryptionCheck{
|
||||
DarwinPath: "/",
|
||||
},
|
||||
wantErr: false,
|
||||
isValid: true,
|
||||
},
|
||||
{
|
||||
name: "darwin with unencrypted root",
|
||||
input: peer.Peer{
|
||||
Meta: peer.PeerSystemMeta{
|
||||
GoOS: "darwin",
|
||||
DiskEncryption: peer.DiskEncryptionInfo{
|
||||
Volumes: []peer.DiskEncryptionVolume{
|
||||
{Path: "/", Encrypted: false},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
check: DiskEncryptionCheck{
|
||||
DarwinPath: "/",
|
||||
},
|
||||
wantErr: false,
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
name: "windows with encrypted C drive",
|
||||
input: peer.Peer{
|
||||
Meta: peer.PeerSystemMeta{
|
||||
GoOS: "windows",
|
||||
DiskEncryption: peer.DiskEncryptionInfo{
|
||||
Volumes: []peer.DiskEncryptionVolume{
|
||||
{Path: "C:", Encrypted: true},
|
||||
{Path: "D:", Encrypted: false},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
check: DiskEncryptionCheck{
|
||||
WindowsPath: "C:",
|
||||
},
|
||||
wantErr: false,
|
||||
isValid: true,
|
||||
},
|
||||
{
|
||||
name: "windows with unencrypted C drive",
|
||||
input: peer.Peer{
|
||||
Meta: peer.PeerSystemMeta{
|
||||
GoOS: "windows",
|
||||
DiskEncryption: peer.DiskEncryptionInfo{
|
||||
Volumes: []peer.DiskEncryptionVolume{
|
||||
{Path: "C:", Encrypted: false},
|
||||
{Path: "D:", Encrypted: true},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
check: DiskEncryptionCheck{
|
||||
WindowsPath: "C:",
|
||||
},
|
||||
wantErr: false,
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
name: "unsupported ios operating system",
|
||||
input: peer.Peer{
|
||||
Meta: peer.PeerSystemMeta{
|
||||
GoOS: "ios",
|
||||
},
|
||||
},
|
||||
check: DiskEncryptionCheck{
|
||||
LinuxPath: "/",
|
||||
DarwinPath: "/",
|
||||
WindowsPath: "C:",
|
||||
},
|
||||
wantErr: false,
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
name: "unsupported android operating system",
|
||||
input: peer.Peer{
|
||||
Meta: peer.PeerSystemMeta{
|
||||
GoOS: "android",
|
||||
},
|
||||
},
|
||||
check: DiskEncryptionCheck{
|
||||
LinuxPath: "/",
|
||||
DarwinPath: "/",
|
||||
WindowsPath: "C:",
|
||||
},
|
||||
wantErr: false,
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
name: "linux peer with no linux path configured passes",
|
||||
input: peer.Peer{
|
||||
Meta: peer.PeerSystemMeta{
|
||||
GoOS: "linux",
|
||||
DiskEncryption: peer.DiskEncryptionInfo{
|
||||
Volumes: []peer.DiskEncryptionVolume{
|
||||
{Path: "/", Encrypted: false},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
check: DiskEncryptionCheck{
|
||||
DarwinPath: "/",
|
||||
WindowsPath: "C:",
|
||||
},
|
||||
wantErr: false,
|
||||
isValid: true,
|
||||
},
|
||||
{
|
||||
name: "darwin peer with no darwin path configured passes",
|
||||
input: peer.Peer{
|
||||
Meta: peer.PeerSystemMeta{
|
||||
GoOS: "darwin",
|
||||
DiskEncryption: peer.DiskEncryptionInfo{
|
||||
Volumes: []peer.DiskEncryptionVolume{
|
||||
{Path: "/", Encrypted: false},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
check: DiskEncryptionCheck{
|
||||
LinuxPath: "/",
|
||||
WindowsPath: "C:",
|
||||
},
|
||||
wantErr: false,
|
||||
isValid: true,
|
||||
},
|
||||
{
|
||||
name: "windows peer with no windows path configured passes",
|
||||
input: peer.Peer{
|
||||
Meta: peer.PeerSystemMeta{
|
||||
GoOS: "windows",
|
||||
DiskEncryption: peer.DiskEncryptionInfo{
|
||||
Volumes: []peer.DiskEncryptionVolume{
|
||||
{Path: "C:", Encrypted: false},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
check: DiskEncryptionCheck{
|
||||
LinuxPath: "/",
|
||||
DarwinPath: "/",
|
||||
},
|
||||
wantErr: false,
|
||||
isValid: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
isValid, err := tt.check.Check(context.Background(), tt.input)
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
assert.Equal(t, tt.isValid, isValid)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDiskEncryptionCheck_Validate(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
check DiskEncryptionCheck
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
name: "valid linux, darwin and windows paths",
|
||||
check: DiskEncryptionCheck{
|
||||
LinuxPath: "/",
|
||||
DarwinPath: "/",
|
||||
WindowsPath: "C:",
|
||||
},
|
||||
expectedError: false,
|
||||
},
|
||||
{
|
||||
name: "valid linux path only",
|
||||
check: DiskEncryptionCheck{
|
||||
LinuxPath: "/",
|
||||
},
|
||||
expectedError: false,
|
||||
},
|
||||
{
|
||||
name: "valid darwin path only",
|
||||
check: DiskEncryptionCheck{
|
||||
DarwinPath: "/",
|
||||
},
|
||||
expectedError: false,
|
||||
},
|
||||
{
|
||||
name: "valid windows path only",
|
||||
check: DiskEncryptionCheck{
|
||||
WindowsPath: "C:",
|
||||
},
|
||||
expectedError: false,
|
||||
},
|
||||
{
|
||||
name: "invalid empty paths",
|
||||
check: DiskEncryptionCheck{},
|
||||
expectedError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err := tc.check.Validate()
|
||||
if tc.expectedError {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDiskEncryptionCheck_Name(t *testing.T) {
|
||||
check := DiskEncryptionCheck{}
|
||||
assert.Equal(t, DiskEncryptionCheckName, check.Name())
|
||||
}
|
||||
@@ -550,6 +550,14 @@ func infoToMetaData(info *system.Info) *proto.PeerSystemMeta {
|
||||
})
|
||||
}
|
||||
|
||||
diskEncryptionVolumes := make([]*proto.DiskEncryptionVolume, 0, len(info.DiskEncryption.Volumes))
|
||||
for _, vol := range info.DiskEncryption.Volumes {
|
||||
diskEncryptionVolumes = append(diskEncryptionVolumes, &proto.DiskEncryptionVolume{
|
||||
Path: vol.Path,
|
||||
Encrypted: vol.Encrypted,
|
||||
})
|
||||
}
|
||||
|
||||
return &proto.PeerSystemMeta{
|
||||
Hostname: info.Hostname,
|
||||
GoOS: info.GoOS,
|
||||
@@ -585,5 +593,8 @@ func infoToMetaData(info *system.Info) *proto.PeerSystemMeta {
|
||||
|
||||
LazyConnectionEnabled: info.LazyConnectionEnabled,
|
||||
},
|
||||
DiskEncryption: &proto.DiskEncryptionInfo{
|
||||
Volumes: diskEncryptionVolumes,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1232,6 +1232,8 @@ components:
|
||||
$ref: '#/components/schemas/PeerNetworkRangeCheck'
|
||||
process_check:
|
||||
$ref: '#/components/schemas/ProcessCheck'
|
||||
disk_encryption_check:
|
||||
$ref: '#/components/schemas/DiskEncryptionCheck'
|
||||
NBVersionCheck:
|
||||
description: Posture check for the version of NetBird
|
||||
type: object
|
||||
@@ -1346,6 +1348,22 @@ components:
|
||||
description: Path to the process executable file in a Windows operating system
|
||||
type: string
|
||||
example: "C:\ProgramData\NetBird\netbird.exe"
|
||||
DiskEncryptionCheck:
|
||||
description: Posture check for disk encryption status
|
||||
type: object
|
||||
properties:
|
||||
linux_path:
|
||||
description: Mount path to check on Linux (e.g., "/", "/home")
|
||||
type: string
|
||||
example: "/"
|
||||
darwin_path:
|
||||
description: Mount path to check on macOS (e.g., "/")
|
||||
type: string
|
||||
example: "/"
|
||||
windows_path:
|
||||
description: Drive letter to check on Windows (e.g., "C:", "D:")
|
||||
type: string
|
||||
example: "C:"
|
||||
Location:
|
||||
description: Describe geographical location information
|
||||
type: object
|
||||
|
||||
@@ -370,6 +370,9 @@ type AvailablePorts struct {
|
||||
|
||||
// Checks List of objects that perform the actual checks
|
||||
type Checks struct {
|
||||
// DiskEncryptionCheck Posture check for disk encryption status
|
||||
DiskEncryptionCheck *DiskEncryptionCheck `json:"disk_encryption_check,omitempty"`
|
||||
|
||||
// GeoLocationCheck Posture check for geo location
|
||||
GeoLocationCheck *GeoLocationCheck `json:"geo_location_check,omitempty"`
|
||||
|
||||
@@ -476,6 +479,18 @@ type DNSSettings struct {
|
||||
DisabledManagementGroups []string `json:"disabled_management_groups"`
|
||||
}
|
||||
|
||||
// DiskEncryptionCheck Posture check for disk encryption status
|
||||
type DiskEncryptionCheck struct {
|
||||
// DarwinPath Mount path to check on macOS (e.g., "/")
|
||||
DarwinPath *string `json:"darwin_path,omitempty"`
|
||||
|
||||
// LinuxPath Mount path to check on Linux (e.g., "/", "/home")
|
||||
LinuxPath *string `json:"linux_path,omitempty"`
|
||||
|
||||
// WindowsPath Drive letter to check on Windows (e.g., "C:", "D:")
|
||||
WindowsPath *string `json:"windows_path,omitempty"`
|
||||
}
|
||||
|
||||
// Event defines model for Event.
|
||||
type Event struct {
|
||||
// Activity The activity that occurred during the event
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -154,6 +154,19 @@ message Flags {
|
||||
bool disableSSHAuth = 15;
|
||||
}
|
||||
|
||||
// DiskEncryptionVolume represents encryption status of a single volume.
|
||||
message DiskEncryptionVolume {
|
||||
// Mount path (Linux/macOS) or drive letter (Windows)
|
||||
string path = 1;
|
||||
// Whether the volume is encrypted
|
||||
bool encrypted = 2;
|
||||
}
|
||||
|
||||
// DiskEncryptionInfo holds disk encryption detection results.
|
||||
message DiskEncryptionInfo {
|
||||
repeated DiskEncryptionVolume volumes = 1;
|
||||
}
|
||||
|
||||
// PeerSystemMeta is machine meta data like OS and version.
|
||||
message PeerSystemMeta {
|
||||
string hostname = 1;
|
||||
@@ -173,6 +186,7 @@ message PeerSystemMeta {
|
||||
Environment environment = 15;
|
||||
repeated File files = 16;
|
||||
Flags flags = 17;
|
||||
DiskEncryptionInfo diskEncryption = 18;
|
||||
}
|
||||
|
||||
message LoginResponse {
|
||||
|
||||
Reference in New Issue
Block a user