Web ブラウザ上で Wasmを使ってOpen Policy Agent (OPA) を実行


# はじめに

汎用ポリシーエンジンである Open Policy Agent では、WebAssembly 形式でポリシーを配布可能です。本記事では、WebAssembly 形式で出力された Open Policy Agent のポリシーを Web ブラウザで評価することを試してみた内容を紹介します。

# Open Policy Agent とは

[Open Policy Agent (OPA)](https://www.openpolicyagent.org/) は OSS の軽量で汎用的なポリシーエンジンです。開発時や運用時におけるルール(ポリシー)を事前に定義することで、ポリシーに反する情報を検出できます。OPA はポリシーを定義するための宣言型言語 Rego と、ポリシー評価のためのシンプルな API を提供しています。

# WebAssembly とは

[WebAssembly](https://webassembly.org/) とは、 Web ブラウザ上で実行可能なバイナリコードの仕様です。C/C++, Rust などの言語で記述されたプログラムをコンパイルし、Web ブラウザ上で実行します。実行時のパース等が不要で高速に動作させることができるため、ブラウザの機能では実現できなかった処理が可能になります。

また、WebAssembly はプラットフォームに依存せず、高いポータビリティを有していることから、Web ブラウザ以外での活用という点でも注目されています。

# WebAssembly と OPA

OPA には、ポリシーを WebAssembly 形式で出力する機能があります。通常ポリシー評価のためには、OPA を Daemon として動作させて API を利用するか、ライブラリとして組み込む(Go 言語に限る)必要があります。ポリシーを Wasm 形式で出力することで、Web ブラウザをはじめ、さまざまなプラットフォーム上で OPA のポリシーを評価できるようになります。

# Web ブラウザ上で OPA を動作させる手順

ここからは、OPA のポリシーを WebAssembly (Wasm) 形式で出力し、Web ブラウザ上で実行させる手順を説明していきます。

なお、Node.js 上での実行に関しては、[`opa-wasm` のサンプル](https://github.com/open-policy-agent/npm-opa-wasm/tree/main/examples)等を参考にしてください。

## Wasm ファイルの作成

まずは Rego で記述したポリシーから、Wasm ファイルを作成します。 Wasm ファイルの作成には、バンドルファイルを作成するための `opa build` コマンドを利用します。

```sh
$ opa build -t wasm -e 'example/allow' policy/wasm/policy.rego
```

Wasm 形式でポリシーを出力するには最低 1 つ、エントリーポイントとなるルールを指定する必要があります(`-e` で指定)。上記の例では、`example` パッケージの `allow` というルールがエントリーポイントとなります。

`opa build` コマンドが成功すると、`bundle.tar.gz` というバンドルファイルが生成されます。Wasm ファイルはこのバンドルファイルの中に出力されます。

```sh
# bundle.tar.gz の中身を確認
$ tar tvf bundle.tar.gz
-rw------- 0/0             128 1970-01-01 09:00 /policy/wasm/policy.rego
-rw------- 0/0          116734 1970-01-01 09:00 /policy.wasm
-rw------- 0/0              87 1970-01-01 09:00 /.manifest
# policy.wasm ファイルのみを解凍
$ tar -xzf bundle.tar.gz /policy.wasm
```

Wasm 形式の注意点としては、JSON で記述される Data ドキュメントが含まれないという点が挙げられます。Daemon として OPA を実行する場合、ポリシーと一緒に Data ドキュメントが読み込まれ、ポリシーの評価時に Data ドキュメントの情報が参照されます。しかし、Wasm で利用する際には後述するように外部から Data の情報をセットする必要があります。

## Wasm ファイルの読み込み (`@open-policy-agent/opa-wasm` の利用)

作成した Wasm 形式のポリシーを JavaScript で利用するには、[@open-policy-agent/opa-wasm](https://github.com/open-policy-agent/npm-opa-wasm) パッケージを利用します。

```sh
$ npm install @open-policy-agent/opa-wasm
```

`@open-policy-agent/opa-wasm` には `loadPolicy` という関数が用意されており、これを利用して OPA の wasm ファイルを読み込むことができます。

```ts
import { loadPolicy } from "@open-policy-agent/opa-wasm";

...
// wasm ファイルを ArrayBufferとして読み込む
const policyWasm = await fetch("/path/to/wasm").then(res => res.arrayBuffer());
// webpack の arraybuffer-loader を利用すればバンドル時に組み込むことも可能
// const policyWasm = require("./path/to/policy.wasm");

const policy = await loadPolicy(policyWasm);
```

## ポリシー評価の実行

`loadPolicy` によって作成されたオブジェクトには、`setData()` と `evaluate()` メソッドが提供されています。

`setData()` はポリシーに Data ドキュメントをセットします。前述の通り、Wasm 形式のポリシーには Data ドキュメントが含まれないため、(API などを利用して) 外部から取得してポリシーに Data ドキュメントをセットします。

```ts
const data = { hello: "world" };
policy.setData(data);
```

`evaluate()` は Data ドキュメントと入力を基にポリシーを評価し、評価結果が `object` として返却されます。

`input` には JSON serializable なオブジェクトを指定します。

```ts
const input = { world: "world" };
const result = policy.evaluate(input);
console.log(result);
// {result: true}
```

# おわりに

OPA のポリシーを Wasm 形式で作成し、Web ブラウザ上でポリシー評価をしてみました。Wasm 形式でポリシーを配布できるため、1 つのコードベースから複数のプラットフォームで同じポリシーを適用できる点はかなり有用だと感じました。例えば Web アプリケーションとバックエンドサーバで、同一のポリシーを利用して Payload の Validation を行うといったユースケースも考えられます。

引き続き OPA, WebAsembly の動向に注目していきたいと思います。