Open Policy Agent (OPA) For Kubernetes
Tags: kubernetes, devops, cloud, docker
Published: 2022-07-20 11:21:32

As much as we sometimes hate the word “standard”, at the end of the day, we have to have a standard for environments. Whether it’s how we’re deploying cloud-based infrastructure or how we’re getting alerted if something goes down, standards are important. Standards around how we deploy and manage components in our environments are just as, or if not more so, important.

In this blog post, you’ll learn about what OPA is and how it ties into Kubernetes.

## What Is OPA?

The short answer is it’s a standardized way to implement policies for your environment (who has access to what, what can be deployed, where it can be deployed, etc.).

The long answer is you have a way to manage every single layer of your environment with fine-grained control over how all pieces of your environment interact with one another. If you have a Kubernetes environment, you can manage everything from who has access to what Pods to how Pods are being deployed via a Kubernetes Manifest to injecting sidecars into Pods.

The way that you’re able to implement those policies is by using something called Rego, which is the OPA language (think of it as a programming language). Rego allows you to define the exact policy that you want in your Kubernetes environment down to the smallest form factor, like a Label. Because the policies are all code, it allows you to work with your team for collaboration vs having a “point and click” solution that you can’t collaborate on. 

For example, the code below makes sure that the `mylastname` Label in a Kubernetes Manifest starts with `Levan`. If it doesn’t, you’ll get an error output stating `mylastname label must start with Levan`

```jsx
package kubernetes.validating.label

deny[msg] {
	value := input.request.object.metadata.labels.mylastname

	not startswith(value, "levan")

	msg := sprintf("mylastname label must start with `Levan`; found `%v`", [value])
}
```

You can use OPA to enforce policies for:

- Kubernetes
- Microservices
- CICD

and a lot more. OPA is pretty much available for almost any cloud-based environment. You can see all of the different ecosystems [here](https://www.openpolicyagent.org/docs/latest/ecosystem/), and if you can’t find one, you can build one for your use case.

## How Does It Fit Into Kubernetes?

At the highest level, Kubernetes works as an API. It’s a way to manage infrastructure and services via an API. Without the Kubernetes API, Kubernetes wouldn’t exist.

At the second to the highest level, there are Controllers. Controllers look at what you’ve deployed to Kubernetes, like Pods, and confirm that the current state is the desired state. If you’re familiar with Terraform, sort of think of it like a TFSTATE file (Controllers aren’t files, this is just an analogy). The TFSTATE file looks to confirm that when you deploy infrastructure, it’s being deployed as the desired state. Kubernetes Controllers look to confirm that the Pods (and other components, Pods are just an example) are being deployed as the desired state.

[OPA Gatekeeper](https://open-policy-agent.github.io/gatekeeper/website/docs/) (or sometimes called the OPA Admission Controller), which is a project providing integration between Kubernetes and OPA, is just like the Controllers that were mentioned above for Pods. It confirms that the current state is the desired state. Diving in a bit deeper, the Kubernetes Admission Controller enforces policies for CRUD operations in your Kubernetes cluster. For example, let’s say that you want to enforce quotas/resource limits for Pods within your organization. Then, an engineer creates a Kubernetes Manifest without resource limits for the Pods they’re deploying. If OPA sees that the engineer is trying to deploy the Kubernetes Manifest with something like `kubectl create -f` or `kubectl apply -f`, the Kubernetes Admission Controller will block it from being deployed.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6iws5pwpe42iv9qgl295.png) 

### Other Pieces Of The Puzzle

Declarative languages and platforms appear to be what many organizations are going towards today. The reason why is because there’s a big difference between how you architect and plan declarative workloads vs imperative workloads.

Declarative == tell me what to do, not how to do it

Imperative == tell me what to do and how to do it

There are several reasons why engineers are moving towards OPA, and one of them is the fact that OPA and Kubernetes are both declarative. Rego is declarative, so you write policies in a way that tells OPA what you want, but not how to do it. In Kubernetes, you write YAML files that tell Kubernetes what you want, but not how to do it. Because both OPA and Kubernetes are declarative, you can architect both solutions in a similar fashion.

Another big reason is that Rego looks and sort of feels like Go (golang), and a lot of engineers working within Kubernetes use Go as Kubernetes was built using Go and it’s one of the most concise cloud-native languages.

## Deploying OPA For Kubernetes

Now that you know what OPA is and why it’s important in Kubernetes, let’s learn how to deploy it to a Kubernetes cluster.

1. Create a new Kubernetes Namespace to deploy OPA into

```jsx
kubectl create namespace opa
```

1. Create TLS credentials for OPA. The communication between Kubernetes and OPA must be a secure connection using TLS.

```jsx
openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes -key ca.key -days 100000 -out ca.crt -subj "/CN=admission_ca"
```

1. Generate the TLS key and cert

```jsx
cat >server.conf <<EOF
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
prompt = no
[req_distinguished_name]
CN = opa.opa.svc
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth, serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = opa.opa.svc
EOF
```

```jsx
openssl genrsa -out server.key 2048
openssl req -new -key server.key -out server.csr -config server.conf
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 100000 -extensions v3_req -extfile server.conf
```

1. Create a Kubernetes Secret to store the TLS credentials

```jsx
kubectl create secret tls opa-server --cert=server.crt --key=server.key --namespace opa
```

1. Deploy OPA as an Admission Controller. Because it’s a huge YAML file, you can find the configuration at the GitHub URL [here](https://github.com/AdminTurnedDevOps/kubernetes-examples/blob/main/OPA/admission-controller.yaml). It creates the following for OPA to have access to the Kubernetes cluster:
- ClusterRoleBinding
- Role
- RoleBinding
- Service
- Deployment

You can run `kubectl apply -f admission-controller.yaml` when you pull the code in the URL above from GitHub

After you deploy, you should see the admission controller running in the `opa` Namespace by running the following command:

`kubectl get all -n opa`

Congrats! You have successfully deployed the Admission Controller.