Google Cloudで WebページをStorageに置いてBasic認証のような簡易認証をつけて表示する
夏のブログリレー企画
この記事は『blessing software 夏のブログリレー企画』の2日目の記事です。
昨日はKanonさん@samurai_se の「Kotlinのプロパティ初期化処理、きちんと使い分けできてますか?」が公開されました。
明日はGANGANさん@gangan_nikki の「ESLintモジュールを用いてNuxt3 + ESLint設定を行う」についての記事です!お楽しみに!
はじめに
Google Cloud(GCP)はGoogleが作ったクラウドサーバです。
Google Cloudでホームページは簡単に公開できます。
サーバレスで構築するのも簡単です。
しかし、「basic認証のようなPWで閲覧者を制限する & Webファイルはストレージに置く」というのは意外と難しい。
求める要件は以下
・ID/PWで閲覧者を制限する
・好きな時にID/PW制限を解除して公開状態にもできる
・サーバレス
・ウェブファイルはCloud Storageに配置し、一般公開はしない
AWSだとCDNで認証ヘッダーを確認し、認証がない場合は認証を促すプログラムを組み込めます。
似たような仕組みをGoogle Cloudでも
ロードバランサーで署名付きCookieを確認
->認証していない場合は証を促すプログラムを実行し、ID・PWが合えば署名付きCookieを発行
->署名付きCookieがあればCDNで非公開のストレージを閲覧を許可
することで実現できます。
やり方はこの記事↓です
Googleの人が書いた記事です。これに沿って進めていきます。基本構成
Cloud Storageにウェブファイルを置き、ロードバランサー経由で公開します。
- 非公開状態のCloud Storage(ウェブファイルを保存)
- ロードバランサー(署名付きCookieの有無でサーバを振り分け)
- CDN(署名付きCookieがあるとき、Cloud Storageのファイルを表示)
- Cloud Run(ID/PW認証画面を表示し、認証時に署名付きCookieを発行)
通常のWebサーバを構築
以下の順で進めます。
- 公開状態の通常のWebサーバを構築
- 認証機能をつけて閲覧者を制限
まず、通常のWebサーバを構築します。
Cloud Storageを作る
webファイルを置くストレージを作ります。
バケット作成
適当なバケット名で作成します。
公開できるように「このバケットに対する公開アクセス禁止を適用する」のチェックは外します。
公開設定
公開できるか確認のため一旦、公開設定します
「Cloud Storage」のバケットに「allUsers」で「Storageオブジェクト閲覧者」権限を追加します。
web用設定
web用設定で「index.html」をデフォルトで見るように設定
- 「インデックス(メイン)ページ サフィックス」に"index.html"を設定
- SPA(シングルページアプリケーション)にする時は、「エラーページ」にも"index.html"を設定
ロードバランサーを作る
「ロードバランサーを作成」->「アプリケーション ロードバランサ(HTTP/S)」
"ロードバランサー"の種類は↓です
「フロントエンドの構成」で適当なインターネット側との口を作ります
「バックエンドの構成」で先ほど作ったバケット紐づけます。
ここでつけた「バックエンドバケット名」は後で使います。
一旦、設定は完了なので「ロードバランサー」を作成してください。
「ロードバランサー」の「フロントエンドの構成」のIPは(ドメインを取得した)DNSに設定してWebページを公開します。
Storageに適当なファイルを上げて公開されていることを確認しましょう。
認証機能を付ける
通常のWebを表示するまでは構築しました。
ここから、ID/PWの認証を付けて見れる人を制限します。
認証サーバを作る
元記事にある認証サーバを立てます。
「cloud shell」を立ち上げ
「エディター」を開きます
元記事にあるソースをクローン
# クローン
git clone https://github.com/GoogleCloudPlatform/community.git
様々なチュートリアルが入っていますが、
今回使用するソースを"ワークスペース"で開きます。
"community/archived/securing-gcs-static-website/flask_login"フォルダを右クリックして、「Open as Workspace」を選択
どうも今のソースはライブラリのバージョンに問題があるみたいなので、 requirements.txt
を古いバージョンに戻しておきます。
click==7.1.2
Flask==1.1.2
itsdangerous==1.1.0
Jinja2==2.11.2
MarkupSafe==1.1.1
Werkzeug==1.0.1
google-cloud-secret-manager==1.0.0
デプロイ画面を表示
1.別のプロジェクトに入ってないか確認。(プロジェクトが違った場合は、ここから変更)
2.エディターを立ち上げ
3.デプロイ用の画面を表示
1.Cloud Runのサーバ名をわかりやすいものを付ける
2.サーバの場所
3.サーバのソースの保存場所と名前。(日本にサーバを置くなら、"asia."を先頭に付けよう。アジア圏に保存されて起動が少し早くなる)
デプロイする。
Cloud Storageの権限変更
ストレージの一般公開アクセスを削除して、非公開上にする
バケットに移動して、「権限」から"all user"を削除
ロードバランサーを編集
ロードバランサーで以下の設定を行います
- 通信先に認証サーバ(Cloud Run)の追加
- 署名付きCookieで通信するCDNの追加
- 署名付きCookieの有無でルーティング設定
1.通信先に認証サーバ(Cloud Run)の追加
ロードバランサーの「編集」から「バックエンドの構成」を変更します。
バックエンドサービスとして、上で作った認証サーバ(この記事だと、"flask-login")を追加します。
2.署名付きCookieで通信するCDNの追加
CDNを追加します。
追加するためには、対象のストレージを編集します。
CDNのチェックを入れます。
今回は、キャッシュの必要はないので、キャッシュは無効(「常に再検証」)にします。
署名付き Cookieのキーを登録
「高度な構成」から「署名付き URL と署名付き Cookie を使用してアクセスを制限する」を選択し「署名鍵」を追加します
"cdn-auth-key"など適当な名前で保存し、キーを払い出します。
出力された「署名鍵の値」は後で使うのでメモしておいてください。
「キャッシュ期間」も"7日間"程度に伸ばしておきます。
署名付きCookieの有無でルーティング設定
「バックエンド サービス名」と「バックエンド バケット名」を使うのでメモしておいてください。
また、「プロジェクト ID」が必要です。
「プロジェクト ID」はTOPページを見るか、コマンドでも取得できます。
echo ${GOOGLE_CLOUD_PROJECT}
今回使うルーティングはGUIがまだないのでYAML設定を書く必要あります。
といっても難しくありません。後で認証が不要になればGUIの表示に戻せます。
「ルーティング ルール」を「詳細なホストとパスのルール」に「ホストとパスのルールを追加」
「ホスト名」に"*"を入れます。
下のYAML
${PROJECT_ID}
に「プロジェクト ID」
${BACKEND_BUCKET_NAME}
「バックエンド バケット名」
${BACKEND_CLOUD_RUN_NAME}
「Cloud Runのバックエンド名」
で置き換えて、「パスマッチャー」に記載します
defaultService: projects/${PROJECT_ID}/global/backendBuckets/${BACKEND_BUCKET_NAME}
name: path-matcher-1
routeRules:
- matchRules:
- headerMatches:
- headerName: cookie
prefixMatch: Cloud-CDN-Cookie
prefixMatch: /
priority: 0
service: projects/${PROJECT_ID}/global/backendBuckets/${BACKEND_BUCKET_NAME}
- matchRules:
- prefixMatch: /
priority: 1
service: projects/${PROJECT_ID}/global/backendServices/${BACKEND_CLOUD_RUN_NAME}
例えば、
${PROJECT_ID}
:hoge-project
${BACKEND_BUCKET_NAME}
:hoge-be-bucket
${BACKEND_CLOUD_RUN_NAME}
:flask-login-bes
だと、以下になります。
defaultService: projects/hoge-project/global/backendBuckets/hoge-be-bucket
name: path-matcher-1
routeRules:
- matchRules:
- headerMatches:
- headerName: cookie
prefixMatch: Cloud-CDN-Cookie
prefixMatch: /
priority: 0
service: projects/hoge-project/global/backendBuckets/hoge-be-bucket
- matchRules:
- prefixMatch: /
priority: 1
service: projects/hoge-project/global/backendServices/flask-login-bes
Secret ManagerにCDNの「署名鍵」を保存
メニューから「セキュリティ」->「Secret Manager」を開きます。
「シークレットの作成」から
"cdn-auth-key"など適当な名前ををつけます。この名前はCloud Runの設定で使います。
「シークレットの値」はCDNで出力した「署名鍵の値」を保存します。
認証サーバ(Cloud Run)を設定
認証サーバ(Cloud Run)にシークレットマネージャーにアクセスする権限を付与します
「cloud shell」で以下のコマンドを流します
export PROJECT_NUM=$(gcloud projects describe ${GOOGLE_CLOUD_PROJECT} --format="value(projectNumber)")
gcloud projects add-iam-policy-binding \
--member=serviceAccount:${PROJECT_NUM}-compute@developer.gserviceaccount.com \
--role=roles/secretmanager.secretAccessor ${GOOGLE_CLOUD_PROJECT}
署名付きCookieの値やID・PWを設定します
Cloud Runに移動して、"認証サーバ"を編集します。
環境変数で以下の値を入れます。
値の部分をご自身の環境にあう値を入れてください。
WEB_URL:(WEBのURL、"http"から入力)
PROJECT_ID:(「プロジェクト ID」、TOPページに記載あり)
CDN_SIGN_KEY:(「Secret Manager」に登録した名前)
USER_NAME:(認証時に入力を求めるログインID)
USER_PASSWORD:(認証時に入力を求めるログインパスワード)
入力例↓
CDNからCloud Storage の非公開データに閲覧権限を与える
「cloud shell」を立ち上げ
下のコマンドを実行する。
[BUCKET_NAME]
を実際にアクセスする「バケット名」に置き換える。
export PROJECT_NUM=$(gcloud projects describe ${GOOGLE_CLOUD_PROJECT} --format="value(projectNumber)")
# 権限の変更
gsutil iam ch \
serviceAccount:service-${PROJECT_NUM}@cloud-cdn-fill.iam.gserviceaccount.com:objectViewer gs://[BUCKET_NAME]
今回の例だと、バケット名は"hoge-com"なので、下のようになる。
export PROJECT_NUM=$(gcloud projects describe ${GOOGLE_CLOUD_PROJECT} --format="value(projectNumber)")
gsutil iam ch \
serviceAccount:service-${PROJECT_NUM}@cloud-cdn-fill.iam.gserviceaccount.com:objectViewer gs://hoge-com
Storageの設定を変更したとき(?)に、ここで付与した"非公開データに閲覧権限"が外れるときがあるので、閲覧できないときはコマンドを実行して付与しなおします。
完成
WEBにアクセスして、認証画面が出ましたか?
ID/PWを入力して、画面が表示できることを確認しましょう。
お疲れ様でした!!
おわり
作業が難しいわけではありませんが、もう少しサックとできるようになってほしいですね。
Edge CDNみたないなのが追加されると良いのですが、他のクラウドも微妙な状況なので現状は仕方ないのかもしれません。
Cloud Load BalancerにWASMによるEdge処理が来るみたいです。
早く来てほしいです。
あと、Basic認証とタイトル入れましたが、認証が画面があまり似てません。
時間を見つけて、その辺に手をいれたいです。
余談ですが、Google Cloudの機能の一つとして Firebase があります。
Firebaseを使って、Basic認証をつける方法もあります。
Discussion