Skip to content

Commit

Permalink
Add OpenShift SecurityContextConstraints object linting (#650)
Browse files Browse the repository at this point in the history
Co-authored-by: Cereberus <[email protected]>
  • Loading branch information
mancubus77 and Cereberus authored Nov 2, 2023
1 parent 238ec49 commit 41071a5
Show file tree
Hide file tree
Showing 14 changed files with 400 additions and 1 deletion.
15 changes: 15 additions & 0 deletions docs/generated/checks.md
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,21 @@ key: owner
**Remediation**: Set runAsUser to a non-zero number and runAsNonRoot to true in your pod or container securityContext. Refer to https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ for details.
**Template**: [run-as-non-root](templates.md#run-as-non-root-user)
## scc-deny-privileged-container
**Enabled by default**: No
**Description**: Indicates when allowPrivilegedContainer SecurityContextConstraints set to true
**Remediation**: SecurityContextConstraints has AllowPrivilegedContainer set to "true". Using this option is dangerous, please consider using allowedCapabilities instead. Refer to https://docs.openshift.com/container-platform/4.12/authentication/managing-security-context-constraints.html#scc-settings_configuring-internal-oauth for details.
**Template**: [scc-deny-privileged-container](templates.md#securitycontextconstraints-allowprivilegedcontainer)
**Parameters**:
```yaml
AllowPrivilegedContainer: true
```
## sensitive-host-mounts
**Enabled by default**: Yes
Expand Down
18 changes: 18 additions & 0 deletions docs/generated/templates.md
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,24 @@ KubeLinter supports the following templates:
**Supported Objects**: DeploymentLike


## SecurityContextConstraints allowPrivilegedContainer

**Key**: `scc-deny-privileged-container`

**Description**: Flag SCC with allowPrivilegedContainer set to true

**Supported Objects**: SecurityContextConstraints


**Parameters**:

```yaml
- description: allowPrivilegedContainer value
name: allowPrivilegedContainer
required: false
type: boolean
```

## Service Account

**Key**: `service-account`
Expand Down
15 changes: 15 additions & 0 deletions e2etests/bats-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,21 @@ get_value_from() {
[[ "${count}" == "2" ]]
}

@test "scc-deny-privileged-container" {
tmp="tests/checks/scc-deny-privileged-container.yml"
cmd="${KUBE_LINTER_BIN} lint --include scc-deny-privileged-container --do-not-auto-add-defaults --format json ${tmp}"
run ${cmd}

print_info "${status}" "${output}" "${cmd}" "${tmp}"
[ "$status" -eq 1 ]

message1=$(get_value_from "${lines[0]}" '.Reports[0].Object.K8sObject.GroupVersionKind.Kind + ": " + .Reports[0].Diagnostic.Message')
count=$(get_value_from "${lines[0]}" '.Reports | length')

[[ "${message1}" == "SecurityContextConstraints: SCC has allowPrivilegedContainer set to true" ]]
[[ "${count}" == "1" ]]
}

@test "sensitive-host-mounts" {
tmp="tests/checks/sensitive-host-mounts.yml"
cmd="${KUBE_LINTER_BIN} lint --include sensitive-host-mounts --do-not-auto-add-defaults --format json ${tmp}"
Expand Down
10 changes: 10 additions & 0 deletions pkg/builtinchecks/yamls/scc-deny-privileged-container.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
name: "scc-deny-privileged-container"
description: "Indicates when allowPrivilegedContainer SecurityContextConstraints set to true"
remediation: >-
SecurityContextConstraints has AllowPrivilegedContainer set to "true". Using this option is dangerous, please consider using allowedCapabilities instead. Refer to https://docs.openshift.com/container-platform/4.12/authentication/managing-security-context-constraints.html#scc-settings_configuring-internal-oauth for details.
scope:
objectKinds:
- SecurityContextConstraints
template: "scc-deny-privileged-container"
params:
AllowPrivilegedContainer: true
14 changes: 14 additions & 0 deletions pkg/extract/scc_spec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package extract

import (
ocpSecV1 "github.com/openshift/api/security/v1"
"golang.stackrox.io/kube-linter/pkg/k8sutil"
)

// SCCallowPrivilegedContainer extracts allowPrivilegedContainer from the given object, if available.
func SCCallowPrivilegedContainer(obj k8sutil.Object) (bool, bool) {
if scc, ok := obj.(*ocpSecV1.SecurityContextConstraints); ok {
return scc.AllowPrivilegedContainer, true
}
return false, false
}
23 changes: 23 additions & 0 deletions pkg/lintcontext/mocks/scc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package mocks

import (
"testing"

ocpSecV1 "github.com/openshift/api/security/v1"
"github.com/stretchr/testify/require"
"golang.stackrox.io/kube-linter/pkg/objectkinds"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// AddMockSecurityContextConstraints adds a mock SecurityContextConstraints to LintContext
func (l *MockLintContext) AddMockSecurityContextConstraints(t *testing.T, name string, allowFlag bool) {
require.NotEmpty(t, name)
l.objects[name] = &ocpSecV1.SecurityContextConstraints{
TypeMeta: metaV1.TypeMeta{
Kind: objectkinds.SecurityContextConstraints,
APIVersion: objectkinds.GetSCCAPIVersion(),
},
ObjectMeta: metaV1.ObjectMeta{Name: name},
AllowPrivilegedContainer: allowFlag,
}
}
3 changes: 2 additions & 1 deletion pkg/lintcontext/parse_yaml.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

y "github.com/ghodss/yaml"
ocsAppsV1 "github.com/openshift/api/apps/v1"
ocpSecV1 "github.com/openshift/api/security/v1"
"github.com/pkg/errors"
k8sMonitoring "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
"golang.stackrox.io/kube-linter/pkg/k8sutil"
Expand Down Expand Up @@ -42,7 +43,7 @@ func init() {
clientScheme := scheme.Scheme

// Add OpenShift and Autoscaling schema
schemeBuilder := runtime.NewSchemeBuilder(ocsAppsV1.AddToScheme, autoscalingV2Beta1.AddToScheme, k8sMonitoring.AddToScheme)
schemeBuilder := runtime.NewSchemeBuilder(ocsAppsV1.AddToScheme, autoscalingV2Beta1.AddToScheme, k8sMonitoring.AddToScheme, ocpSecV1.AddToScheme)
if err := schemeBuilder.AddToScheme(clientScheme); err != nil {
panic(fmt.Sprintf("Can not add OpenShift schema %v", err))
}
Expand Down
26 changes: 26 additions & 0 deletions pkg/objectkinds/securitycontext.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package objectkinds

import (
ocpSecV1 "github.com/openshift/api/security/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
)

const (
// Service represents Kubernetes Service objects.
SecurityContextConstraints = "SecurityContextConstraints"
)

var (
sccGVK = ocpSecV1.SchemeGroupVersion.WithKind("SecurityContextConstraints")
)

func init() {
RegisterObjectKind(SecurityContextConstraints, MatcherFunc(func(gvk schema.GroupVersionKind) bool {
return gvk == sccGVK
}))
}

// GetSCCAPIVersion returns SCC's apiversion
func GetSCCAPIVersion() string {
return sccGVK.GroupVersion().String()
}
1 change: 1 addition & 0 deletions pkg/templates/all/all.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import (
_ "golang.stackrox.io/kube-linter/pkg/templates/requiredannotation"
_ "golang.stackrox.io/kube-linter/pkg/templates/requiredlabel"
_ "golang.stackrox.io/kube-linter/pkg/templates/runasnonroot"
_ "golang.stackrox.io/kube-linter/pkg/templates/sccdenypriv"
_ "golang.stackrox.io/kube-linter/pkg/templates/serviceaccount"
_ "golang.stackrox.io/kube-linter/pkg/templates/servicetype"
_ "golang.stackrox.io/kube-linter/pkg/templates/sysctl"
Expand Down
69 changes: 69 additions & 0 deletions pkg/templates/sccdenypriv/internal/params/gen-params.go

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

8 changes: 8 additions & 0 deletions pkg/templates/sccdenypriv/internal/params/params.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package params

// Params represents the params accepted by this template.
type Params struct {

// allowPrivilegedContainer value
AllowPrivilegedContainer bool
}
42 changes: 42 additions & 0 deletions pkg/templates/sccdenypriv/template.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package sccdenypriv

import (
"fmt"

"golang.stackrox.io/kube-linter/pkg/check"
"golang.stackrox.io/kube-linter/pkg/config"
"golang.stackrox.io/kube-linter/pkg/diagnostic"
"golang.stackrox.io/kube-linter/pkg/extract"
"golang.stackrox.io/kube-linter/pkg/lintcontext"
"golang.stackrox.io/kube-linter/pkg/objectkinds"
"golang.stackrox.io/kube-linter/pkg/templates"
"golang.stackrox.io/kube-linter/pkg/templates/sccdenypriv/internal/params"
)

const (
templateKey = "scc-deny-privileged-container"
)

func init() {
templates.Register(check.Template{
HumanName: "SecurityContextConstraints allowPrivilegedContainer",
Key: templateKey,
Description: "Flag SCC with allowPrivilegedContainer set to true",
SupportedObjectKinds: config.ObjectKindsDesc{
ObjectKinds: []string{objectkinds.SecurityContextConstraints},
},
Parameters: params.ParamDescs,
ParseAndValidateParams: params.ParseAndValidate,
Instantiate: params.WrapInstantiateFunc(func(p params.Params) (check.Func, error) {
return func(_ lintcontext.LintContext, object lintcontext.Object) []diagnostic.Diagnostic {
state, found := extract.SCCallowPrivilegedContainer(object.K8sObject)
if found && state == p.AllowPrivilegedContainer {
return []diagnostic.Diagnostic{
{Message: fmt.Sprintf("SCC has allowPrivilegedContainer set to %v", state)},
}
}
return nil
}, nil
}),
})
}
70 changes: 70 additions & 0 deletions pkg/templates/sccdenypriv/template_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package sccdenypriv

import (
"testing"

"github.com/stretchr/testify/suite"
"golang.stackrox.io/kube-linter/pkg/diagnostic"
"golang.stackrox.io/kube-linter/pkg/lintcontext/mocks"
"golang.stackrox.io/kube-linter/pkg/templates"
"golang.stackrox.io/kube-linter/pkg/templates/sccdenypriv/internal/params"
)

func TestSCCPriv(t *testing.T) {
suite.Run(t, new(SCCPrivTestSuite))
}

type SCCPrivTestSuite struct {
templates.TemplateTestSuite

ctx *mocks.MockLintContext
}

func (s *SCCPrivTestSuite) SetupTest() {
s.Init(templateKey)
s.ctx = mocks.NewMockContext()
}

func (s *SCCPrivTestSuite) addSCCWithPriv(name string, allowFlag bool) {
s.ctx.AddMockSecurityContextConstraints(s.T(), name, allowFlag)
}

func (s *SCCPrivTestSuite) TestPrivFalse() {
const acceptableScc = "scc-priv-false"

s.addSCCWithPriv(acceptableScc, false)

s.Validate(s.ctx, []templates.TestCase{
{
Param: params.Params{
AllowPrivilegedContainer: true,
},
Diagnostics: map[string][]diagnostic.Diagnostic{
acceptableScc: nil,
},
ExpectInstantiationError: false,
},
})
}

func (s *SCCPrivTestSuite) TestPrivTrue() {
const (
unacceptableScc = "scc-priv-true"
)

s.addSCCWithPriv(unacceptableScc, true)

s.Validate(s.ctx, []templates.TestCase{
{
Param: params.Params{
AllowPrivilegedContainer: true,
},
Diagnostics: map[string][]diagnostic.Diagnostic{
unacceptableScc: {
{Message: "SCC has allowPrivilegedContainer set to true"},
},
},
ExpectInstantiationError: false,
},
})
}
Loading

0 comments on commit 41071a5

Please sign in to comment.