Cloudflare Accessを使って、Pagesにアップしたサイトを保護する話
これは、信州大学 kstm Advent Calendar 2024 4日目の記事です。
はじめに
当記事は、Cloudflare PagesにCloudflareの提供するCloudflare Accessという簡単で安全なユーザーアクセスを提供するサービスを利用することで、デプロイするページに対してアクセス制御を行うことを目的としています。
今回、デプロイするサイトを作成するためにNext.jsを利用しました。
Cloudflare Pagesとは
Cloudflare Pagesとは、Cloudflareの提供するWebサイトのホスティングサービスです。
類似のサービスとしてGithub Pagesなどがありますが、データ転送量制限の制限などがあるため、今回Cloudflare Pagesを利用しました。また、Cloudflare Pagesの場合Server Side Renderingなど一部のNext.jsの機能を利用することができます。
Next.jsをCloudflare Pages上に作成する
Cloudflare PagesにデプロイするNext.jsのページを作成するにはcreate-cloudflare CLI(C3)を利用します。
npm create cloudflare@latest -- my-next-app --framework=next
また、cloudflare pages上でNext.jsを利用する場合edge runtimeを利用する必要があります。
export const runtime = "edge";
詳しい手段については、こちらを参照してください。
APIルートを作成する
また、APIルートの作成にはHonoを利用しました。
import { Hono } from "hono";
import { handle } from "hono/vercel";
export const runtime = "edge";
//DB(D1)にアクセスするため記述しています。特に触らない場合必要ありません。
type Bindings = {
DB: D1Database;
};
// basePath は API ルートのベースパスを指定します
// 以降、新たに生やす API ルートはこのパスを基準に追加されます
const app = new Hono<{ Bindings: Bindings }>().basePath("/api");
app.get('/', (c) => c.text('Hono!'))
export const GET = handle(app);
export const POST = handle(app);
Next.js上において、apiルートをhonoで記述する場合VercelのAdapterを利用することで実現できます。
今回のコード例においては、最後の2文がこれに当たります。
デプロイの方法
Gitレポジトリの連携や、CLIからのデプロイなどの方法があるためそれぞれの用途に応じて設定します。
今回はGitレポジトリと連携することで、GitHubにpushした際に自動的にデプロイする形としました、
Cloudflare Accessを利用したアクセス制御
Pagesにデプロイすることができたら、次にCloudflare Accessを利用したアクセス制御を行います。
こちらを利用する理由として、まだ作成途中のアプリケーションなどに対して開発者のみアクセスすることができるようにしたりすることにより、インターネット上にプレビューを公開しながらもアクセスを許可されたものだけが閲覧することができます。
Cloudflare PagesでAccessを有効にするには、Pagesのプロジェクトに移動し、Settings(設定)タブに移動したのち、General(一般)
> Access policy(Access ポリシー)
を有効にします。
有効にしたのち、manage(管理)
をクリックすることで、CloudflareのZero Trustのページに移動するため、こちらでAccessの設定を行います。
Accessのタブにおいて、プロジェクト名 - Cloudflare Pages
というapplicationが作成されているのが確認できると思います。
Githubとの連携の設定
今回はGithub organizationのメンバー全員に対してアクセス権を付与することを想定します。
Settings > Authentication > Login methods > Add newに移動し、App IDの設定などを行います。
これにより、ログインの方法としてGitHubを利用することができるようになりました。
他者のアクセスを許可する
Access > Applicationをクリックし、Configurateを押すことでPolicyタブに移動すると思います。
まず、初期設定ではプレビュー用の*.example.com
のみが保護されているため、example.com
についても保護するため、domainを追加します、カスタムドメインを利用している場合もこちらに追加してください。
次に、Add policyをおし、Policyの設定を行います。
Policy name | Actions | Session duration |
---|---|---|
任意の名前 | Allow | 必要に応じて(できるだけ短いほうが望ましい) |
Add Policy
をおし、Configure rulesにおいて、SelectorにおいてGithub Organization
を選択し、GitHub organization name
を入力します。
その後、Add policy
を押して設定を保存します。
そして、AuthonicationタブからGithubをクリックすることで、当アプリケーションにおいて、Githubによる認証を有効にすることができました。
React Server Component(RSC)からのAPIアクセスについて
従来ではデータベースへのアクセスなどについては隠ぺいのため、/api配下においてアクセスなどを行ってきました。
しかし、RSCの登場により、DBのアクセスについてもデータを取得したのち、コンポーネントを作成してからクライアントに返却するため、コンポーネントと同一コードにおいてアクセスすることができるようになりました。また、/apiにserver sideからのアクセスについてはパフォーマンスの低下などのデメリットがあるため、必要がなければRSC内で直接データを取得することが望まれます。
しかし、RSC以前のNext.jsからの以降の最中やDBのアクセスロジックについて/apiにまとめておきたいなどの場合、RSCから自分自身にfetchを行うことがあるかもしれません。
const headersData = headers();
const host = headersData.get("host");
if (!host) {
throw new Error("Host header is missing");
}
const protocol =
headersData.get("x-forwarded-proto") ??
(host.startsWith("localhost") ? "http" : "https");
const apiBase = `${protocol}://${host}`;
Next.jsのRSCにおいて自分自身にアクセスするには絶対パスのurlが必要になるため上記のようなコードでアクセスを行うことになります。
しかし、上記コードからのアクセスについては先ほど実装したCloudflare Accessよって制限されているため、アクセスできません。
そこで、Service Authという機能を利用することでアクセスできるようになります。
Service Authについて、Access > Service Authに移動し、Create Service Tokenで新規Tokenを作成します。
そのまま進み、CF_ACCESS_CLIENT_IDとCF_ACCESS_CLIENT_SECRETを取得します。
そののち下記コードのようにHeaderに追加し、fetchの際にheaderを付与することで、RSCからもapi routeにアクセスすることができるようになりました。
const appendHeaders = new Headers();
appendHeaders.append(
"CF-Access-Client-Id",
process.env.CF_ACCESS_CLIENT_ID as string,
);
appendHeaders.append(
"CF-Access-Client-Secret",
process.env.CF_ACCESS_CLIENT_SECRET as string,
);
参考文献
Discussion