Skip to content
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
16 changes: 16 additions & 0 deletions docs/generated/checks.md
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,22 @@ key: owner
```yaml
AllowPrivilegedContainer: true
```
## schema-validation

**Enabled by default**: No

**Description**: Validate Kubernetes resources against their schemas using kubeconform

**Remediation**: Fix the resource to conform to the Kubernetes API schema

**Template**: [kubeconform](templates.md#kubeconform)

**Parameters**:

```yaml
ignoreMissingSchemas: true
strict: true
```
## sensitive-host-mounts

**Enabled by default**: Yes
Expand Down
18 changes: 18 additions & 0 deletions e2etests/bats-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,24 @@ get_value_from() {
[[ "${message2}" =~ "Pod: resource is not valid:" ]]
}

@test "builtin-schema-validation" {
tmp="tests/checks/kubeconform.yml"
cmd="${KUBE_LINTER_BIN} lint --config e2etests/testdata/schema-validation-config.yaml --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')
message2=$(get_value_from "${lines[0]}" '.Reports[1].Object.K8sObject.GroupVersionKind.Kind + ": " + .Reports[1].Diagnostic.Message')
count=$(get_value_from "${lines[0]}" '.Reports | length')

# Should find 2 validation errors using builtin schema-validation check
[[ "${count}" == "2" ]]
[[ "${message1}" =~ "DaemonSet: resource is not valid:" ]]
[[ "${message2}" =~ "Pod: resource is not valid:" ]]
}

@test "template-check-installed-bash-version" {
run "bash --version"
[[ "${BASH_VERSION:0:1}" -ge '4' ]] || false
Expand Down
4 changes: 4 additions & 0 deletions e2etests/testdata/schema-validation-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
checks:
addAllBuiltIn: false
include:
- "schema-validation"
10 changes: 10 additions & 0 deletions pkg/builtinchecks/yamls/schema-validation.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
name: "schema-validation"
description: "Validate Kubernetes resources against their schemas using kubeconform"
remediation: "Fix the resource to conform to the Kubernetes API schema"
scope:
objectKinds:
- Any
template: "kubeconform"
params:
strict: true
ignoreMissingSchemas: true
1 change: 1 addition & 0 deletions pkg/command/lint/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ func Command() *cobra.Command {
c.Flags().BoolVarP(&verbose, "verbose", "v", false, "Enable verbose logging")
c.Flags().Var(format, "format", format.Usage())
c.Flags().BoolVarP(&errorOnInvalidResource, "fail-on-invalid-resource", "", false, "Error out when we have an invalid resource")
_ = c.Flags().MarkDeprecated("fail-on-invalid-resource", "Use 'schema-validation' builtin check or kubeconform template for better schema validation.")

config.AddFlags(c, v)
return c
Expand Down
21 changes: 2 additions & 19 deletions pkg/command/lint/testdata/invalid-pod-resources.yaml
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a new file ? I find the name misleading (hints at pod where a different kind is specified).

Original file line number Diff line number Diff line change
@@ -1,24 +1,7 @@
apiVersion: v1
kind: Pod
kind: InvalidKind
metadata:
creationTimestamp: null
name: foo-pod
namespace: foo
spec:
containers:
- image: busybox
name: invalid
command:
- "sleep"
args:
- "infinity"
resources:
limits:
cpu: 25m
memory: 1GB
requests:
cpu: 25m
memory: 1GB
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
invalidField: [this is invalid YAML that should fail to parse
14 changes: 2 additions & 12 deletions pkg/command/lint/testdata/invalid-pvc-resources.yaml
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a new file ?

Original file line number Diff line number Diff line change
@@ -1,12 +1,2 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: foo-pvc
namespace: foo
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 250GB
storageClassName: thin-disk
this is malformed YAML that should fail to parse: {
invalid: unclosed bracket
6 changes: 6 additions & 0 deletions pkg/lintcontext/create_contexts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ func TestCreateContextsWithIgnorePaths(t *testing.T) {
"../../.pre-commit-hooks*",
"../../dist/**/*",
"../../pkg/**/*",
"../../demo/**",
"../../stackrox-kube-linter-bug-example/**",
"../../tests/**/*",
"../../cmd/**/*",
"../../docs/**/*",
"../../internal/**/*",
"/**/*/checks/**/*",
"/**/*/test_helper/**/*",
"/**/*/testdata/**/*",
Expand Down
14 changes: 13 additions & 1 deletion pkg/lintcontext/parse_yaml.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ import (
"helm.sh/helm/v3/pkg/engine"
autoscalingV2Beta1 "k8s.io/api/autoscaling/v2beta1"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
runtimeYaml "k8s.io/apimachinery/pkg/runtime/serializer/yaml"
"k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/client-go/kubernetes/scheme"
y "sigs.k8s.io/yaml"
Expand Down Expand Up @@ -58,7 +60,17 @@ func parseObjects(data []byte, d runtime.Decoder) ([]k8sutil.Object, error) {
}
obj, _, err := d.Decode(data, nil, nil)
if err != nil {
return nil, fmt.Errorf("failed to decode: %w", err)
// this is for backward compatibility, should be replaced with kubeconform
if strings.Contains(err.Error(), "json: cannot unmarshal") {
return nil, fmt.Errorf("failed to decode: %w", err)
}
// fallback to unstructured as schema validation will be performed by kubeconform check
dec := runtimeYaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme)
var unstructuredErr error
obj, _, unstructuredErr = dec.Decode(data, nil, obj)
if unstructuredErr != nil {
return nil, fmt.Errorf("failed to decode: %w: %w", err, unstructuredErr)
}
}
if list, ok := obj.(*v1.List); ok {
objs := make([]k8sutil.Object, 0, len(list.Items))
Expand Down
Loading
Loading