Skip to content

Commit

Permalink
Refactor parameters to support arbitrary types (#10)
Browse files Browse the repository at this point in the history
Instead of a map[string]string, support (almost) arbitrary parameters via a map[string]interface{}.

Use k8s-style methodology to describe the objects: ie, describe them as Go structs, embedding metadata in comments, and parse out the properties from that using the same library k8s uses, and then use code generation for the validation etc.

Also add a new check template (and built-in check) for forbidden API object versions.
  • Loading branch information
viswajithiii authored Oct 22, 2020
1 parent 1b13a45 commit 5e2425e
Show file tree
Hide file tree
Showing 44 changed files with 1,300 additions and 115 deletions.
4 changes: 2 additions & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ jobs:
make lint
- run:
name: Ensure generated docs are up-to-date
name: Ensure generated files are up-to-date
command: |
make generated-docs
make generated-srcs
git diff --exit-code HEAD
test:
Expand Down
9 changes: 8 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,18 @@ lint: golangci-lint staticcheck
## Code generation #
####################

.PHONY: go-generated-srcs
go-generated-srcs: deps
go generate ./...

.PHONY: generated-docs
generated-docs: build
generated-docs: go-generated-srcs build
kube-linter templates list --format markdown > docs/generated/templates.md
kube-linter checks list --format markdown > docs/generated/checks.md

.PHONY: generated-srcs
generated-srcs: go-generated-srcs generated-docs

.PHONY: packr
packr: $(PACKR_BIN)
packr
Expand Down
11 changes: 6 additions & 5 deletions docs/generated/checks.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ The following table enumerates built-in checks:

| Name | Enabled by default | Description | Template | Parameters |
| ---- | ------------------ | ----------- | -------- | ---------- |
| env-var-secret | Yes | Alert on objects using a secret in an environment variable | env-var |- `name`: `.*secret.*` <br />|
| no-read-only-root-fs | Yes | Alert on containers not running with a read-only root filesystem | read-only-root-fs | none |
| privileged-container | Yes | Alert on deployments with containers running in privileged mode | privileged | none |
| required-label-owner | No | Alert on objects without the 'owner' label | required-label |- `key`: `owner` <br />|
| run-as-non-root | Yes | Alert on containers not set to runAsNonRoot | run-as-non-root | none |
| env-var-secret | Yes | Alert on objects using a secret in an environment variable | env-var | `{"name":".*secret.*"}` |
| no-extensions-v1beta | Yes | Alert on objects using deprecated API versions under extensions v1beta | disallowed-api-obj | `{"group":"extensions","version":"v1beta.+"}` |
| no-read-only-root-fs | Yes | Alert on containers not running with a read-only root filesystem | read-only-root-fs | `{}` |
| privileged-container | Yes | Alert on deployments with containers running in privileged mode | privileged | `{}` |
| required-label-owner | No | Alert on objects without the 'owner' label | required-label | `{"key":"owner"}` |
| run-as-non-root | Yes | Alert on containers not set to runAsNonRoot | run-as-non-root | `{}` |
165 changes: 156 additions & 9 deletions docs/generated/templates.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,156 @@
The following table enumerates supported check templates:

| Name | Description | Supported Objects | Parameters |
| ---- | ----------- | ----------------- | ---------- |
| env-var | Flag environment variables that match the provided patterns | DeploymentLike |- `name` (required): A regex for the env var name <br />- `value`: A regex for the env var value <br />|
| privileged | Flag privileged containers | DeploymentLike | none |
| read-only-root-fs | Flag containers without read-only root file systems | DeploymentLike | none |
| required-label | Flag objects not carrying at least one label matching the provided patterns | Any |- `key` (required): A regex for the key of the required label <br />- `value`: A regex for the value of the required label <br />|
| run-as-non-root | Flag containers set to run as a root user | DeploymentLike | none |
This page lists supported check templates.

## Disallowed API Objects

**Key**: `disallowed-api-obj`

**Description**: Flag disallowed API object kinds

**Supported Objects**: Any

**Parameters**:
```
[
{
"name": "group",
"type": "string",
"description": "The disallowed object group.",
"required": false,
"examples": [
"apps"
],
"regexAllowed": true,
"negationAllowed": true
},
{
"name": "version",
"type": "string",
"description": "The disallowed object API version.",
"required": false,
"examples": [
"v1",
"v1beta1"
],
"regexAllowed": true,
"negationAllowed": true
},
{
"name": "kind",
"type": "string",
"description": "The disallowed kind.",
"required": false,
"examples": [
"Deployment",
"DaemonSet"
],
"regexAllowed": true,
"negationAllowed": true
}
]
```

## Environment Variables

**Key**: `env-var`

**Description**: Flag environment variables that match the provided patterns

**Supported Objects**: DeploymentLike

**Parameters**:
```
[
{
"name": "name",
"type": "string",
"description": "The name of the environment variable.",
"required": true,
"regexAllowed": true,
"negationAllowed": true
},
{
"name": "value",
"type": "string",
"description": "The value of the environment variable.",
"required": false,
"regexAllowed": true,
"negationAllowed": true
}
]
```

## Privileged Containers

**Key**: `privileged`

**Description**: Flag privileged containers

**Supported Objects**: DeploymentLike

**Parameters**:
```
[]
```

## Read-only Root Filesystems

**Key**: `read-only-root-fs`

**Description**: Flag containers without read-only root file systems

**Supported Objects**: DeploymentLike

**Parameters**:
```
[]
```

## Required Label

**Key**: `required-label`

**Description**: Flag objects not carrying at least one label matching the provided patterns

**Supported Objects**: Any

**Parameters**:
```
[
{
"name": "key",
"type": "string",
"description": "Key of the required label.",
"required": true,
"regexAllowed": true,
"negationAllowed": true
},
{
"name": "value",
"type": "string",
"description": "Value of the required label.",
"required": false,
"regexAllowed": true,
"negationAllowed": true
}
]
```

## Run as non-root user

**Key**: `run-as-non-root`

**Description**: Flag containers set to run as a root user

**Supported Objects**: DeploymentLike

**Parameters**:
```
[]
```

2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
github.com/ghodss/yaml v1.0.0
github.com/gobuffalo/packr v1.30.1
github.com/golangci/golangci-lint v1.30.0
github.com/mitchellh/mapstructure v1.1.2
github.com/pkg/errors v0.9.1
github.com/spf13/cobra v1.0.0
github.com/stretchr/objx v0.2.0 // indirect
Expand All @@ -18,4 +19,5 @@ require (
k8s.io/api v0.19.1
k8s.io/apimachinery v0.19.1
k8s.io/client-go v0.19.0
k8s.io/gengo v0.0.0-20200728071708-7794989d0000
)
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,7 @@ golang.org/x/tools v0.0.0-20200321224714-0d839f3cf2ed/go.mod h1:Sl4aGygMT6LrqrWc
golang.org/x/tools v0.0.0-20200324003944-a576cf524670/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200414032229-332987a829c3/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200422022333-3d57cf2e726e/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200519015757-0d0afa43d58a/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200625211823-6506e20df31f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
Expand Down Expand Up @@ -765,10 +766,13 @@ k8s.io/apimachinery v0.19.1/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlm
k8s.io/client-go v0.19.0 h1:1+0E0zfWFIWeyRhQYWzimJOyAk2UT7TiARaLNwJCf7k=
k8s.io/client-go v0.19.0/go.mod h1:H9E/VT95blcFQnlyShFgnFT9ZnJOAceiUHM3MlRC+mU=
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20200728071708-7794989d0000 h1:XgICMZutMLbopSVIJJrhUun6Hbuh1NTZBv2sd0lvypU=
k8s.io/gengo v0.0.0-20200728071708-7794989d0000/go.mod h1:aG2eeomYfcUw8sE3fa7YdkjgnGtyY56TjZlaJJ0ZoWo=
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/klog/v2 v2.2.0 h1:XRvcwJozkgZ1UQJmfMGpvRthQHOvihEhYtDfAaxMz/A=
k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o=
k8s.io/utils v0.0.0-20200729134348-d5654de09c73 h1:uJmqzgNWG7XyClnU/mLPBWwfKKF1K8Hf8whTseBgJcg=
k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
mvdan.cc/gofumpt v0.0.0-20200709182408-4fd085cb6d5f h1:gi7cb8HTDZ6q8VqsUpkdoFi3vxwHMneQ6+Q5Ap5hjPE=
mvdan.cc/gofumpt v0.0.0-20200709182408-4fd085cb6d5f/go.mod h1:9VQ397fNXEnF84t90W4r4TRCQK+pg9f8ugVfyj+S26w=
Expand Down
9 changes: 9 additions & 0 deletions internal/builtinchecks/yamls/no-extensions-v1beta.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
name: "no-extensions-v1beta"
description: "Alert on objects using deprecated API versions under extensions v1beta"
scope:
objectKinds:
- Any
template: "disallowed-api-obj"
params:
group: "extensions"
version: "v1beta.+"
10 changes: 5 additions & 5 deletions internal/check/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ package check

// A Check represents a single check. It is serializable.
type Check struct {
Name string `json:"name"`
Description string `json:"description"`
Scope *ObjectKindsDesc `json:"scope"`
Template string `json:"template"`
Params map[string]string `json:"params,omitempty"`
Name string `json:"name"`
Description string `json:"description"`
Scope *ObjectKindsDesc `json:"scope"`
Template string `json:"template"`
Params map[string]interface{} `json:"params,omitempty"`
}
85 changes: 85 additions & 0 deletions internal/check/parameter_desc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package check

import (
"golang.stackrox.io/kube-linter/internal/pointers"
)

// ParameterType represents the expected type of a particular parameter.
type ParameterType string

// This block enumerates all known type names.
// These type names are chosen to be aligned with OpenAPI/JSON schema.
const (
StringType ParameterType = "string"
IntegerType ParameterType = "integer"
BooleanType ParameterType = "boolean"
NumberType ParameterType = "number"
ObjectType ParameterType = "object"
)

// ParameterDesc describes a parameter.
type ParameterDesc struct {
Name string
Type ParameterType
Description string

Examples []string

// SubParameters are the child parameters of the given parameter.
// Only relevant if Type is "object".
SubParameters []ParameterDesc

// Required denotes whether the parameter is required.
Required bool

// NoRegex is set if the parameter does not support regexes.
// Only relevant if Type is "string".
NoRegex bool

// NotNegatable is set if the parameter does not support negation via a leading !.
// OnlyRelevant if Type is "string".
NotNegatable bool

// Fields below are for internal use only.

XXXStructFieldName string
}

// HumanReadableParamDesc is a human-friendly representation of a ParameterDesc.
// It is intended only for API documentation/JSON marshaling, and must NOT be used for
// any business logic.
type HumanReadableParamDesc struct {
Name string `json:"name"`
Type ParameterType `json:"type"`
Description string `json:"description"`
Required bool `json:"required"`
Examples []string `json:"examples,omitempty"`
RegexAllowed *bool `json:"regexAllowed,omitempty"`
NegationAllowed *bool `json:"negationAllowed,omitempty"`
SubParameters []HumanReadableParamDesc `json:"subParameters,omitempty"`
}

// HumanReadableFields returns a human-friendly representation of this ParameterDesc.
func (p *ParameterDesc) HumanReadableFields() HumanReadableParamDesc {
out := HumanReadableParamDesc{
Name: p.Name,
Type: p.Type,
Description: p.Description,
Required: p.Required,
Examples: p.Examples,
}

if p.Type == StringType {
out.RegexAllowed = pointers.Bool(!p.NoRegex)
out.NegationAllowed = pointers.Bool(!p.NotNegatable)
}

if len(p.SubParameters) > 0 {
subParamFields := make([]HumanReadableParamDesc, 0, len(p.SubParameters))
for _, subParam := range p.SubParameters {
subParamFields = append(subParamFields, subParam.HumanReadableFields())
}
out.SubParameters = subParamFields
}
return out
}
19 changes: 9 additions & 10 deletions internal/check/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,22 @@ import (
// object passed in the second argument.
type Func func(lintCtx *lintcontext.LintContext, object lintcontext.Object) []diagnostic.Diagnostic

// A ParameterDesc describes a parameter to a check template.
type ParameterDesc struct {
ParamName string
Required bool
Description string
}

// ObjectKindsDesc describes a list of supported object kinds for a check template.
type ObjectKindsDesc struct {
ObjectKinds []string `json:"objectKinds"`
}

// A Template is a template for a check.
type Template struct {
Name string
// HumanName is a human-friendly name for the template.
// It is to be used ONLY for documentation, and has no
// semantic relevance.
HumanName string
Key string
Description string
SupportedObjectKinds ObjectKindsDesc
Parameters []ParameterDesc
Instantiate func(params map[string]string) (Func, error)

Parameters []ParameterDesc
ParseAndValidateParams func(params map[string]interface{}) (interface{}, error)
Instantiate func(parsedParams interface{}) (Func, error)
}
Loading

0 comments on commit 5e2425e

Please sign in to comment.