From 9d38d88b591216687247e946159f06eb94e67aee Mon Sep 17 00:00:00 2001 From: Shahriyar Jalayeri Date: Mon, 6 Nov 2023 16:14:30 +0100 Subject: [PATCH] security : check secure mount options Signed-off-by: Shahriyar Jalayeri --- tests/sec/{remote.go => rutils.go} | 36 +++++++++++++ tests/sec/sec_test.go | 81 +++++++++++++++++++++++++++++- 2 files changed, 116 insertions(+), 1 deletion(-) rename tests/sec/{remote.go => rutils.go} (70%) diff --git a/tests/sec/remote.go b/tests/sec/rutils.go similarity index 70% rename from tests/sec/remote.go rename to tests/sec/rutils.go index c259a3af9..cc5e43b26 100644 --- a/tests/sec/remote.go +++ b/tests/sec/rutils.go @@ -1,6 +1,7 @@ package sec_test import ( + "encoding/json" "fmt" "io" "os" @@ -11,6 +12,12 @@ import ( "github.com/lf-edge/eden/pkg/utils" ) +type mount struct { + Path string `json:"path"` + Type string `json:"type"` + Options string `json:"options"` +} + type remoteNode struct { openEVEC *openevec.OpenEVEC } @@ -86,3 +93,32 @@ func (node *remoteNode) readFile(fileName string) ([]byte, error) { command := fmt.Sprintf("cat %s", fileName) return node.runCommand(command) } + +func (node *remoteNode) getMountPoints(mtype string) ([]mount, error) { + mountCommand := "mount -l" + if mtype != "" { + mountCommand = fmt.Sprintf("mount -l -t %s", mtype) + } + + command := mountCommand + ` | awk ' + BEGIN { print " [ "} + { + printf " %s {\"path\": \"%s\", \"type\": \"%s\", \"options\": \"%s\"}", separator, $3, $5, $6; + separator = ","; + } + END { print " ] " } + '` + + out, err := node.runCommand(command) + if err != nil { + return nil, err + } + + var mounts []mount + if err := json.Unmarshal(out, &mounts); err != nil { + return nil, err + + } + + return mounts, nil +} diff --git a/tests/sec/sec_test.go b/tests/sec/sec_test.go index 537606930..480754349 100644 --- a/tests/sec/sec_test.go +++ b/tests/sec/sec_test.go @@ -93,7 +93,6 @@ func TestMain(m *testing.M) { func TestAppArmorEnabled(t *testing.T) { log.Println("TestAppArmorEnabled started") defer log.Println("TestAppArmorEnabled finished") - t.Parallel() edgeNode := tc.GetEdgeNode(tc.WithTest(t)) tc.WaitForState(edgeNode, 60) @@ -108,3 +107,83 @@ func TestAppArmorEnabled(t *testing.T) { t.Fatal("AppArmor is not enabled") } } + +func TestCheckMountOptions(t *testing.T) { + log.Println("TestCheckMountOptions started") + defer log.Println("TestCheckMountOptions finished") + + edgeNode := tc.GetEdgeNode(tc.WithTest(t)) + tc.WaitForState(edgeNode, 60) + + fail := false + mounts, err := rnode.getMountPoints("") + if err != nil { + t.Fatal(err) + } + + // checl of mounts of type proc are secure + misconfig := checkMountOptionsByType("proc", mounts, []string{"nosuid", "nodev", "noexec"}) + if len(misconfig) > 0 { + for _, msg := range misconfig { + t.Logf("[FAIL] %s", msg) + } + fail = true + } + + // TODO: set hidepid=2 on /proc and this to the above list + misconfig = checkMountOptionsByType("proc", mounts, []string{"hidepid=2"}) + if len(misconfig) > 0 { + for _, msg := range misconfig { + t.Logf("[FAIL] %s", msg) + } + } + + // check of mounts of type tmpfs are secure + misconfig = checkMountOptionsByType("tmpfs", mounts, []string{"nosuid", "nodev", "noexec"}) + if len(misconfig) > 0 { + for _, msg := range misconfig { + t.Logf("[FAIL] %s", msg) + } + fail = true + } + + if fail { + t.Fatal("Some mount options are not secure, see logs above") + } +} + +func checkMountSecurityOptions(mount mount, secureOptions []string) []string { + secOptNotFound := make([]string, 0) + + for _, option := range secureOptions { + if !strings.Contains(mount.Options, option) { + secOptNotFound = append(secOptNotFound, fmt.Sprintf("'%s' option is not set on %s", option, mount.Path)) + } + } + + return secOptNotFound +} + +func checkMountOptionsByType(mountType string, mounts []mount, options []string) []string { + secOptNotFound := make([]string, 0) + for _, mount := range mounts { + if mount.Type == mountType { + misses := checkMountSecurityOptions(mount, options) + secOptNotFound = append(secOptNotFound, misses...) + } + } + + return secOptNotFound +} + +func checkMountOptionsByPath(mountPath string, mounts []mount, options []string) []string { + secOptNotFound := make([]string, 0) + for _, mount := range mounts { + if mount.Path == mountPath { + misses := checkMountSecurityOptions(mount, options) + secOptNotFound = append(secOptNotFound, misses...) + } + } + + return secOptNotFound +}