あらすじ
うーむ。何か変わったことをやりたいな・・・
ということで思いついたのが
- GitHub Pagesにフロントエンド(React/TypeScript)
- 自宅鯖にバックエンド (warp/Rust)
という構成のサービス。
基本的なアイデア:
バックエンドサービスは基本的にはGET系のAPIのみを提供し、
POST系の処理はプライベートネットワークからのみアクセス可能とする。
GETだけならトークンもいらんだろうしGitHub Pages(無料版)でもええか?
ということで、さっそく作ってみたのですが・・・
落とし穴1: Mixed Contentの禁止
Google ChormeではMixed Contentが禁止されています。
これは、HTTPSで提供されているサービス中にHTTPで提供されるコンテンツが混じっているような状況のことなのですが、ここでは、次のような状況になっていました。
- GitHub PagesはTLSを使用(httpsでアクセス)
- 用意したバックエンドは80番ポート(http)で公開
当然、API呼び出しがブロックされることに。簡単な回避方法も見つからなかったのでひとまずLet's EncryptでTLSを導入することに。
Let's EncryptでのTLSの導入について以前書いた記事
また、この時のドメインについては、いつもの無料DDNSサービス
DDNS Nowさんを使わせていただきました。
落とし穴2: ERR_CERT_COMMON_NAME_INVALID
443ポートの解放し忘れなど初歩的なミスを犯しながらなんとか当初の目的であった
GitHub Pages上での動作確認に成功したのですが、ここで一つ問題が。
フロントエンドに変更を入れた後、デプロイ前にローカルで動作を確認したかったのですが、確認のためにブラウザを開いているマシンとバックエンドサービスをホストしているマシンのグローバルIPアドレスが同じなために、ルータのポートフォワーディングが正しく機能しないことが判明。
具体的には、ブラウザ側からURLに向けてリクエストが投げられると、
- URLからグローバルIPアドレスにDDNSが変換
- それを使ってルータに到達したパケットは送り先がルータ内のネットワーク
- ポートフォワーディングの代わりにルータの設定画面を返す
という流れに入ってしまうんですね。
これは同じことはGitHub Pages上の本番フロントエンドを開いた時にも言えるのですが、こちらはひとまず置いておくとして、少なくとも開発時には通常のおうちインターネットで結果の確認ができないと困ってしまうわけです。
とはいえ、同一ネットワークにはいれるのであれば、ポートフォワーディングが機能しなくとも単にプライベートアドレスでAPIにアクセスするように開発時のコードを修正してあげれば・・・
ERR_CERT_COMMON_NAME_INVALID
・・・はい、ダメなんですね。証明書にはしっかりとホスト名が書いてあるので、IPアドレスでアクセスしようとすると、そこが一致しないために不正な証明書であるとしてはじかれてしまいます。
いろいろ対処法はあると思うのですが、ひとまず今回は、バックエンド側にルーティングの処理を追加して、httpでのアクセスもできるようにしました。そのうえでフロントエンド側では、開発時のみhttpで直接プライベートアドレスにAPIリクエストを投げるようにしてあげたわけですね。
まとめ
- Mixed ContentではじかれるのでAPI側は面倒でもTLS対応しよう
- 今はLet's Encrypt君がいるのでそんなに面倒くさくないよ
- 何らかの事情で証明書エラーをバイパスしたいときにはいっそのこと開発時のみと割り切ってhttpアクセスも許しちゃうのも手かも?
あとがき
- ネットワーク・インフラ何もわからない日曜エンジニアなので、「セキュリティ的にお前かなり危ないことやってるよ」とかあったらこっそり教えてください
成果物