These instructions go over the instructions for deploying OPA, creating simple policies and then evaluating the using the OPA REST API.
In order to deploy this demo you will need the following:
- OpenShift Cluster. For this demo we use OCP 4.12, however any 4.x version should work as well. Alternatively you can use any Kubernetes cluster, however the instructions in this demo are specific to OpenShift.
- OpenShift CLI. You can download OC cli from your cluster as specified here.
- Git Bash or shell/bash terminal. Although not required, the commands below assume a Linux shell syntax.
In order to deploy OPA as a REST API we first need to create 3 simple Kubernetes resources.
Create a file named deployment.yaml with this content:
apiVersion: apps/v1
kind: Deployment
metadata:
name: opa
spec:
replicas: 1
selector:
matchLabels:
app: opa
template:
metadata:
labels:
app: opa
spec:
containers:
- name: opa
securityContext:
capabilities:
drop: ["ALL"]
runAsNonRoot: true
allowPrivilegeEscalation: false
seccompProfile:
type: "RuntimeDefault"
image: openpolicyagent/opa:0.50.1-debug
args:
- "run"
- "--watch"
- "--ignore=.*"
- "--server"
- "--skip-version-check"
- "--log-level"
- "debug"
- "--set=status.console=true"
- "--set=decision_logs.console=true"
Most of the parameters above are optional, but included for higher verbosity level which is helpful for troubleshooting. You can view the purpose of these parameters in the documentation for the opa run command. Some important observations to highlight here are:
We are using OPA version 0.50.1
which was the latest available at the time of writing. We are using the -debug
version of the OPA image which includes a CLI that can be useful for inspecting the deployed files. However for a production release it is recommended that you use the "-rootless" version of this image.
The --server
parameter is what tells OPA to run in server mode so that it can listen for REST API requests.
Next create a file named "service.yaml" which will expose OPA as a service within the cluster:
kind: Service
apiVersion: v1
metadata:
labels:
app: opa
name: opa
spec:
selector:
app: opa
ports:
- name: http
protocol: TCP
port: 80
targetPort: 8181
Finally create a file named "route.yaml" which will expose OPA service:
kind: Route
apiVersion: route.openshift.io/v1
metadata:
labels:
app: opa
name: opa
spec:
selector:
matchLabels:
app: opa
to:
kind: Service
name: opa
port:
targetPort: http
Note that we are using http for demo purposes. In a production environment ensure that you are using https.
First login to your OpenShift cluster by obtaining a token from your cluster using the OC CLI:
oc login --token=<sha256~token> --server=<your-openshift-cluster-api-url>
Next create a new project for this cluster. We export the NAMESPACE name as a variable so that it can be used in subsequent steps:
NAMESPACE=opa
oc new-project $NAMESPACE
New proceed to create the resources using the files created above:
oc apply -f deployment.yaml -n $NAMESPACE
oc apply -f service.yaml -n $NAMESPACE
oc apply -f route.yaml -n $NAMESPACE
To get the route that was created, issue the following command:
echo http://$(oc get route $NAMESPACE -o jsonpath='{.spec.host}')
Navigate to the route below in your browser and you should see the OPA home screen, which allows you to evaluate a policy
Next we need to define and load a couple policies for demo purposes.
One of the nice features about Rego is that it provides several built-in functions. One set of functions that is particularly helpful is the one for JWT (JSON Web Token) token validation. The policy shown below will first decode a JWT token and then validate it against a secret that was used to sign the token. We use a "shared" secret for demo purposes, however the JWT function can verify token using JWKS (JSON Web Key Sets). For anybody familiar with the JWKS verification flow knows that it is not a trivial implementation. The built-in verify token functions will take care of retrieving KIDs (Key ids) from the corresponding well known url and it even provides caching capabilities to speed up that process.
First create a file named jwt.rego:
package com.demo.common.jwt
import input
import future.keywords.in
valid_token(jwt) = token {
[header, payload, sig]:= io.jwt.decode(jwt)
valid := io.jwt.verify_hs256(jwt, 'secret')
token := {"valid": valid,
"name": payload.name}
}
As you can see from this rego file, it is primarily json, except for the import/package headers. Again, in this case we are using a shared secret, which is done only for demo purposes.
We will then load this policy using the "Create Policy" REST API from OPA Agent:
OPA_URL=http://$(oc get route $NAMESPACE -o jsonpath='{.spec.host}')
cat jwt.rego | curl --location --request PUT "${OPA_URL}/v1/policies/com/demo/common/jwt" --header 'Content-Type: text/plain' --data-binary '@-'
Let us decompose the url used above:
${OPA_URL}
- the base OPA URLv1/policies
- the default location for policiescom/demo/common/jwt
- this is how policies are retrieved. Noticed that it matches the package name, i.e.com.demo.common.jwt
, but using a different character separator. There is no hard rule that these should match, but I have found it as a good practice to follow to make it easier to organize policies.
In this step we create a policy that uses the common JWT policy loaded above. Create a file named api.rego with this content:
package com.demo.myapi
import data.com.demo.common.jwt.valid_token
default allow := { #disallow requests by default
"allowed": false,
"reason": "unauthorized resource access"
}
allow := { "allowed": true } { #allow GET requests to viewer user
input.method == "GET"
input.path[1] == "policy"
token := valid_token(input.identity)
token.name == "viewer"
token.valid
}
allow := { "allowed": true } { #allow POST requests to admin user
input.method == "POST"
input.path[1] == "policy"
token := valid_token(input.identity)
token.name == "admin"
token.valid
}
Notice the import to the "valid_token" function. It matches the package used above, but it is prepended with data.
Next we load this policy with a similar curl command:
cat api.rego | curl --location --request PUT "${OPA_URL}/v1/policies/com/demo/myapi" \
--header 'Content-Type: application/json' --data-binary '@-'
To evaluate the policy we will need to get a valid jwt token. You can get one from jwt.io, the only requirement is that you enter the same secret from the jwt policy above into the <your-256-bit-secret>
on the "Verify Signature" section. Additionally change the name (in the Payload section) to viewer and copy the generated token. Subsequently repeat the steps and enter admin as the name and save both tokens in a file where you can copy values from.
Next create a request to test a successful viewer request named viewer-allowed.json:
{
"input": {
"identity": "<viewer token>",
"path": "policy",
"method": "GET"
}
}
Execute the curl command below (notice the url changes from policy to data):
cat viewer-not-allowed.json | curl --location --request POST "${OPA_URL}/v1/data/com/demo/myapi" --header 'Content-Type: application/json' --data-binary '@-'
Expect allowed true output similar to this:
{
"result": {
"allow": {
"allowed": true
}
}
}
Next create a request to test a not allowed viewer request named viewer-not-allowed.json by changing the method to POST:
{
"input": {
"identity": "<viewer token>",
"path": "policy",
"method": "POST"
}
}
Execute the curl command below and expect the output to include allowed false:
cat viewer-not-allowed.json | curl --location --request POST "${OPA_URL}/v1/data/com/demo/myapi" \
--header 'Content-Type: application/json' --data-binary '@-'
{"result":{"allow":{"allowed":false,"reason":"unauthorized resource access"}}
Next create an admin-allowed.json file with this request:
{
"input": {
"identity": "<admin jwt token>",
"path": "policy",
"method": "POST"
}
}
Execute the curl command and expect the output to include allowed true:
cat admin-allowed.json | curl --location --request POST "${OPA_URL}/v1/data/com/demo/myapi" \
--header 'Content-Type: application/json' --data-binary '@-'
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. ๐๐๐
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google โค๏ธ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.