Distributing API Authorization Policies using OPA Bundles
Tags: opa, security, microservices, authorization
Published: 2022-07-05 08:00:12

A typical organization will have several Applications & Deployments in multiple environments. Most of them would have some central Identity Provider like Keycloak or Azure AD/Okta... 
While the identity provider issued OAuth2.0 token and solve the Identity problem, we didn't have a clean central way to handle API Authorization. This usually might be bundled along with the Application code.

For Authorization, OPA is an open-source CNCF graduated project that allows us to write Authorization logic in Declarative statements.

The goal today is to explore the OPA bundle feature. We can 

- build policies using Rego, 
- package them as bundles (tar files), 
- and distribute them to various Applications or Deployment Clusters and the from a central location.

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

The central policy portal can organize your policies as the below diagram shows. An individual bundle is created at the environment level and it will have all the policies of the projects that the environment hosts.


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

In my organization, we had a similar distribution of environment. 
All the APIs were fronted by various API Gateways. What we want is for every call should be intercepted by API Gateway and forwarded to the Authorization engine. If the Authorization engine returns false then the request should be declined with HTTP status 403. Below diagram represents this.


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


The below table gives details on how this can be achieved with different API Gateways.

| API Gateway | Details |
| :---------:| -------:|
|  AWS API Gateway | - |
|  Traefik | [Using Forward Auth Middleware](https://doc.traefik.io/traefik/middlewares/http/forwardauth/) |
|  NGINX | [Module ngx_http_auth_request_module] (http://nginx.org/en/docs/http/ngx_http_auth_request_module.html) |


In our case, we have deployed OPA as a sidecar to a golang API project. The API receives a request from the API Gateway and then invokes the OPA. It parses the response received and if the Allow is set to false, then the API will return 403 to API Gateway.


Below is the yaml for the deployment of the API project and OPA. The configuration is from wsl2 desktop. 

```
apiVersion: apps/v1
kind: Deployment
metadata:
  name: authorization
  namespace: default
  labels:
    app: authorization
spec:
  replicas: 1
  selector:
    matchLabels:
      app: authorization
  template:
    metadata:
      labels:
        app: authorization
    spec:
      containers:
      - name: auth-policy-manager
        image: naseemmohammed/policyengine:0.1.7
        imagePullPolicy: IfNotPresent
        env:
        - name: ENV_AUTH_SERVER
          value: ":8080"
        - name: ENV_PPSA
          value: "localhost"
        - name: ENV_OPA_PORT
          value: "8181"
        ports:
        - containerPort: 8080
      - name: opa
        image: openpolicyagent/opa:0.41.0
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8181
        env:
		# location to subscribe for new policy bundles
        - name: AWS_REGION 
          value: us-east-2
        - name: AWS_ACCESS_KEY_ID
          value: AKIA52XXXUIISSCC-FAKE
        - name: AWS_SECRET_ACCESS_KEY
          value: E/VLCUZJ2G-NOTEXISTING-aUhmsMcI33yS8O     
        args:
          - "run" 
          - "--ignore=.*"  # exclude hidden dirs created by Kubernetes
          - "--server"
          - "--set=decision_logs.console=true"          
          - "--config-file"
          - "/config/config.yaml"
        volumeMounts:        
        - readOnly: true  
          mountPath: /config
          name: config-volume
        livenessProbe:
          httpGet:
            scheme: HTTP              # assumes OPA listens on localhost:8181
            port: 8181
          initialDelaySeconds: 5      # tune these periods for your environemnt
          periodSeconds: 5000  # in prod reduce to 5
        readinessProbe:
          httpGet:
            path: /health?bundle=true  # Include bundle activation in readiness
            scheme: HTTP
            port: 8181
          initialDelaySeconds: 5
          periodSeconds: 5
      volumes:
      - name: config-volume
        hostPath:
          # directory location on host
          path: /run/desktop/mnt/host/c/Users/nmohammed/Downloads/cluster-files
          # this field is optional
          type: Directory
      imagePullSecrets:
        - name: topsecret

```

Now the OPA configuration file below

```
services:
  s3:
    url: https://zohoohio.s3.us-east-2.amazonaws.com
    credentials:
      s3_signing:
        environment_credentials: {}

bundles:
  authz:
    service: s3
    resource: Zoho-onprem/bundle.tar.gz
    persist: false
    polling:
      min_delay_seconds: 100
      max_delay_seconds: 200
```