Skip to content

Proposal: fine-grained policy-excludesΒ #591

@moredhel

Description

@moredhel

This is a proposal for implementing fine-grained excludes.

This proposal adds a new excludes type to the conftest tool which allows for more fine-grained exclusion of policies. I have also attached a sample implementation (linked here) that can already be played with & serve as a reference.

Issue

Currently conftest is limited to only being able to 'disable' a policy rule over a whole file. This means that it's not possible to have a policy be applied to a subset of elements within a file, it's all or nothing.

This is naturally an issue, especially for projects which are wanting to adopt conftest incrementally into an already existing project. In this case it is difficult to apply a fine-grained policy enforcement which blocks newly added policy-violating definitions into an already existing file.

While there are workarounds, it is difficult to use them and requires modifying the deny rules directly (meaning they won't show up as being explicit exceptions).

Proposal

The linked Proposal includes an extension to conftest with a new keyword exclude. This can be used to attach specific exclusions to a policy. Below is a quick sample:

package main

deny_root[result] {
	input.kind == "Deployment"
	c = input.spec.template.spec.containers[_]
	not c.securityContext.runAsNonRoot

	# "msg" is used to set the message that conftest will output
        # all other keys are used to match against the exclusion
        #
        # "result" can still be a plain string for backwards compatibility
	result := {
		"container": c.name,
		"deployment": input.metadata.name,
		"msg": sprintf("container %s in deployment %s doesn't set runAsNonRoot", [c.name, input.metadata.name]),
	}
}

# here we are adding specific exceptions.
root_exceptions = [{"deployment": "mydep", "containers": ["host-agent"]}]

# The exception we want to be able to express is "mydep can run container 'host-agent' as root", but no other containers (even in the same pod) are allowed to run as root.
exclude_root[attrs] {
	deployment := input.metadata.name
	container := input.spec.template.spec.containers[_].name
	exception := root_exceptions[_]

	deployment == exception.deployment
	container == exception.containers[_]

        here we can return a list of attributes which we would like to match & exclude on.
	attrs = [{"container": container, "deployment": deployment}]
}

First off, the current behaviour is preserved, so no policies will be affected by this change.

There are a few changes to how we need to write our rego.

  1. When returning from the deny/warn policy, an object can be returned instead (as specified in the above example)
  2. The exclusion & the deny rules must have a matching suffix (ie. deny_root & exclude_root).
  3. the return of the exclude is matched against the attributes of the deny rule (rather than on the name of the rule)

With these changes, we are able to write more flexible exclusion rules with as much flexibility in our exclusion logic as we would like.

Actions

  • Are there any blocking issues that should be addressed? If not, then I would appreciate an ok & I will proceed with documentation & flesh out the testing in preparation for merging.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions