Cloud Run の IAP による高度な認証を設定したサービスが CORS ポリシーに対応できるか試してみた
はじめに
Cloud Run 用に Identity-Aware Proxy (IAP) を構成する がプレビュー (Pre-GA)となりました。従来は Cloud Run で IAP によって認証するにはロードバランサーを構築する必要がありましたが、今回のアップデートにより Cloud Run 単体で IAP を利用できるようになりました。
本記事では、以下のような条件で Cloud Run 用の IAP を構成した結果、上手く動作するのか試したものです。
条件
- 異なるドメインからリクエストされる Cloud Run サービス
- CORS (Cross-Origin Resource Sharing) ポリシー に準拠する必要がある
試した結果
結論から申し上げますと、IAP の認証を通った後であれば Cloud Run サービスのプログラムで CORS ポリシーに対応することでレスポンスを正常に取得することができました。
ただ先に認証を通す必要があることと、サービスへリクエストする側が認証情報をサービスに渡す必要がありました。
検証内容
概要
以下の図のような構成で検証を行います。
- Cloud Storage で静的なウェブサイトをホスティングする方法で設置した HTML から Cloud Run サービスをリクエストします
- Cloud Run 用の Identity-Aware Proxy (IAP) を構成します
- 静的なウェブサイト と Cloud Run は異なるドメインのため、CORS ポリシーに対応する必要があります
Cloud Run (Functions) サービスの作成
先ずは以下のようなプログラムで Cloud Run Functions サービスを IAP を設定していない状態にて作成します。
from flask import Flask, jsonify, request
from flask_cors import CORS
import werkzeug.datastructures
import json
import os
app = Flask(__name__)
# --- CORS 設定 ---
# 警告: 本番環境では '*' (すべてのオリジン) を許可するのはセキュリティリスクとなる可能性があります。
# 実際のフロントエンドのドメインに置き換えるか、特定のオリジンをリストで指定することを強く推奨します。
#
# 例: 特定のオリジンからのアクセスを許可し、認証情報(クッキーなど)も許可する場合
# これは、IAP を利用するような、認証情報を含むクロスオリジンリクエストで推奨される設定です。
# IAP がセッションクッキーを使用するため、フロントエンドからのリクエストにも 'credentials: "include"' が必要です。
# 'https://your-frontend-domain.com' を、この Cloud Run サービスにアクセスするフロントエンドのドメインに置き換えてください。
CORS(
app,
resources={r"/api/*": {
"origins": ["https://your-frontend-domain.com", "http://localhost:3000"], # 許可するオリジンのリスト
"supports_credentials": True, # クッキーなどの認証情報を含むリクエストを許可
"methods": ["GET", "POST", "OPTIONS"], # 許可するHTTPメソッド
"allow_headers": ["Content-Type", "Authorization"] # 許可するリクエストヘッダー
}}
)
@app.route('/')
def home():
"""ルートパスへのアクセス."""
return "Cloud Run Service with CORS configured. Access /api/data."
@app.route('/api/data', methods=['GET', 'POST'])
def handle_data():
"""クロスオリジンからのアクセスを想定する API エンドポイント."""
if request.method == 'GET':
return jsonify({"message": "Hello from Cloud Run! (GET request)", "source": "cloud-run-cors-example"})
elif request.method == 'POST':
try:
received_data = request.json
return jsonify({"message": "Data received via POST!", "received_data": received_data, "source": "cloud-run-cors-example"}), 200
except Exception as e:
return jsonify({"error": f"Invalid JSON or request error: {e}"}), 400
return "Method not allowed for this endpoint.", 405
def main(req):
"""Cloud Functionのエントリポイント"""
with app.app_context():
headers = werkzeug.datastructures.Headers()
for key, value in req.headers.items():
headers.add(key, value)
request_data = req.get_data(as_text=True)
json_data_main = None
form_data = None
if request.content_type == 'application/json' and request_data:
try:
json_data_main = json.loads(request_data)
except json.JSONDecodeError:
print("Warning: Invalid JSON data received in main function.")
else:
form_data = request.form
with app.test_request_context(
method=req.method,
base_url=req.base_url,
path=req.path,
query_string=req.query_string,
headers=headers,
data=form_data,
json=json_data_main
):
try:
rv = app.preprocess_request()
if rv is None:
rv = app.dispatch_request()
except Exception as e:
rv = app.handle_user_exception(e)
response = app.make_response(rv)
return app.process_response(response)
Flask
Werkzeug
flask-cors
- 第2世代となった Functions は、Cloud Run 同様に IAP の設定が可能になっています
スクリーンショット
- プログラムでは CORS ポリシーに対応するため、
https://storage.googleapis.com
を許可するようにします
動作確認
ブラウザから URL にリクエストを送信して、レスポンスを受けます。
Cloud Run サービスの設定
コンソールから以下の箇所にチェックを付けて、IAP をオンにします。
- コマンドで Cloud Run の IAP をオンにするには、以下のように
--iap
を付けます
gcloud alpha run services update iap-test --region=asia-northeast2 --iap
- 新規で Cloud Run をデプロイする際に IAP をオンにする場合は、以下のようになります
gcloud alpha run deploy iap-test \
--image=[your-image] \
--region=asia-northeast2 \
--iap
動作確認
ブラウザから URL にリクエストを送信すると、アクセスが拒否されます。
IAP のアクセス制御
IAP によるアクセス制御を CloudShell から以下のコマンドにて設定します。今回は、ドメインで認証できたユーザーへのアクセスを許可します。
gcloud alpha iap web add-iam-policy-binding \
--member=domain:rescuenow.co.jp \
--role=roles/iap.httpsResourceAccessor \
--region asia-northeast2 \
--resource-type=cloud-run \
--service=iap-test
設定の反映を確認
「Identity-Aware Proxy(IAP)による高度な認証」欄にある、「ポリシーの編集」をクリックすると以下のような画面が表示されて設定を確認できます。
動作確認
しばらくすると設定が反映され、レスポンスを受けることができます。
Cloud Run をリクエストするページ
以下の HTML を Cloud Storage にアップロードします。
<!DOCTYPE html>
<html>
<head>
<title>CORS Test Frontend</title>
</head>
<body>
<h1>CORS Test Frontend</h1>
<button onclick="fetchData()">Fetch Data from Cloud Run</button>
<button onclick="postData()">Post Data to Cloud Run</button>
<pre id="response"></pre>
<script>
const cloudRunServiceUrl = "https://YOUR_CLOUD_RUN_SERVICE_URL.a.run.app"; // ★あなたのCloud RunサービスのURLに置き換える
async function fetchData() {
try {
// credentials: "include" はIAPなどで認証情報(クッキーなど)を使う場合に必要
const response = await fetch(`${cloudRunServiceUrl}/api/data`, {
method: 'GET',
credentials: 'include'
});
const data = await response.json();
document.getElementById('response').textContent = JSON.stringify(data, null, 2);
} catch (error) {
document.getElementById('response').textContent = `Error: ${error.message}`;
console.error('Fetch error:', error);
}
}
async function postData() {
try {
const response = await fetch(`${cloudRunServiceUrl}/api/data`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
credentials: 'include', // IAPなどで認証情報(クッキーなど)を使う場合に必要
body: JSON.stringify({ message: "Hello from frontend!" })
});
const data = await response.json();
document.getElementById('response').textContent = JSON.stringify(data, null, 2);
} catch (error) {
document.getElementById('response').textContent = `Error: ${error.message}`;
console.error('Fetch error:', error);
}
}
</script>
</body>
</html>
- cloudRunServiceUrl を作成した Cloud Run の URL(
.run.app
まで) に置き換えます - リクエスト(
fetch
メソッド)に、credentials: 'include’
パラメータを含めて IAP へクライアントの認証情報を渡す必要があります
動作確認
Cloud Storage で公開された URL にリクエストを送信して、レスポンスを受け取れることを確認します。
Cloud Run サービスのプログラムで CORS ポリシーに対応していない場合
CORS ポリシー違反となります。
HTML のパラメータが不足している場合
リクエスト(fetch
メソッド)に、credentials: 'include’
パラメータを含めていない場合、IAP へクライアントの認証情報が渡らないためアクセス拒否されます。(oauth2 の認証エラーが出ます)
その他
プリフライトリクエスト (Preflight Request) について
IAP では、プリフライトリクエストの送信によって拒否される場合がある とあります。今回は特に対策の必要がありませんでしたが、クライアントの振る舞いによっては検討が必要となります。
おわりに
今回は、Cloud Run の IAP による高度な認証を設定したサービスが CORS ポリシーに対応できるか試したことをまとめました。
検証のきっかけは、既存環境に従来の方法で IAP を利用して異なるドメインからリクエストされる Cloud Run サービスがあったので、機能の移行を検討するためでした。
本当はドメイン マッピングの機能も組み合わせてみたかったのですが、まだ実施できていません。また今後、試してみたいと思います。
Discussion