LoginSignup
14
1

エッジサーバにセキュアなJS実行環境を構築できる「Deno Subhosting」を試してみる

Last updated at Posted at 2023-12-17

はじめに

みなさんはエッジ関数をつかっていますか?
最近エッジロケーションでJavaScriptの実行環境を提供するサービスが多く登場しています。

仮にエッジ関数を提供するサービスを自分で作ろうとした場合、悪意のあるコードが内部システムに侵入しようとするというようなセキュリティリスクがあります。
このようなセキュリティリスクから守り、セキュアにエッジ関数を提供する機能としてDenoからDeno Subhostingという機能がリリースされました!
本記事ではDeno Subhostingとは何かということを整理し、実際に簡単なサンプルコードを動かしてみます。

エッジ関数とDeno Subhosting

エッジ関数

まずはエッジ関数について見ていきましょう。
エッジ関数を提供しているサービスには以下のサービスが挙げられます。

  • netlify edge function
  • Supabase Edge Functions
  • Cloudflare worker
  • AWS CloudFront Functions

エッジ関数ではキャッシュよりさらに手前の段階でJSを実行することになります。このメリットとしてはA/Bテストやリクエストに応じたリダイレクトの処理、JWTの検証などを高速に行うことができます。

このような考え方は、vercelが提唱するedge midlewareという概念で整理されています。

エッジ関数を作る上での問題

前述の通り、SaaSサービスとしてエッジ関数を提供しようとした場合、セキュリティ面が極めて重大な懸念事項になります。エッジ関数のプロバイダとしては、ユーザが入力したコードが内部システムに侵入したり、他のユーザーのデータにアクセスするなど脅威から守る必要があります。

Deno Subhostingとは

Deno Subhostingとは 「自分で作ったサービス内で、ユーザーによって書かれた信頼されていないJavaScriptコードを安全に実行するプラットフォーム」 です。Deno Subhostingを用いることでセキュアにエッジ関数を提供できるようになります。

また、前節で記載したセキュリティリスクから守るために、Deno Subhostingでは以下の三つのアプローチでテナントを隔離しています。(詳細はこちら)

  1. APIの制限
    javascriptのV8エンジンの機能であるisolateでの実行、仮想化されたファイルシステムの所有、VPCの分離を通して、自身のランタイムへのアクセスしか許可されないようにしています。[^3]
  2. インフラレベルでの防御
    独自のプロセス、名前空間、seccompフィルタを使用することで自身のデータにのみアクセスできます。
  3. リソースの公平性
    cgroupsによるリソース制限やリソース監視ツールの導入により、すべてのユーザーが公平なリソースを利用できることを保証しています。

各レイヤーでの適切な分離と監視により、安全なJS実行環境を提供しているというわけですね。

すでにDeno Subhostingはnetlify edge functionやSupabase Edge Functionsに使用されており、最近ではEC向けプラットフォームを提供しているDeco.cx社でも採用されたようです。

Deno Subhostingを試す

それでは実際にDeno Subhostingを試してみましょう!

Quick start

Deno SubhostingのTopのGet Startから、ページに従ってOrganization、アクセストークン、プロジェクトの順に作成していきます。

OrganizationのIDが表示され、その下の"Generate Access Token"ボタンを押してアクセストークンを生成します。OrganizationIDとアクセストークンが正しいかを確認するテスト用のCurlコマンドを出してくれるので、一応叩いて確認しておきましょう。
image.png
続いてプロジェクトを作成していきます。公式ページにあるように、すでにdenoの環境が整っている方はプロジェクトはjavascriptで書いたコードをdenoコマンドで実行して作成することもできますし、単純にCurlコマンドから作成することも可能です。
image.png

curl -i -X POST \
  https://api.deno.com/v1/organizations/XXXXXXXXX/projects \
  -H 'Authorization: Bearer XXXXXXX(access_token)' \
  -H 'Content-Type: application/json' \
  -d '{ "description": "My first project" }'

プロジェクトの作成までできたら、早速サンプルコードをデプロイしてみます。ここでもcurlとjavascriptでサンプルコードがありますが、今回はcurlでやってみます。内容としてはシンプルなHello Worldのテキストを応答するだけのコードです。
尚、depoyのAPIのパラメーターの説明についてはこちらに記載されています。image.png

curl -i -X POST \
  'https://api.deno.com/v1/projects/XXXXXXXXXX/deployments' \
  -H 'Authorization: Bearer XXXXXXX' \
  -H 'Content-Type: application/json' \
  -d '{
    "entryPointUrl": "main.ts",
    "assets": {
      "main.ts": {
        "kind": "file",
        "content": "Deno.serve((req: Request) => new Response(\"Hello World\"));"
      }
    },
    "envVars": {},
    "description": "My first deployment"
  }'

正常に終了すると以下のような画面となり、URLが表示されます。
image.png

URLを叩いてみると、想定通りHello Worldが応答されました!
image.png

デプロイするコードを変更する

先ほどはサンプルのコードをデプロイしてみましたが今度はデプロイするコードを変更してみます。
curlコマンドのリクエストボディ内のmain.tsのcontentsがデプロイされますので、適宜この項目を修正すればよさそうです。

"content": "Deno.serve((req: Request) => new Response(\"Hello World\"));"
"content": "Deno.serve((req: Request) => new Response(\"hogehoge\"));"

デプロイコマンドを実行して発行されたDeployment URLを押下すると、表示された文字が変更されていることがわかります。
image.png

このようにデプロイしたいコードはリクエストのボディに入れることになるので、より複雑なコードをデプロイする際には制御文字の排除等を行うようにしましょう。

ドメインを取得する

先ほどまででコードのデプロイをcurlからしていましたが、deploy APIのレスポンスにはデプロイ先のドメインが入っていません。

deploy APIのリクエスト
{
  "id": "XXXXXXXXX",
  "projectId": "XXXXXXXXXXX-XXXXXXXXX",
  "description": null,
  "status": "pending",
  "databases": {},
  "createdAt": "2023-12-17T11:47:11.893397Z",
  "updatedAt": "2023-12-17T11:47:11.893397Z"
}

そのためエンドポイントを確認するにはダッシュボードを見に行く必要があると思っていましたが、以下のようにAPIを叩くことでも確認できるようです。{deploymentId}には先ほどのdeploy APIのレスポンスに載っていたidを入れます。

リクエスト
curl -i -X GET \
  'https://api.deno.com/v1/deployments/{deploymentId}' \
  -H 'Authorization: Bearer XXXXXXXX' \
  -H 'Content-Type: application/json'
レスポンス
{
  "id": "XXXXXXXXX",
  "projectId": "XXXXXXXXXXX-XXXXXXXXX",
  "description": "My first deployment",
  "status": "success",
  "domains": [
    "cool-fly-21-qwtrp42em2cx.deno.dev"
  ],
  "databases": {},
  "createdAt": "2023-12-17T11:47:11.893397Z",
  "updatedAt": "2023-12-17T11:47:11.893397Z"
}

レスポンスの項目"domains"に先ほどのデプロイ先のドメインが入っています。APIからでも確認できていそうですね!
他にもDeno Deploy REST APIとして多くのAPIが提供されているので気になる方は見てみて下さい。

まとめ

エッジ関数とは何かという説明からはじめ、Deno Subhostingの紹介と簡単な使用方法について解説しました。

今回はエッジ関数をつくるという文脈でDeno Subhostingを紹介しましたが、ユーザーにアプリ内でカスタムロジックを書く機能を提供する、という視点で考えると他にもいろいろ応用範囲が広がりそうです。
他にもこんな使い方があるよーというアイディアを持っている方がいればぜひ教えてください!

14
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
1