Skip to content

Commit

Permalink
selinux: Print status and usage of RawSelinux
Browse files Browse the repository at this point in the history
The nodestatus controller didn't handle the rawselinux policy at all.
Let's handle the kind in the nodestatus and add a very basic test for
the rawselinuxprofile so that we are sure it works at all.
  • Loading branch information
jhrozek authored and ccojocar committed Jan 23, 2024
1 parent b1bb82a commit 457592d
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 18 deletions.
12 changes: 8 additions & 4 deletions test/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ func (e *e2e) TestSecurityProfilesOperator() {
"SELinux: base case (install policy, run pod and delete)",
e.testCaseSelinuxBaseUsage,
},
{
"SELinux: base case (install policy, run pod and delete) for the raw CR",
e.testCaseRawSelinuxBaseUsage,
},
{
"SELinux: non-default template",
e.testCaseSelinuxNonDefaultTemplate,
Expand Down Expand Up @@ -430,19 +434,19 @@ func (e *e2e) writeAndDo(verb, manifest, filePattern string) func() {
return func() { os.Remove(fileName) }
}

func (e *e2e) getSELinuxPolicyName(policy string) string {
usageStr := e.getSELinuxPolicyUsage(policy)
func (e *e2e) getSELinuxPolicyName(kind, policy string) string {
usageStr := e.getSELinuxPolicyUsage(kind, policy)
// Udica (the library that helps out generate SELinux policies),
// adds .process in the end of the usage. So we need to remove it
// to get the module name
return strings.TrimSuffix(usageStr, ".process")
}

func (e *e2e) getSELinuxPolicyUsage(policy string) string {
func (e *e2e) getSELinuxPolicyUsage(kind, policy string) string {
ns := e.getCurrentContextNamespace(defaultNamespace)
// This describes the usage string, which is not necessarily
// the name of the policy in the node
return e.kubectl("get", "selinuxprofile", "-n", ns, policy, "-o", "jsonpath={.status.usage}")
return e.kubectl("get", kind, "-n", ns, policy, "-o", "jsonpath={.status.usage}")
}

func (e *e2e) sliceContainsString(slice []string, s string) bool {
Expand Down
2 changes: 1 addition & 1 deletion test/tc_metrics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func (e *e2e) testCaseSelinuxMetrics(nodes []string) {
e.kubectl("wait", "--timeout", defaultSelinuxOpTimeout,
"--for", "condition=ready", "selinuxprofile", "errorlogger")

rawPolicyName := e.getSELinuxPolicyName("errorlogger")
rawPolicyName := e.getSELinuxPolicyName("selinuxprofile", "errorlogger")
e.logf("assert errorlogger policy is installed")
e.assertSelinuxPolicyIsInstalled(nodes, rawPolicyName, maxNodeIterations, sleepBetweenIterations)

Expand Down
46 changes: 34 additions & 12 deletions test/tc_selinux_base_usage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,19 @@ spec:
- open
`

rawErrorloggerPolicy = `
apiVersion: security-profiles-operator.x-k8s.io/v1alpha2
kind: RawSelinuxProfile
metadata:
name: raw-errorlogger
spec:
policy: |
(blockinherit container)
(allow process var_log_t ( dir ( open read getattr lock search ioctl add_name remove_name write )))
(allow process var_log_t ( file ( getattr read write append ioctl lock map open create )))
(allow process var_log_t ( sock_file ( getattr read write append open )))
`

// this is the equivalent of errorloggerPolicy but with several calls removed. The idea is to
// ensure that the workload will fail if the policy in incomplete. Allows setting a parameter
// as needed.
Expand Down Expand Up @@ -139,28 +152,37 @@ spec:
)

func (e *e2e) testCaseSelinuxBaseUsage(nodes []string) {
e.selinuxBaseUsage("selinuxprofile", errorloggerPolicy, "errorlogger", nodes)
}

func (e *e2e) testCaseRawSelinuxBaseUsage(nodes []string) {
e.selinuxBaseUsage("rawselinuxprofile", rawErrorloggerPolicy, "raw-errorlogger", nodes)
}

func (e *e2e) selinuxBaseUsage(kind, policy, polName string, nodes []string) {
e.selinuxOnlyTestCase()

e.logf("The 'errorlogger' workload should be able to use SELinux policy")

e.logf("creating policy")
e.writeAndCreate(errorloggerPolicy, "errorlogger-policy.yml")
rmFn := e.writeAndCreate(policy, "errorlogger-policy.yml")
defer rmFn()

// Let's wait for the policy to be processed
e.kubectl("wait", "--timeout", defaultSelinuxOpTimeout,
"--for", "condition=ready", "selinuxprofile", "errorlogger")
"--for", "condition=ready", kind, polName)

rawPolicyName := e.getSELinuxPolicyName("errorlogger")
rawPolicyName := e.getSELinuxPolicyName(kind, polName)

e.logf("assert policy is installed")
e.assertSelinuxPolicyIsInstalled(nodes, rawPolicyName, maxNodeIterations, sleepBetweenIterations)

e.logf("creating workload")

podWithPolicy := fmt.Sprintf(podWithPolicyFmt, e.getSELinuxPolicyUsage("errorlogger"))
podWithPolicy := fmt.Sprintf(podWithPolicyFmt, e.getSELinuxPolicyUsage(kind, polName))
e.writeAndCreate(podWithPolicy, "pod-w-policy.yml")

e.waitFor("condition=ready", "pod", "errorlogger")
e.waitFor("condition=ready", "pod", polName)

e.logf("the workload should be running")
podWithPolicyPhase := e.kubectl(
Expand All @@ -175,7 +197,7 @@ func (e *e2e) testCaseSelinuxBaseUsage(nodes []string) {
e.kubectl("delete", "pod", "errorlogger")

e.logf("removing policy")
e.kubectl("delete", "selinuxprofile", "errorlogger")
e.kubectl("delete", kind, polName)

e.logf("assert policy was removed")
e.assertSelinuxPolicyIsRemoved(nodes, rawPolicyName, maxNodeIterations, sleepBetweenIterations)
Expand All @@ -198,7 +220,7 @@ func (e *e2e) testCaseSelinuxIncompletePolicy() {
"--for", "condition=ready", "selinuxprofile", enforcingProfileName)

e.logf("creating workload - it should become ready, but fail")
podWithPolicy := fmt.Sprintf(podWithPolicyFmt, e.getSELinuxPolicyUsage(enforcingProfileName))
podWithPolicy := fmt.Sprintf(podWithPolicyFmt, e.getSELinuxPolicyUsage("selinuxprofile", enforcingProfileName))
e.writeAndCreate(podWithPolicy, "pod-w-incomplete-policy.yml")

// note: this would have been much nicer with kubectl wait --jsonpath, but I found it racy incase the status
Expand Down Expand Up @@ -240,7 +262,7 @@ func (e *e2e) testCaseSelinuxNonDefaultTemplate(nodes []string) {
e.kubectl("wait", "--timeout", defaultSelinuxOpTimeout,
"--for", "condition=ready", "selinuxprofile", netContainerPolicyName)

rawPolicyName := e.getSELinuxPolicyName(netContainerPolicyName)
rawPolicyName := e.getSELinuxPolicyName("selinuxprofile", netContainerPolicyName)

e.logf("assert policy is installed")
e.assertSelinuxPolicyIsInstalled(nodes, rawPolicyName, maxNodeIterations, sleepBetweenIterations)
Expand All @@ -263,7 +285,7 @@ func (e *e2e) testCaseSelinuxIncompletePermissivePolicy() {
"--for", "condition=ready", "selinuxprofile", permissiveProfileName)

e.logf("creating workload - it should become ready, but fail")
podWithPolicy := fmt.Sprintf(podWithPolicyFmt, e.getSELinuxPolicyUsage(permissiveProfileName))
podWithPolicy := fmt.Sprintf(podWithPolicyFmt, e.getSELinuxPolicyUsage("selinuxprofile", permissiveProfileName))
e.writeAndCreate(podWithPolicy, "pod-w-incomplete-permissive-policy.yml")

e.waitFor("condition=ready", "pod", "errorlogger")
Expand Down Expand Up @@ -292,7 +314,7 @@ func (e *e2e) testCaseSelinuxIncompleteDisabledPolicy() {
"--for", "condition=ready=false", "selinuxprofile", disabledProfileName)

e.logf("creating workload - it should not even become ready")
podWithPolicy := fmt.Sprintf(podWithPolicyFmt, e.getSELinuxPolicyUsage(disabledProfileName))
podWithPolicy := fmt.Sprintf(podWithPolicyFmt, e.getSELinuxPolicyUsage("selinuxprofile", disabledProfileName))
e.writeAndCreate(podWithPolicy, "pod-w-incomplete-disabled-policy.yml")

var exitCode string
Expand Down Expand Up @@ -332,7 +354,7 @@ func (e *e2e) assertSelinuxPolicyIsInstalled(nodes []string, policy string, node
if missingPolName != "" {
if i == nodeIterations-1 {
e.Fail(fmt.Sprintf(
"The SelinuxProfile errorlogger wasn't found in the %s node with the name %s",
"The SelinuxProfile wasn't found in the %s node with the name %s",
missingPolName, policy,
))
} else {
Expand All @@ -358,7 +380,7 @@ func (e *e2e) assertSelinuxPolicyIsRemoved(nodes []string, policy string, nodeIt
if missingPolName != "" {
if i == nodeIterations-1 {
e.Fail(fmt.Sprintf(
"The SelinuxProfile errorlogger was found in the %s node with the name %s",
"The SelinuxProfile was found in the %s node with the name %s",
missingPolName, policy,
))
} else {
Expand Down
2 changes: 1 addition & 1 deletion test/tc_selinux_profilebindings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func (e *e2e) testCaseSelinuxProfileBinding() {
namespace := e.getCurrentContextNamespace(defaultNamespace)

e.logf("Getting selinux profile usage")
selinuxUsage := e.getSELinuxPolicyUsage(selinuxTestProfileName)
selinuxUsage := e.getSELinuxPolicyUsage("selinuxprofile", selinuxTestProfileName)

e.logf("Testing that pod has securityContext")
output := e.kubectl(
Expand Down

0 comments on commit 457592d

Please sign in to comment.