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.
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.
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:
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.
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.
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.
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.