# Open Policy Agent

Open Policy Agent, "OPA" for short, is a lightweight and responsive framework that was developed to de-couple policy from your services' code, across your service mesh. The capabilities of OPA are far-reaching and we will run you through some of the most helpful functionalities that it offers.

{% hint style="success" %}
**Don't know how to get started with Open Policy Agent?**

Add OPA into your Grey Matter service mesh with this [tutorial](https://docs.greymatter.io/guides/open-policy-agent-integration)!
{% endhint %}

## Editing Policies Already Set in Place (OPA's Policy API)

Once OPA is added into your service mesh and policies are set in place, you are able to edit and delete these policies on the fly with OPA's REST API. It's as simple as creating or modifying a secret in your service mesh and shelling into the pod that contains your policy.

If you want to add a policy to a service pod, remove the current secret _opa-policy_ you have. This can be done with:

```
kubectl delete secret opa-policy
```

This will allow our new policy to be available in the mesh. Now that we're in the same directory as our new policy, we can make that our new _opa-policy_ secret with:

```
kubectl create secret generic opa-policy --from-file your-policy.rego
```

Now, we must shell into the Grey Matter sidecar of the pod we are working on. We will use this kubectl command to get a shell of the sidecar:

```
kubectl exec -it <pod-name> -c sidecar sh
```

It is at this point where we can send policy edits through OPA's REST API. There are three HTTP methods that OPA's REST API has dedicated to editing policies:

* PUT - Upload a new policy
* PATCH - Edit an existing policy
* DELETE - Delete an existing policy

Here is an example of how to upload a new policy through the sidecar using curl and the Rego policy file that we made into a secret:

```bash
curl -X PUT --data-binary @your-policy.rego \
    localhost:8181/v1/policies/
```

{% hint style="info" %}
**Note** Using this same curl syntax but with a different method i.e. PATCH, DELETE, you can also update and delete policies.
{% endhint %}

## API Protection

Protecting your API and establishing authorized users is as simple as running OPA in your API pod and using the following methods to determine _who_ gets _what_ permissions.

{% hint style="success" %}
**Don't want to deal with the hassle of constantly uploading and deleting policies in your mesh to see if they work correctly?**

Test out your policies with test requests / data and check out tons of Rego Policy examples in the [Rego Playground](https://play.openpolicyagent.org/)!
{% endhint %}

## JWT Verification / Signing

The Rego library includes built in functions for JSON Web Token verification and signing. This gives the ability for OPA to be able to authenticate JWTs, interpret the payload information, and sign JWTs with your certificate.

### Verification

For instance, take this example HTTP request and Rego policy from the [OPA Docs](https://play.openpolicyagent.org/)

```javascript
"request": {
    "http": {
    "headers": {
        ":authority": "example-app",
        ":method": "POST",
        ":path": "/pets/dogs",
        "accept": "*/*",
        "authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiQWxpY2lhIFNtaXRoc29uaWFuIiwicm9sZXMiOlsicmVhZGVyIiwid3JpdGVyIl0sInVzZXJuYW1lIjoiYWxpY2UifQ.md2KPJFH9OgBq-N0RonGdf5doGYRO_1miN8ugTSeTYc",
        "content-length": "0",
        "user-agent": "curl/7.68.0-DEV",
        "x-ext-auth-allow": "yes",
        "x-forwarded-proto": "http",
        "x-request-id": "1455bbb0-0623-4810-a2c6-df73ffd8863a"
```

```
package envoy.http.jwt   # Envoy JWT Package

default allow = false

allow {
    claims.username == "alice"  # Information stripped from ensured token
}

claims := payload {
    io.jwt.verify_hs256(bearer_token, "B41BD5F462719C6D6118E673A2389")  # Ensure token authorization with certificate

    [_, payload, _] := io.jwt.decode(bearer_token)  # Return token information
}

bearer_token := t {    # Sanitize token string
    v := input.attributes.request.http.headers.authorization   # Take raw string
    startswith(v, "Bearer ")  # Get start
    t := substring(v, count("Bearer "), -1)  # Retrieve full JWT Token
}
```

There are several built-in JWT functions that Rego has in its arsenal, in order to work with JSON Web Tokens. The first step being to split the request strings into the raw token, then comparing it to a certificate, and finally extracting a payload, if necessary. As per the example, one can also utilize this to determine Role Based Access.

{% hint style="info" %}
**Note** More information on Rego's built in [Token Verification](https://www.openpolicyagent.org/docs/latest/policy-reference/#token-verification).
{% endhint %}

### Signing

Rego also supports JWT Signing in two methods:

* Raw: `io.jwt.encode_sign_raw(headers, payload, key)`
* With a Symmetric / RSA key: `io.jwt.encode_sign(headers, payload, key)`

Here are two examples of how these functions would look (from [https://openpolicyagent.org](https://openpolicyagent.org)):

#### Signing with a Symmetric Key (HMAC with SHA-256)

```
io.jwt.encode_sign({
    "typ": "JWT",
    "alg": "HS256"
}, {
    "iss": "joe",
    "exp": 1300819380,
    "aud": ["bob", "saul"],
    "http://example.com/is_root": true,
    "privateParams": {
        "private_one": "one",
        "private_two": "two"
    }
}, {
    "kty": "oct",
    "k": "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow"
})
```

#### Signing Raw Token

```
io.jwt.encode_sign_raw(
    `{"typ":"JWT","alg":"HS256"}`,
     `{"iss":"joe","exp":1300819380,"http://example.com/is_root":true}`,
    `{"kty":"oct","k":"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow"}`
)
```

{% hint style="info" %}
**Note** More information on Rego's built in [Token Signing](https://www.openpolicyagent.org/docs/latest/policy-reference/#token-signing).
{% endhint %}

## Access Control Lists (OPA's Data API)

OPA offers even more control over your security and permissions system. Using OPA's Data API, you can hardcode in Access Control Lists that determine who is authorized to use your API and what permissions they have. These ACL's take the form of json documents that map out users and their individual permissions. Custom Rego policies can extract information from your ACL to help resolve decisions. Here is what one ACL might look like:

```javascript
{
  "john": [
    "read"
  ],
  "bob": [
    "read",
    "write"
  ]
}
```

We would save this as something along the lines of `api-permissions-acl.json`. We would go about uploading these _data_ documents the same way we edit policies: first, upload the file as a secret to the mesh, and then use the same PUT/PATCH/DELETE request style in the sidecar. However, the curl syntax is slightly different. We would point the curl to a different endpoint, we would use `/v1/data/` instead. Uploading a new ACL data document would look as such:

```bash
curl -X PUT --data-binary @api-permissions-acl.json \
    localhost:8181/v1/data/
```

And to invoke this data in our policy, we would use this _include_ statement:

```
import data.api.permissions.acl
```

{% hint style="info" %}
**Note** More information on OPA's [Data API](https://www.openpolicyagent.org/docs/latest/rest-api/#data-api).
{% endhint %}
