Cloudflare API Shield を JWT 門番にする
Cloudflare の API Shield では mTLS での認証のほかにも JWT 検証の機能があるということで、その動作確認を行います。
手っ取り早く、下記の手順で行います。
-
/login
エンドポイントは Cloudflare Access の Service Token で認証し、認可も OK なら JWT を返す(認証認可されなければブロック) -
/private
エンドポイントは JWT を Cloudflare API Shield で検証する(検証できなければブロック) - WAF Custom Rules でこれら以外のパスはブロック
今回 1 は JWT 取るためのオマケで(他の環境のほうがより現実的と思います)、2 が本編です。
1. Access で認証認可、 JWT 入手
Access に認証認可をさせ、JWT を渡すことができます。
ここでは、あらかじめ作成した Service Token(ID と Secret)を HTTP リクエストに含んで認証する方法を使ってみます。
Service Token を作る
1-1. Access
Self-hosted アプリを /login
で定義し、Service Auth のポリシーを付与
1-2. Access Applications の Service Auth rules enforce authentication flows that do not require an identity provider IdP login, such as service tokens and mutual TLS.
Service Token の ID と Secret をヘッダーにつけて /login
に接続すると、認証認可が成功し、 CF_Authorization
Cookie に JWT が格納されました。
$ curl "https://api0.oymk.work/login" -H "CF-Access-Client-Id: $CID" -H "CF-Access-Client-Secret: $SCR" -c access.cookie
$ cat access.cookie
#HttpOnly_api0.oymk.work FALSE / TRUE 1733131099 CF_Authorization eyJ....
2. API Shield で JWT を検証
一方 /private
では Access で署名された適正な JWT がないと接続できないようにします。
これを API Shield でやってみます。
JWK の入手
2-1.Cloudflare Access の場合は https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/certs
で公開されます。
一世代前と現行の鍵が表示されるので現行(public_cert
の kid
)に合致する key
を取ります。
As shown in the example below, https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/certs contains two public keys: the current key used to sign all new tokens, and the previous key that has been rotated out.
keys: both keys in JWK format
public_cert: current key in PEM format
public_certs: both keys in PEM format
API Shield で JWT 検証設定
2-2.JWT の格納場所は Cookie あるいはヘッダーを指定できます(2024年12月時点)。
今回はヘッダーを利用してみました(cf_authorization_test という名前)。
Token Key
に 2-1 で入手した公開鍵の情報を貼り付け、追加します。
追加した鍵情報やエンドポイントを指定し、ルールを定義します。
Token がない場合には non-complient
扱いにし、その場合の Action は Block
としました。
2-3. API Shield 動作確認
まずば正当なアクセス。
200 OK が返ります。
# curl の cookie 出力から JWT 値を得る
$ TKN=`awk '/CF_Authorization/ {print $7}' access.cookie`
# 値をヘッダーに入れて送る
$ curl -v "https://api0.oymk.work/private" -H "cf_authorization_test: $TKN"
< HTTP/2 200
次に、ヘッダー無しで送ってみます。
$ curl -v "https://api0.oymk.work/private"
< HTTP/2 403
error code: 1020%
error code: 1020 という応答です。
Firewall Event を見ると API Shield - Token Validation でブロックされていることがわかります。
API Shield の公開鍵を一世代前に入れ替えてみます。
期待値は現行の鍵で署名された JWT がブロックされることです。
入れ替えると、即座にブロックされ始めました。
$ curl -v "https://api0.oymk.work/private" -H "cf_authorization_test: $TKN"
< HTTP/2 403
error code: 1020%
その他、検証の詳細や Cloudflare API からの操作は Dev docs が参考になります。
2-4. 鍵のローテーションに対応
運用に際しては、JWK のアップデートへの追随が重要になります。
(鍵は 4 つ登録できるので、実際の運用でのスムーズな移行が考慮されています)
API Shield での鍵ローテーションについては Workers を使って Cloudflare API から更新する例が紹介されていますので、他の ID プロバイダーでも流用できると思います。
おわりに
以上、Workers/Snippets や Access 背後のオリジンでの JWT 検証に加え API Shield でも簡素にきちんとシールドできることを確認できました。
ほかにも API Shiled の JWT 検証を利用して user クレームが admin のときにリクエストの WAF アタックスコアが低ければマネージドチャレンジを掛けるというような WAF Custom Rule の例もありましたので、ユースケースに柔軟に対応できそうです。
Discussion