<img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id=521127644762074&amp;ev=PageView&amp;noscript=1">

Fairwinds Jamathon: Unlocking the Power of OpenAI: Creating OPA Policies for Kubernetes

Fairwinds hosts an annual Jamathon amongst employees. It’s a fun, several week program that gives every person the chance to try something new, work with different teams, and come up with some pretty cool ideas. 

During our most recent Jamathon, one team brainstormed experiments we could run with OpenAI’s ChatGPT. The team was made up of Nick Haven, Mahmoud Hosseini, Mary Henry, Vitor Vezani and me. Ideas included generating Helm Charts, creating policies, writing automation rules, building Dockerfiles and writing open source documentation. A number of experiments were run, but this blog post is to talk about using OpenAI to create OPA policies. 

What is OPA?

Open Policy Agent (OPA) is a framework for validating structured data. It nudges users toward writing policy-as-code, extending the community’s successful push toward infrastructure-as-code. While OPA can validate any kind of structured data, including Terraform, HTTP requests, and Dockerfiles, it is most often thought of in conjunction with Kubernetes manifests. Learn more about managing OPA policies with Fairwinds Insights.

OPA and Fairwinds Insights

Fairwinds Insights has over 100 built-in policies that can be used to audit or block resources in your Kubernetes environment or Infrastructure-as-Code. Insights also allows users to create custom policies using OPA. We wanted to see how OpenAI would work if we prompted it to write OPA policies and also to include those in Insights. Here’s what it looks like: 

OpenAI prompt: Create an OPA policy that blocks anyone from using the default namespace. 

	package kubernetes.admission

	import data.kubernetes.admission

	default allow = false

	deny {
		Admission.kubernetes.namespaces[_] metadata.name == “default”
	}

In this policy, we define a package called kubernetes.admission and import the ‘data.kubernetes.admission’ package, which contains the input data for the policy. The ‘allow' variable is set to ‘false’ by default, which means that all requests are denied unless an explicit ‘allow’ rule is defined.  

The deny rule is triggered if any namespace in the admission.kubernetes.namespaces array has a metadata.name equal to default. This rule denies the request and prevents anyone from using the default namespace.   

You can apply this policy to your OPA instance, which is typically integrated with a Kubernetes cluster to enforce the restriction on the default namespace. Keep in mind that OPA needs to be properly configured and integrated with Kubernetes for the policy to take effect. 

This isn’t a bad start. ChatGPT is clearly familiar with Rego, the programming language used for OPA policies, as well as the basic syntax needed for Kubernetes checks. But Fairwinds Insights has a more powerful system for writing OPA policies than is typically used, so we needed to get more specific.

In order to get ChatGPT to write a Fairwinds-flavored OPA policy, we just provided an example! ChatGPT was able to easily translate that into a policy with different behavior. Here’s our prompt:

Here is an example OPA policy. Can you write a brand new OPA policy example using the 'fairwinds' package?
<>
package fairwinds
labelblock[actionItem] {
    # List the keys of Kubernetes labels that will be blocked.
    blockedLabels := {"foo"}
    provided := {label | input.metadata.labels[label]}
    missing := blockedLabels - provided
    found := blockedLabels - missing
    count(found) > 0
    actionItem := {
        "title": "Bad label is present",
        "description": sprintf("Label %v is present", [found]),
        "severity": 0.1,
        "remediation": "Remove the label",
        "category": "Reliability"
    }
}
<>
The new policy you write should disallow creating resources in the default namespace.`

Note that we’re providing a lot more options than the OPA policy ChatGPT originally created. A typical policy just outputs a “message” — we add lots of metadata, such as description, severity level, a category, and remediation guidance. ChatGPT picked up on this immediately. Here’s how it responded to the above prompt:

Sure, here is a new OPA policy example using the 'fairwinds' package to disallow creating resources in the default namespace.

package fairwinds

deny[actionItem] {
    input.metadata.namespace == "default"
    actionItem := {
        "title": "Resource in default namespace",
        "description": sprintf("Resource %v is created in the default namespace", [input.kind]),
        "severity": 0.7,
        "remediation": "Create resources in a non-default namespace",
        "category": "Security"
    }
}

This policy denies any resources created in the default namespace. The severity of this policy violation is 0.7, indicating a significant issue. The remediation suggested is to create resources in a non-default namespace.

This is perfect! ChatGPT not only got the logic right, it also came up with a good human-readable title, description, and remediation. “Security” is in fact a valid category, and arguably the right one for this policy (mingling many disparate resources in the default namespace makes it hard to adhere to the Principle of Least Privilege). It came up with a severity level of 0.7, which is pretty high, but not unreasonable.

We expect that an end-user utilizing ChatGPT to generate policies would spend a couple minutes looking over the logic and metadata, and maybe tweaking some values — like severity level.

While ChatGPT does a great job with a lot of different types of policies, it’s not 100% perfect. Here’s an example that it doesn’t get quite right:

Can you write a policy which makes sure that CPU requests are always set to at least 50 millicpu?

package fairwinds

deny[actionItem] {
    input.spec.template.spec.containers[_].resources.requests.cpu < 0.05
    actionItem := {
        "title": "Insufficient CPU request",
        "description": sprintf("CPU request for container %v is less than 50 millicpu", [input.spec.template.spec.containers[_].name]),
        "severity": 0.5,
        "remediation": "Set CPU requests to at least 50 millicpu",
        "category": "Performance"
    }
}

This policy will iterate over every container in the pod specification and check if the CPU requests are less than 0.05 (50 millicpu). If it finds such a case, it denies the action and suggests a remediation.

Interestingly, ChatGPT seems to understand one of the limitations of its own policy — that the CPU would have to be specified in units of CPU. In reality, this field is a string, and needs to be passed to the built-in units.parse function before it can be measured.

Another limitation is that this policy can only apply to certain controller types, like Deployments. It would likely break on a Job, which puts the pod spec in a slightly different location. It would be better to check input.kind at the start of the policy to make sure it’s valid.

Integrating ChatGPT into Fairwinds Insights

The team continued to play around with different prompts and then asked: what do we want to do with this next?

We decided to create a beta feature in Fairwinds Insights. You can plug your OpenAI account into the Insights CLI. It means Insights users can use the insights-cli for generating OPA policies, powered by OpenAI. 

Of note, there are a few ‘gotchas’ — which is why this is a beta feature in Insights that users can play with, but users should check ChatGPT’s outputs carefully. In this example, the ChatGPT-generated OPA policy stops users from creating a Namespace called default, but the user probably wants to stop people from putting things in a default namespace that already exists. Here is our example: 

➜ insights-cli generate opa openai -m gpt-4 -p "block anyone from using the default namespace"
...........
package fairwinds

deny[actionItem] {
    input.metadata.namespace == "default"
    actionItem := {
        "title": "Resource in default namespace",
        "description": sprintf("Resource %v is created in the default namespace", [input.kind]),
        "severity": 0.7,
        "remediation": "Create resources in a non-default namespace",
        "category": "Security"
    }
}

To make this new beta feature work, we did some post-processing of ChatGPT’s response to make sure we only output valid code (and none of the English explainers that it tends to add at the beginning and end of its responses). Overall, it seems to work well, though users will still need to check ChatGPT’s work to make sure it has created a correct policy, and may need to tweak some of the output parameters, such as Severity and Category.

OpenAI presents new opportunities in every industry. This is a cool way to apply the benefits to Kubernetes policies. But users should be careful to review all code before putting it into use and, of course, be very careful about putting any proprietary information into OpenAI. 

Free Download: A Platform Engineers Guide to Kubernetes