Skip to content
Branch: master
Find file History
patrick-east Add a high level diagram for cf-worker example
Signed-off-by: Patrick East <east.patrick@gmail.com>
Latest commit 813150e May 16, 2019

README.md

WASM Policy Worker

This example shows making a Cloudflare Worker which can enforce Rego Policies that have been compiled into WebAssembly (wasm).

High Level Diagram

The Worker

The worker is a single javascript file that uses some npm modules, most importantly the @open-policy-agent/opa-wasm module.

Because the Cloudflare worker should be a single self-contained script we will use Webpack to build a bundle.js that includes the npm modules.

Build the worker using:

# Install dependencies
npm install

# Build with webpack
npm run build

The output of the build goes into the ./dist directory.

The policy

Using the example.rego file build it with:

opa build -d example.rego 'data.example.allow = true'

The input for the policy is going to be a JSON string like:

{
  "fetcher": {},
  "redirect": "manual",
  "headers": {},
  "url": "https://opa4fun.com/",
  "method": "GET",
  "bodyUsed": false,
  "body": null
}

The policy is only giving a boolean response for data.example.allow == true, in the example.rego policy file that means that it is a GET request or a POST to /api.

The policy itself isn't very exciting, but acts as a proof of concept. With access to the full Request and Response objects plus additional Cloudflare API's and headers you can do some pretty powerful things.

Uploading the Cloudflare worker

Prerequisites

Before using workers you must have a Cloudflare account and a site configured to use Cloudflare. See (https://www.cloudflare.com/)[https://www.cloudflare.com/] for instructions to get started.

You must also have Workers enabled. See https://www.cloudflare.com/products/cloudflare-workers/

Test It/Upload Via Editor

Open up the Editor in the Cloudflare dashboard and copy in the dist/bundle.js contents. Then upload the policy.wasm file as a "Resource" with the name REGO_WASM (needs to match the variable used in the code).

From the Editor you can configure & deploy the worker.

Upload via API

Alternatively, and arguably easier (once the worker is stable), they can be managed via API's.

For the commands shown below either setup some env variables for CF credentials and account info, or substitute values manually as required. See https://developers.cloudflare.com/workers/api/ for details.

export CF_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxx
export CF_EMAIL=example@gmail.com
export CF_ZONE_ID=yyyyyyyyyyyyyyyyyyyyyyy

Below are some helpers to make API calls. The official documentation is the best source of truth, but these should help get started.

Create a route

The worker script needs to be associated with a route. Start by creating one, or checking if some exist already (see below).

Make sure to substitute the domain name for your application, the example below is using a fake url example.com

curl -X POST "https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/workers/filters" \
    -H "X-Auth-Email:${CF_EMAIL}" \
    -H "X-Auth-Key:${CF_API_KEY}" \
    -H "Content-type: application/json" \
    -d '{"pattern": "example.com/*", "enabled": true}'

This example just adds a route that will catch pretty much any API call for the site. See https://developers.cloudflare.com/workers/api/route-matching/ for more details on configuring the route.

Get a route

curl -X GET "https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/workers/filters" \
    -H "X-Auth-Email:${CF_EMAIL}" \
    -H "X-Auth-Key:${CF_API_KEY}"

Initially this should be empty

POST a new worker/update the worker

This uses the Resource Bindings API to upload both a script and wasm binary together, with bindings for how to load the WebAssembly.

The key being the metadata_wasm.json file which defines what variable in the worker context to bind the loaded wasm module to, as well as the form parts that are the script and assembly files.

curl -X PUT "https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/workers/script" \
    -H "X-Auth-Email:${CF_EMAIL}" \
    -H "X-Auth-Key:${CF_API_KEY}" \
    -F 'metadata=@metadata_wasm.json;type=application/json' \
    -F 'wasmpolicy=@policy.wasm;type=application/wasm' \
    -F 'script=@./dist/bundle.js;type=application/javascript'

GET the current worker

curl -X GET "https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/workers/script" \
    -H "X-Auth-Email:${CF_EMAIL}" \
    -H "X-Auth-Key:${CF_API_KEY}"

Initially this should be empty

Exercising the policy

For the example policy you can verify it works by making requests like:

curl -svL -X GET https://${DOMAIN_NAME}/

With a successful 200 response (depends on the backing HTTP service).

curl -svL -X PUT https://${DOMAIN_NAME}/

With a 403 response like:

{
  "error": "Not allowed by policy"
}
You can’t perform that action at this time.