kawases.org

TOKYO, JP 00:00:00

Cloudflare Workers + HonoでIP制限を実装する際にはまったポイント

Cloudflare-workerでの静的ファイルの配信でIPアドレス制限のはまったポイントについて解説します

Table of Contents

はじめに

Cloudflare Workers で静的サイトを配信しつつ、特定のIPアドレスからのみアクセスを許可するIP制限を実装しようとした際にハマったポイントと解決策をまとめます。

環境

  • Cloudflare Workers
  • Hono(Webフレームワーク)
  • Wrangler(CLIツール)
  • Static Assets(静的ファイル配信機能)

やりたかったこと

社内向けWebアプリケーションを Cloudflare Workers でホスティングし、特定のIPアドレス(社内ネットワーク)からのみアクセスを許可したい。

最初の実装

特に何も考えずHonoにIP制限をかけるミドルウェアがあることは知ってたのでそちらを実装しました。

hono.devEXTERNALIP Restriction Middleware - HonoWeb framework built on Web Standards for Cloudflare Workers, Fastly Compute, Deno, Bun, Vercel, Node.js, and others. Fast, but not only fast.LINKOPEN →

https://hono.dev/docs/middleware/builtin/ip-restriction

wrangler.jsonc

{
  "name": "frontend",
  "main": "index.js",
  "compatibility_date": "2024-01-01",
  "assets": {
    "directory": "./dist",
    "binding": "ASSETS"
  }
}

index.js

import { Hono } from 'hono'
import { getConnInfo } from 'hono/cloudflare-workers'
import { ipRestriction } from 'hono/ip-restriction'

const app = new Hono()

app.use(
  '*',
  ipRestriction(getConnInfo, {
    denyList: [],
    allowList:[
      '192.0.2.0',
      '192.0.2.1',
    ]
  })
)

app.all('*', async (c) => {
  return c.env.ASSETS.fetch(c.req.raw)
})

export default app

発生した問題: IP制限が効かない

症状

  • IPアドレスは正しく取得できている
  • 許可リストにないIPからでもページが表示されてしまう
  • ミドルウェアが実行されていない様子

原因

Cloudflare Workers の Static Assets はデフォルトで Worker コードを経由せずに直接配信される。

developers.cloudflare.comEXTERNALStatic AssetsCreate full-stack applications deployed to Cloudflare Workers.LINKOPEN →

https://developers.cloudflare.com/workers/static-assets/
どうやら、wrangler.jsoncassets を設定すると、静的ファイルへのリクエストは Worker のコードを一切通らずに、Cloudflare のエッジから直接配信されるみたいです。

後述のデバックでIPが許可されてないにもかかわらずリクエストが通ってたため、原因が切り分けられました(公式ドキュメントを読む大切さ。。。)

つまり、どんなにapp.use('*', ...) でミドルウェアを設定しても、静的ファイルへのリクエストでは実行されない状態でした。

解決策

run_worker_first: true を設定して、すべてのリクエストを Worker 経由にすることで解決します。

{
  "name": "my-frontend",
  "main": "index.js",
  "compatibility_date": "2024-01-01",
  "assets": {
    "directory": "./dist",
    "binding": "ASSETS",
    "run_worker_first": true
  }
}

この設定により、静的ファイルへのリクエストも必ず Worker コードを通るようになり、IP制限ミドルウェアが正しく動作します。

デバッグ方法

クライアントのIPアドレスを確認するためのデバッグ用エンドポイントを一時的に追加すると便利でした。

app.get('/debug/ip', (c) => {
  return c.json({
    clientIP: c.req.header('CF-Connecting-IP'),
    cfCountry: c.req.header('CF-IPCountry'),
  })
})

最終的な実装

wrangler.jsonc

{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "my-frontend",
  "main": "index.js",
  "compatibility_date": "2024-01-01",
  "assets": {
    "directory": "./dist",
    "binding": "ASSETS",
    "html_handling": "auto-trailing-slash",
    "not_found_handling": "single-page-application",
    "run_worker_first": true
  }
}

index.js

import { Hono } from 'hono'
import { ipRestriction } from 'hono/ip-restriction'
import { getConnInfo } from 'hono/cloudflare-workers'

const app = new Hono()

// IP制限ミドルウェア
app.use(
  '*',
  ipRestriction(getConnInfo, {
    denyList: [],
    allowList:[
      '192.0.2.0',
      '192.0.2.1',
    ],
  })
)

// 静的ファイル配信
app.all('*', async (c) => {
  return c.env.ASSETS.fetch(c.req.raw)
})

export default app

まとめ

Cloudflare Workers + Static Assets でIP制限を実装する際の重要なポイント:
run_worker_first: true を設定する - これがないと静的ファイルは Worker を経由しないです。

公式ドキュメントをまず確認しようというお話でした。
この記事が誰かのお役に立てば幸いです。

参考リンク