OPA policy-based testing of Helm charts

OPA policy-based testing of Helm charts

·

6 min read

By Iheanyi Onwubiko

Enhancing Helm chart security and policy compliance with OPA

Managing Kubernetes applications correctly is as crucial as their development. Helm charts are a pivotal tool for deploying and managing these applications efficiently.

However, ensuring that these deployments are secure and adhere to best practices requires robust oversight. This is where the Open Policy Agent (OPA) plays a vital role by enforcing a set of standardised rules.

Leveraging Conftest for OPA policy enforcement

Conftest is a tool that supports DevOps teams by testing structured configuration data against predefined policies, ensuring configurations are correct prior to deployment. These policies, written in the Rego language from OPA, consist of rules that define the necessary logic to enforce desired behaviours. Together, Conftest and OPA create a powerful ecosystem for automating and managing policy enforcement effectively.

The benefits of integrating OPA with Helm

Integrating OPA with Helm chart development enhances Kubernetes management by automating the validation of the Helm templates. This approach not only addresses the challenges of manual checks, but also mitigates potential inconsistencies as Kubernetes environments scale. OPA's policy-as-code model assures uniform policy application across clusters.

Preparing your environment

To integrate Open Policy Agent (OPA) with Helm, you will need several tools and a basic understanding of key technologies:

  1. Helm: Helm installation guide.

  2. Conftest: Conftest installation guide.

  3. Local Kubernetes cluster: Optionally, use Minikube or Kind for realistic testing and validation of Helm charts.

Additionally, familiarity with writing OPA policies in Rego, understanding Helm chart structures and Kubernetes resource management is important. In essence, what we are trying to achieve with this is to validate the final Kubernetes manifests; thus knowledge of the chart will help to fix issues.

For practical demonstrations, I'll reference the helm-OPA-policy-testing-templates repository on GitHub, which showcases example policies to maintain standards and recommended practices in Kubernetes.

Repository structure

  • mars/: Contains the Helm chart that will be tested — “mars” is just a random name for the chart to be tested.

    • templates/: Includes Kubernetes resource templates such as ConfigMaps and Ingresses.

    • chart.yaml: Details the chart version and metadata.

    • values.yaml: Holds default configuration values.

  • policy/: Includes OPA policies structured by enforcement type:

    • deny/: Policies that block deployments under specific conditions.

    • violation/: Policies that identify non-adherence to best practices.

    • warn/: Policies that issue warnings for potential issues.

    • helpers/: Common functions used across various policies.

Leveraging helper functions in Conftest

Helper functions in Conftest streamline policy management by consolidating common logic into reusable components. It simplifies policy creation, enhancing clarity and manageability. The helper functions we will use in the upcoming example scenario are in the kubernetes.rego file in the repository. For detailed examples and guidelines on using these functions, please see the helper functions documentation.

Scenario: Enforcing key policies with Conftest

In this example scenario, we'll use Conftest to enforce two key policies for developing our Helm charts. Firstly, we prohibit the use of the 'latest' image tag for all containers to prevent unpredictability in deployments. Secondly, we mandate that all Kubernetes deployments include specific labels to improve management, visibility and recommended best practices.

Prohibiting 'latest' tag policy*:*

package main

import data.kubernetes

name := input.metadata.name

violation[msg] {
    kubernetes.containers[container]
    [image_name, "latest"] = kubernetes.split_image(container.image)
    msg = kubernetes.format(sprintf("%s in the %s %s has an image, %s, using the latest tag", [container.name, kubernetes.kind, image_name, kubernetes.name]))
}

This policy achieves the following:

  • kubernetes.containers[container]: Iterates over each container.

  • [imagename, "latest"] = kubernetes.splitimage(container.image): Splits the container image string to check if it uses the "latest" tag.

  • msg = kubernetes.format(sprintf(...)): Generates a standardised error message if a container uses the "latest" tag, detailing the container's name and the type of Kubernetes object it belongs to.

Enforce recommended labels policy:

package main

import data.kubernetes

name := input.metadata.name

deny[msg] {
    kubernetes.is_deployment
    not kubernetes.required_deployment_labels
    msg = sprintf("%s must include Kubernetes recommended labels: https://kubernetes.io/docs/concepts/overview/working-with-objects/common-labels/#labels", [name])
}

This policy does the following:

  • kubernetes.is_deployment: Confirms the resource is a Kubernetes deployment.

  • not kubernetes.requireddeploymentlabels: Condition checks whether the deployment is missing the necessary labels. The requireddeploymentlabels function, which performs this verification, is defined in the helper functions file.

  • msg = sprintf(...): Generates an error message for non-compliant deployments, directing users to a URL with labelling best practices.

How to apply these policies:

Save the policies in a .rego file within a "policy" folder. Conftest defaults to searching for rules in a directory named "policy." To specify a different location, use the --policy or -p flag when running tests.

Run the following command from the root of your repository to test Helm charts against the defined OPA policies:

helm template mars | conftest test -p policy/ -

This command sequence ensures your Helm chart configurations comply with OPA policies before deployment:

  • helm template mars: Generates Kubernetes YAML files from the mars Helm chart.

  • | (pipe): Passes the YAML output to the next command.

  • conftest test: Uses Conftest to evaluate the YAML files against the policies in the policy/ directory.

  • -p policy/: Specifies the directory where the policy files are located.

  • -: Indicates that Conftest should read input from the standard input (stdin), provided by the pipe.

You should see an output similar to the screenshot below, indicating policy enforcement failures:

FAIL - - main - release-name-mars must include Kubernetes recommended labels: https://kubernetes.io/docs/concepts/overview/working-with-objects/common-labels/#labels
FAIL - - main - mars in the Deployment nginx has an image, release-name-mars, using the latest tag

This indicates that our Helm charts do not comply with our policies. For example, as highlighted in the _helpers.tpl file, there are missing deployment labels:

Helm charts missing required labels

'Fig 1: Missing required labels'

Furthermore, in the values.yml file, it appears that no specific image tag is defined, which defaults to using the "latest" tag:

Helm chart undefined specific image tag

'Fig 2: Undefined specific image tag'

To align our Helm charts with the policies, we need to define the deployment labels in accordance to what the policy expects, and specify an image tag. We will now update the respective files, as highlighted in the images below:

Helm charts defined required labels

‘Fig 3: Defined required labels’

Helm charts image with a specific tag

‘Fig 4: Image with a specific tag’

After implementing these fixes, rerunning the command helm template mars | conftest test -p policy/ - should not yield any policy failures.

Conclusion

This blog has highlighted the benefits of integrating Open Policy Agent (OPA) with Helm chart development to safeguard Kubernetes deployments. By enforcing standardised policies using Conftest and OPA, teams can ensure their Helm charts meet high security and compliant standards. Moreover, DevOps teams can integrate this mechanism into their GitOps workflow to streamline template validation and necessary security checks, embedding them directly into the deployment process.