dockerのhttps-portalのSTAGE: localでハマった件について
概要
https通信を簡単に実現できるhttps-portalの存在を知り、試してみようと思ったのですが、公式ページのドキュメントに書いてある docker-compose.yml
ではうまくいきませんでした。どのようなエラーが出て、どうすれば回避できるのかについてまとめます。
https-portalとは
docker環境で構築したアプリケーションをデプロイする際に、https通信にする必要があります。それをdockerの内側だけで実現してくれるイメージです。このイメージの特徴は、
- 少ない記述量でhttps化を実現できる
- STAGEというオプションを利用することで本番・テスト・開発環境の3つを自由に行き来することができる
といったことがあげられます。
詳しくは、以下のリンクが役に立つかと思います。私は以下のリンクを見て学習しました。またおそらくこのページに行きついた方はこれらのページをご覧になっているかと思います。
ハマった点
https-portalは、ドメインがない開発環境などでも自己署名認証書を用いたhttps通信ができるように作られています。その機能を試してみるためにシンプルにnginxサーバーをhttps化する以下のdocker-compose.yml
を作成しました。
version: '3.9'
services:
https-portal:
image: steveltn/https-portal:1
ports:
- '80:80'
- '443:443'
restart: always
environment:
DOMAINS: 'localhost -> http://nginx:80' # localhostとnginxの80番portを紐づける。
STAGE: 'local'
volumes:
- https-portal-data:/var/lib/https-portal
nginx:
image: nginx
restart: always
volumes:
https-portal-data:
しかし私の環境ではこのまま、
$docker compose up
で起動させると、
Error response from daemon: driver failed programming external connectivity on endpoint httpd_learn-https-portal-1 (4d451ae5ca658352660344ae3c59dd5fbd420be9e58f518a18651b5e541af948): Error starting userland proxy: error while calling PortManager.AddPort(): cannot expose privileged port 443, you can add 'net.ipv4.ip_unprivileged_port_start=443' to /etc/sysctl.conf (currently 1024), or set CAP_NET_BIND_SERVICE on rootlesskit binary, or choose a larger port number (>= 1024): listen tcp4 0.0.0.0:443: bind: permission denied
といったエラーで実行することができませんでした。このエラーは、1024未満の特権ポートを権限なしに使うことができないというものでした。なぜなのか調べてみると、dockerにはrootlessモードというものあり、私はそのモードでインストールしていたことが原因でした。確かに公式ページのrootlessモードの説明にもそのような記述があります。(Known limitationsの項目)
もし利用されているdockerがrootlessモードであるかを確認するには、
$docker info | grep "rootless"
で"rootless"という文字列が含まれているかで判断できます。入っている場合はrootlessモードでしょう。
解決策
根本的な解決策はrootlessでないdockerを利用したり、特別に443と80ポートは管理者権限なしに利用できるように設定を変更することです。ですがおそらくdockerをrootlessで利用している場合は、管理者権限を持っていなかったり、開発環境でセキュリティを考えてのことなので、https-portal
のためだけに管理者権限を利用することは現実的ではないでしょう。
そこでもとのportを変更してアクセスすることを提案します。ここでは8000
を加えることにしましょう。
ports:
+ - '8080:80'
+ - '8443:443'
- - '80:80'
- - '443:443'
docker-compose.yml
を変更したうえだもう一度、
$docker compose up
を実行します。すると私の場合はエラーなく動くことを確認しました。
続いて起動している環境でcurlでアクセスできるかを試してみます。一度、ctrl+C
で停止し、バックグラウンドモードで入り直します:
$docker compose up -d
起動したら、curlでlocalhostにアクセスしてみましょう。すると、
$curl http://localhost:8080
<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx</center>
</body>
</html>
のように表示されると思います。301リダイレクトとしてhttp通信は機能しており、https通信になるように強制していることがわかりますね。続いて8443ポートを用いて、https通信が実現しているかを試します。
$curl https://localhost:8443
curl: (60) SSL certificate problem: self-signed certificate
More details here: https://curl.se/docs/sslcerts.html
curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.
このようにここでは、Let's encryptを用いずに自己署名証明書を用いていることから、通常の通信ではエラーが出てしまいます。そこで、--insecure
オプションを指定して強制的に接続します。
$curl --insecure https://localhost:8443
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
と表示されて確かにhttps通信でアクセスできていることが確認できるでしょう。ポートフォワーディングでlocalhostの8443ポートを飛ばして、ローカルPC上でアクセスすると、以下のようなおなじみのページを確認できるでしょう。
本記事が皆さんの開発のお役に立てることを願って文章を終えようと思います。最後までお読みいただきありがとうございました。
Discussion