Publishing Blog Draft Behind oauth2-proxy on Cloud Run

Hugo のような static site generator で公開しているブログのドラフトを友人など限られたユーザだけに公開したい。 Netflify Pro ($20/mo) ならパスワードつきサイトを公開できるが、そんなに費用を書けたくない。 そんなとき oauth2-proxyCloud Run で動かすと、限りなく小さな費用でブログドラフトを特定ユーザだけに公開できる。大枠の手順を以下で紹介する。

oauth2-proxy は Google や GitHub などの OAuth を任意の URL の前におけるプロキシサーバ。プロキシするだけでなく静的なコンテンツも返せるので、その機能を使って Hugo などの生成した HTML を配信する。

Cloud Run は Docker イメージを serverless で動かす環境。Serverless なので友人にブログのドラフトを読んでもらうだけならほぼ停止状態になり費用がかからない。そこで oauth2-proxy を動かしたい。

事前に必要なもの

  • Hugo などを使ったブログの Git レポジトリ
  • 適当な GCP のプロジェクト (新しく作るのが無難)

Dockerfile

適当な Hugo ブログの Git repo があるとして、そのトップレベルに以下のような Dockerfile を置く。

FROM alpine:3.13 AS build

WORKDIR /opt/draft-proxy-src
# Copy blog source 
COPY content ./content
COPY static  ./static
COPY themes  ./themes
# Binaries and config files
COPY third_party/hugo_0.89.2_Linux-64bit.tar.gz \
     third_party/oauth2-proxy-v7.2.0.linux-amd64.tar.gz \
     config.toml \
     users.csv \
     ./
# Extract oauth2-proxy. This is used by the next stage.
RUN tar xvf oauth2-proxy-v7.2.0.linux-amd64.tar.gz && \
    mv oauth2-proxy-v7.2.0.linux-amd64/oauth2-proxy .
# Extract Hugo and run it as draft mode (-F -D).
# -b sets the base URL, which is the Cloud Run endpoint.
RUN tar xvf hugo_0.89.2_Linux-64bit.tar.gz hugo && \
    ./hugo -D -F -b https://YOUR_DRAFT_DOMAIN/

FROM alpine:3.13 AS image
WORKDIR /opt/draft-proxy
# Copy generated content
COPY --from=build /opt/draft-proxy-src/public /opt/draft-proxy/public
# ... and required binary and its configuraiton file.
COPY --from=build /opt/draft-proxy-src/oauth2-proxy \
                  /opt/draft-proxy-src/users.csv \
                  /opt/draft-proxy/
# Start serving! 8080 is the default port for Cloud Run.
CMD ["./oauth2-proxy", \
     "--provider=google", \
     "--http-address=0.0.0.0:8080", \
     "--reverse-proxy=true", \
     "--authenticated-emails-file=/opt/draft-proxy/users.csv", \
     "--upstream=file:///opt/draft-proxy/public#/", \
     "--redirect-url=https://YOUR_DRAFT_DOMAIN/oauth2/callback", \
     "--client-id=XXXXX", \
     "--client-secret=YYYYY", \
     "--cookie-secret=XXXXX"]

必要なファイル:

  • Hugo ブログのソース (content, static, themes など).
  • Hugo と oauth-proxy のリリース tar ファイル (上の例では third_party/ 以下にコミットされている)
  • users.csv

イメージをビルドしてみる:

$ docker build -t draft-proxy:hello . && \
  docker run -p 8080:8080 --rm draft-proxy:hello

http://localhost:8080/ にアクセスすると oauth2-proxy の認証画面が表示される。

users.csv

users.csv ファイルには、ログインを許したいユーザの GMail アカウントが列挙されている。

user1@gmail.com
user2@gmail.com
user2@gmail.com

なお上の例では Google ログインを使っているが、GitHub など他の OAuth provider も利用できる。oauth2-proxy のドキュメント

oauth2-poxy に渡すフラグのうち --client-id--client-secret は OAuth provider から発行されたものを使う。--cookie-secret は適当に生成された ランダム文字列を使う。ドキュメンテーションでは以下のように生成している:

$ python -c 'import os,base64; print(base64.urlsafe_b64encode(os.urandom(32)).decode())'

Google 側で OAuth の認証画面を設定する。このドキュメント に従って GCP のコンソールでポチポチと設定する。

こうしたドラフトは “External User” 向けに公開する必要があり、そうした OAuth consent screen の作成にはレビューが必要なことになっている。ただし “Testing” モードにしてけばレビューは必要ないので、それがおすすめ。テストユーザには users.csv と同じアカウントを列挙すればよい。

Cloud Run の deploy

以下のコマンドでデプロイできる

$ gcloud builds submit --tag gcr.io/YOUR_GCP_PROJECT_ID/draft-proxy
$ gcloud run deploy draft-proxy --image gcr.io/YOUR_GCP_PROJECT_ID/draft-proxy --platform managed --allow-unauthenticated --region=us-west1

注意:

  • 事前に所定の GCP プロジェクトで gcloud init しておく
  • 様々な API が Enable されていない、しろと問い詰められるので、一つ一つ Enable していく。だいたい gcloud がよろしくやってくれるはず。

(Optional) Cloud Run のカスタムドメイン

Cloud Run は初期状態だと https://xxxx.yyy.run.app のような URL が割り振られる。 この URL は永続的なものなのでドラフトの URL としてそのまま使っても良いが、 もうちょっと気の利いたドメインを割り振る場合は以下のドキュメントに従い追加の設定をする。

注意:

  • 一部の region ではこの機能が使えない! Cloud Run を動かす region がドメイン割当に対応しているか、上のドキュメントで確認しておくと良い。
  • ぼんやり読むと Cloud Domains でドメインを買う/管理する必要があるように見えるが、他所で管理しているドメインも普通に使える。その場合 “domain verification” の手続きが必要。以下の資料を参照のうえ設定する:
  • これらのステップは DNS が関与するので待ち時間が長い。待ちましょう。

(Optional) CI の設定

変更をコミットして GitHub に push したら勝手に deploy してほしい。 Cloud Run のコンソール から デプロイした proxy サービスのページを開くと “SETUP CONTINUOUS DEPLOYMENT” というリンクがある。リンク先の指示に従いウェブからぽちぽちと設定すると CI が動くようになる。

注意:

  • ここでも様々な API が Enable されていない、しろと問い詰められるので、一つ一つ Enable していく。だいたい gcloud がよろしくやってくれるはず。
  • GitHub の認証が必要なので gcloud による CLI にこだわるよりはブラウザで済ませたほうがラク。