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

security requirements of the container-native operators #1967

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
72 changes: 68 additions & 4 deletions CATALOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Depending on the workload type, not all tests are required to pass to satisfy be

## Test cases summary

### Total test cases: 109
### Total test cases: 113

### Total suites: 10

Expand All @@ -19,7 +19,7 @@ Depending on the workload type, not all tests are required to pass to satisfy be
|manageability|2|
|networking|11|
|observability|4|
|operator|7|
|operator|11|
|performance|6|
|platform-alteration|13|
|preflight|17|
Expand All @@ -36,11 +36,11 @@ Depending on the workload type, not all tests are required to pass to satisfy be
|---|---|
|7|1|

### Non-Telco specific tests only: 62
### Non-Telco specific tests only: 66

|Mandatory|Optional|
|---|---|
|42|20|
|46|20|

### Telco specific tests only: 27

Expand Down Expand Up @@ -1122,6 +1122,22 @@ Tags|telco,observability

### operator

#### operator-automount-tokens

Property|Description
---|---
Unique ID|operator-automount-tokens
Description|Tests that check the pods created by the operator ensure that the automount service account token is disabled.
Suggested Remediation|Ensure that the pods created by the operator have the automount service account token disabled.
Best Practice Reference|https://test-network-function.github.io/cnf-best-practices-guide/#cnf-best-practices-cnf-operator-requirements
Exception Process|No exceptions
Tags|common,operator
|**Scenario**|**Optional/Mandatory**|
|Extended|Mandatory|
|Far-Edge|Mandatory|
|Non-Telco|Mandatory|
|Telco|Mandatory|

#### operator-crd-openapi-schema

Property|Description
Expand Down Expand Up @@ -1202,6 +1218,54 @@ Tags|common,operator
|Non-Telco|Mandatory|
|Telco|Mandatory|

#### operator-read-only-file-system

Property|Description
---|---
Unique ID|operator-read-only-file-system
Description|Tests that check the pods created by the operator ensure that the read-only root filesystem setting is enabled.
Suggested Remediation|Ensure that the pods created by the operator have the read-only root filesystem setting enabled.
Best Practice Reference|https://test-network-function.github.io/cnf-best-practices-guide/#cnf-best-practices-cnf-operator-requirements
Exception Process|No exceptions
Tags|common,operator
|**Scenario**|**Optional/Mandatory**|
|Extended|Mandatory|
|Far-Edge|Mandatory|
|Non-Telco|Mandatory|
|Telco|Mandatory|

#### operator-run-as-non-root

Property|Description
---|---
Unique ID|operator-run-as-non-root
Description|Tests that checks the pods created by the operator is run as non root.
Suggested Remediation|Ensure that the pods created by the operator are run as non-root.
Best Practice Reference|https://test-network-function.github.io/cnf-best-practices-guide/#cnf-best-practices-cnf-operator-requirements
Exception Process|No exceptions
Tags|common,operator
|**Scenario**|**Optional/Mandatory**|
|Extended|Mandatory|
|Far-Edge|Mandatory|
|Non-Telco|Mandatory|
|Telco|Mandatory|

#### operator-run-as-user-id

Property|Description
---|---
Unique ID|operator-run-as-user-id
Description|Tests that checks the user id of the pods created by the operator is not 0
Suggested Remediation|Ensure that the user ID of the pods created by the operator is not 0.
Best Practice Reference|https://test-network-function.github.io/cnf-best-practices-guide/#cnf-best-practices-cnf-operator-requirements
Exception Process|No exceptions
Tags|common,operator
|**Scenario**|**Optional/Mandatory**|
|Extended|Mandatory|
|Far-Edge|Mandatory|
|Non-Telco|Mandatory|
|Telco|Mandatory|

#### operator-semantic-versioning

Property|Description
Expand Down
2 changes: 1 addition & 1 deletion cnf-certification-test/accesscontrol/suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ import (

"github.com/operator-framework/api/pkg/operators/v1alpha1"
"github.com/test-network-function/cnf-certification-test/cnf-certification-test/accesscontrol/namespace"
"github.com/test-network-function/cnf-certification-test/cnf-certification-test/accesscontrol/rbac"
"github.com/test-network-function/cnf-certification-test/cnf-certification-test/accesscontrol/resources"
"github.com/test-network-function/cnf-certification-test/cnf-certification-test/accesscontrol/securitycontextcontainer"
"github.com/test-network-function/cnf-certification-test/cnf-certification-test/common"
"github.com/test-network-function/cnf-certification-test/cnf-certification-test/common/rbac"
"github.com/test-network-function/cnf-certification-test/cnf-certification-test/identifiers"
"github.com/test-network-function/cnf-certification-test/cnf-certification-test/networking/netutil"
"github.com/test-network-function/cnf-certification-test/cnf-certification-test/networking/services"
Expand Down
4 changes: 4 additions & 0 deletions cnf-certification-test/identifiers/doclinks.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ const (
TestOperatorCrdSchemaIdentifierDocLink = DocOperatorRequirement
TestOperatorCrdVersioningIdentifierDocLink = DocOperatorRequirement
TestOperatorSingleCrdOwnerIdentifierDocLink = DocOperatorRequirement
TestOperatorRunAsUserIDDocLink = DocOperatorRequirement
TestOperatorRunAsNonRootDocLink = DocOperatorRequirement
TestOperatorAutomountTokensDocLink = DocOperatorRequirement
TestOperatorReadOnlyFilesystemDocLink = DocOperatorRequirement

// Observability Test Suite
TestLoggingIdentifierDocLink = "https://test-network-function.github.io/cnf-best-practices-guide/#cnf-best-practices-logging"
Expand Down
68 changes: 68 additions & 0 deletions cnf-certification-test/identifiers/identifiers.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ var (
TestHelmIsCertifiedIdentifier claim.Identifier
TestOperatorIsInstalledViaOLMIdentifier claim.Identifier
TestOperatorHasSemanticVersioningIdentifier claim.Identifier
TestOperatorReadOnlyFilesystem claim.Identifier
TestOperatorAutomountTokens claim.Identifier
TestOperatorRunAsNonRoot claim.Identifier
TestOperatorRunAsUserID claim.Identifier
TestOperatorCrdVersioningIdentifier claim.Identifier
TestOperatorCrdSchemaIdentifier claim.Identifier
TestOperatorSingleCrdOwnerIdentifier claim.Identifier
Expand Down Expand Up @@ -930,6 +934,70 @@ that Node's kernel may not have the same hacks.'`,
},
TagCommon)

TestOperatorRunAsUserID = AddCatalogEntry(
"run-as-user-id",
common.OperatorTestKey,
`Tests that checks the user id of the pods created by the operator is not 0`,
OperatorRunAsUserID,
NoExceptions,
TestOperatorRunAsUserIDDocLink,
true,
map[string]string{
FarEdge: Mandatory,
Telco: Mandatory,
NonTelco: Mandatory,
Extended: Mandatory,
},
TagCommon)

TestOperatorRunAsNonRoot = AddCatalogEntry(
"run-as-non-root",
common.OperatorTestKey,
`Tests that checks the pods created by the operator is run as non root.`,
OperatorRunAsNonRoot,
NoExceptions,
TestOperatorRunAsNonRootDocLink,
true,
map[string]string{
FarEdge: Mandatory,
Telco: Mandatory,
NonTelco: Mandatory,
Extended: Mandatory,
},
TagCommon)

TestOperatorAutomountTokens = AddCatalogEntry(
"automount-tokens",
common.OperatorTestKey,
`Tests that check the pods created by the operator ensure that the automount service account token is disabled.`,
OperatorAutomountTokens,
NoExceptions,
TestOperatorAutomountTokensDocLink,
true,
map[string]string{
FarEdge: Mandatory,
Telco: Mandatory,
NonTelco: Mandatory,
Extended: Mandatory,
},
TagCommon)

TestOperatorReadOnlyFilesystem = AddCatalogEntry(
"read-only-file-system",
common.OperatorTestKey,
`Tests that check the pods created by the operator ensure that the read-only root filesystem setting is enabled.`,
OperatorReadOnlyFilesystem,
NoExceptions,
TestOperatorReadOnlyFilesystemDocLink,
true,
map[string]string{
FarEdge: Mandatory,
Telco: Mandatory,
NonTelco: Mandatory,
Extended: Mandatory,
},
TagCommon)

TestOperatorCrdVersioningIdentifier = AddCatalogEntry(
"crd-versioning",
common.OperatorTestKey,
Expand Down
8 changes: 8 additions & 0 deletions cnf-certification-test/identifiers/remediation.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@ const (

OperatorCrdSchemaIdentifierRemediation = `Ensure that the Operator CRD is defined with OpenAPI spec.`

OperatorRunAsUserID = `Ensure that the user ID of the pods created by the operator is not 0.`

OperatorRunAsNonRoot = `Ensure that the pods created by the operator are run as non-root.`

OperatorAutomountTokens = `Ensure that the pods created by the operator have the automount service account token disabled.`

OperatorReadOnlyFilesystem = `Ensure that the pods created by the operator have the read-only root filesystem setting enabled.`

OperatorCrdVersioningRemediation = `Ensure that the Operator CRD has a valid version.`

OperatorSingleCrdOwnerRemediation = `Ensure that a CRD is owned by only one Operator`
Expand Down
127 changes: 127 additions & 0 deletions cnf-certification-test/operator/suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ import (
"strings"

"github.com/test-network-function/cnf-certification-test/cnf-certification-test/common"
"github.com/test-network-function/cnf-certification-test/cnf-certification-test/common/rbac"
"github.com/test-network-function/cnf-certification-test/cnf-certification-test/identifiers"
"github.com/test-network-function/cnf-certification-test/cnf-certification-test/operator/phasecheck"
"github.com/test-network-function/cnf-certification-test/internal/clientsholder"

"github.com/test-network-function/cnf-certification-test/internal/log"
"github.com/test-network-function/cnf-certification-test/pkg/checksdb"
Expand All @@ -39,6 +41,7 @@ var (
}
)

//nolint:funlen
func LoadChecks() {
log.Debug("Loading %s suite checks", common.OperatorTestKey)

Expand Down Expand Up @@ -93,6 +96,33 @@ func LoadChecks() {
testOperatorSingleCrdOwner(c, &env)
return nil
}))

checksGroup.Add(checksdb.NewCheck(identifiers.GetTestIDAndLabels(identifiers.TestOperatorRunAsUserID)).
WithSkipCheckFn(testhelper.GetNoOperatorsSkipFn(&env)).
WithCheckFn(func(c *checksdb.Check) error {
testOperatorPodsRunAsUserID(c, &env)
return nil
}))
checksGroup.Add(checksdb.NewCheck(identifiers.GetTestIDAndLabels(identifiers.TestOperatorRunAsNonRoot)).
WithSkipCheckFn(testhelper.GetNoOperatorsSkipFn(&env)).
WithCheckFn(func(c *checksdb.Check) error {
testOperatorPodsRunAsNonRoot(c, &env)
return nil
}))

checksGroup.Add(checksdb.NewCheck(identifiers.GetTestIDAndLabels(identifiers.TestOperatorAutomountTokens)).
WithSkipCheckFn(testhelper.GetNoOperatorsSkipFn(&env)).
WithCheckFn(func(c *checksdb.Check) error {
testOperatorPodsAutomountTokens(c, &env)
return nil
}))

checksGroup.Add(checksdb.NewCheck(identifiers.GetTestIDAndLabels(identifiers.TestOperatorReadOnlyFilesystem)).
WithSkipCheckFn(testhelper.GetNoOperatorsSkipFn(&env)).
WithCheckFn(func(c *checksdb.Check) error {
testOperatorContainersReadOnlyFilesystem(c, &env)
return nil
}))
}

// This function check if the Operator CRD version follows K8s versioning
Expand Down Expand Up @@ -317,3 +347,100 @@ func testOperatorSingleCrdOwner(check *checksdb.Check, env *provider.TestEnviron

check.SetResult(compliantObjects, nonCompliantObjects)
}

func testOperatorPodsRunAsUserID(check *checksdb.Check, env *provider.TestEnvironment) {
var compliantObjects []*testhelper.ReportObject
var nonCompliantObjects []*testhelper.ReportObject

for csv, pods := range env.CSVToPodListMap {
check.LogInfo("Csv: %q", csv)
for _, pod := range pods {
check.LogInfo("Testing Pod %q", pod)
if pod.IsRunAsUserID(0) {
check.LogError("Pod %q UserID of the pods created by the operator is 0", pod.Name)
nonCompliantObjects = append(nonCompliantObjects, testhelper.NewPodReportObject(pod.Namespace, pod.Name, "Pod has been found with UserID is 0", false))
} else {
check.LogInfo("Pod %q UserID of the pods created by the operator is not 0", pod.Name)
compliantObjects = append(compliantObjects, testhelper.NewPodReportObject(pod.Namespace, pod.Name, "Pod has been found with UserID is not 0", true))
}
}
}
check.SetResult(compliantObjects, nonCompliantObjects)
}

func testOperatorPodsRunAsNonRoot(check *checksdb.Check, env *provider.TestEnvironment) {
var compliantObjects []*testhelper.ReportObject
var nonCompliantObjects []*testhelper.ReportObject

for csv, pods := range env.CSVToPodListMap {
check.LogInfo("Csv: %q", csv)
for _, pod := range pods {
// We are looking through both the containers and the pods separately to make compliant and non-compliant objects.

for _, c := range pod.Containers {
if c.IsContainerRunAsNonRoot() {
check.LogInfo("Container %q created by the operator is run as not root", c.Name)
compliantObjects = append(compliantObjects, testhelper.NewPodReportObject(c.Namespace, c.Name, "Container has been found is run as not root", true))
} else {
check.LogError("Pod %q created by the operator is run as root", pod.Name)
nonCompliantObjects = append(nonCompliantObjects, testhelper.NewPodReportObject(pod.Namespace, pod.Name, "Container has been found is run as root", false))
}
}

if pod.IsRunAsNonRoot() {
check.LogInfo("Pod %q created by the operator is run as not root", pod.Name)
compliantObjects = append(compliantObjects, testhelper.NewPodReportObject(pod.Namespace, pod.Name, "Pod has been found is run as not root", true))
} else {
check.LogError("Pod %q created by the operator is run as root", pod.Name)
nonCompliantObjects = append(nonCompliantObjects, testhelper.NewPodReportObject(pod.Namespace, pod.Name, "Pod has been found is run as root", false))
}
}
}
check.SetResult(compliantObjects, nonCompliantObjects)
}

func testOperatorPodsAutomountTokens(check *checksdb.Check, env *provider.TestEnvironment) {
var compliantObjects []*testhelper.ReportObject
var nonCompliantObjects []*testhelper.ReportObject

for csv, pods := range env.CSVToPodListMap {
check.LogInfo("Csv: %q", csv)
for _, pod := range pods {
check.LogInfo("Testing Pod %q", pod)
// Evaluate the pod's automount service tokens and any attached service accounts
client := clientsholder.GetClientsHolder()
podPassed, newMsg := rbac.EvaluateAutomountTokens(client.K8sClient.CoreV1(), pod.Pod)
if !podPassed {
check.LogInfo("Pod %q have automount service tokens set to false", pod)
compliantObjects = append(compliantObjects, testhelper.NewPodReportObject(pod.Namespace, pod.Name, "Pod %q created by the operator that the automount service account token set to false", true))
} else {
check.LogError(newMsg)
nonCompliantObjects = append(nonCompliantObjects, testhelper.NewPodReportObject(pod.Namespace, pod.Name, newMsg, false))
}
}
}
check.SetResult(compliantObjects, nonCompliantObjects)
}

func testOperatorContainersReadOnlyFilesystem(check *checksdb.Check, env *provider.TestEnvironment) {
var compliantObjects []*testhelper.ReportObject
var nonCompliantObjects []*testhelper.ReportObject

for csv, pods := range env.CSVToPodListMap {
check.LogInfo("Csv: %q", csv)
for _, pod := range pods {
check.LogInfo("Testing Pod %q", pod)
for _, cut := range pod.Containers {
check.LogInfo("Testing Container %q", cut.Name)
if cut.IsReadOnlyRootFilesystem(check.GetLogger()) {
check.LogInfo("Pod %q container %q created by the operator is read only root file system.", pod.Name, cut.Name)
compliantObjects = append(compliantObjects, testhelper.NewPodReportObject(pod.Namespace, pod.Name, "Pod has been found read only root file system", true))
} else {
check.LogError("Pod %q container %q created by the operator is read not only root file system.", pod.Name, cut.Name)
nonCompliantObjects = append(nonCompliantObjects, testhelper.NewPodReportObject(pod.Namespace, pod.Name, "Pod has been found read not only root file system", false))
}
}
}
check.SetResult(compliantObjects, nonCompliantObjects)
}
}
Loading
Loading