Dockerでlocalhostにhttpsアクセスできるプロキシを立てる
OAuthが絡むアプリケーションを開発していると、時折 https://localhost/
でローカルのアプリケーションサーバーにアクセスをしたくなる状況があります。セキュリティ上、リダイレクト先URLの指定が必要で、かつhttpsしか設定できない場合があるためです。
ローカルにプロキシサーバーを立てて解決する 手法がありますが、調べてみた限り設定が必要で、かつ手元の環境を汚してしまうものでした。(部屋はともかく)PC内環境美化に力を入れている私としては、何とか余計なインストールは避けたいところ。
そこで、Dockerを使ってSSL Terminationをするプロキシを作成しました。環境を汚さないだけでなく、 初回は30秒 程度、二回目以降は 1秒 程度と高速にセットアップすることが可能です。GitHubおよびDockerHubで公開していますので、需要がある方はご利用ください。
使い方は簡単ですが、IPの取得箇所はOSに依存しています。ご利用の環境に併せて叩き分けてください。下記のコマンドは、ポート3000番で動いているローカルアプリケーションへのプロキシを作成する場合です。
Mac OSX
docker run -it \
-e "HOST_IP=`ipconfig getifaddr en0`" \
-e "PORT=3000" \
-p 443:443 \
--rm \
esplo/docker-local-ssl-termination-proxy
Linux
docker run -it \
-e "HOST_IP=`hostname -I | awk '{print $1}'`" \
-e "PORT=3000" \
-p 443:443 \
--rm \
esplo/docker-local-ssl-termination-proxy
Windowsは?
Pull Request、お待ちしております。
以下はこのDockerイメージを作った原因や、どのようにできているかの説明です。
簡単にはhttpsでアクセスできない
ローカルサーバーにhttpsでアクセスできるようにするには、証明書を作ったり、https用の設定をしてあげないといけません。一般的な開発用サーバーはhttpで立ち上がるため、この手順は手間です。
NginxやApacheでプロキシを立てて、localhostにhttpsでアクセス
localhostにhttpsでアクセスしたい時、よく使われるのは「ホストマシンに(直に)NginxやApacheを立てる」という方法です。もちろん実現できますが、このためだけにNginxをインストールする必要があったり、手順が複雑だったり、起動しているかどうか分からなかったりします。Dockerが無い頃は私もよくやってました。
このような課題は、Dockerを使ってブラックボックスにすることで上手く解決できます。しかも起動が高速で分かりやすいため、開発意欲を削がれずに済みます。
Docker内で立てたNginxプロキシの問題点
問題となるのは、 Nginxの設定ファイルに、如何にしてホストのIPを埋め込むか という点です。
1点目の厄介は(Dockerコンテナからホストネットワークへアクセスするための)IPの取得です。Docker for Macでは、ネットワークモードが host
だとホストマシンからlocalhostでアクセスができません[1]。結果、 bridge
モードでホストのIPを渡す、という手法を取る必要があります。ホストIPはDockerコンテナ内からは取得できないため、起動時に環境変数として渡します。
2点目の厄介は、Nginxの設定ファイルは環境変数を使うように出来ていない、ということです。ありがたいことに、こちらのドキュメントでenvsubstコマンドを使った方法について解説されています。
この方法を利用して環境変数を埋め込みます。他の点に関しては、普通にホストローカルにプロキシサーバーを立てるのと変わりません。
コードの解説
非常にシンプルなので不要な気もしますが、リポジトリのファイルに沿って流れを説明します。
普通のDockerfile
Nginxを使った、普通のSSL Termination Proxyです。
Nginxの設定ファイルは、image内ではまだテンプレートです。
実際の起動時に entrypoint.sh
を呼び出すことで、 envsubst
により環境変数を置換して本来の設定ファイルになります。
nginx.confでは環境変数名を書いておく
nginx.conf
では以下のように、環境変数混じりの指定をしています。
この部分が entrypoint.sh
で置換されます。実際の設定は極めて単純で、443から指定のポートに転送をするだけです。
http {
...
upstream app {
server ${HOST_IP}:${PORT};
envsubstを使ってnginx.confを置換する
entrypoint.sh
でenvsubstを使って置換し、その設定ファイルを使ってNginxを起動します。
envsubst '$$PORT$$HOST_IP' < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf
nginx
まとめ
簡単に https://localhost/
にアクセスするためのDockerイメージを作成しました。これを使って労力を抑えて、本来の目的であるアプリケーション開発を楽しんでください。
MacとLinuxで共通してIPを取得するコマンド、ご存知の方はPRください!
未検証ですが、Linuxなら可能だと思います ↩︎