はじめに
- この記事では、dockerのnginxコンテナを使ったwebサイトのhttps化手順をまとめています。
- dockerコンテナと、let's encryptを使ってサイトのhttps化を目指している方の参考になれば幸いです。
- また、自身の知識の整理のためにssl証明書について冒頭でまとめていますが、既にご存じの方は3. ssl証明書の取得手順までスキップしてください。(もし知識の浅い部分があればご指南いただけると嬉しいです。)
筆者の開発環境
- サーバ: ubuntu 20.04
- nginx (docker)
目次
1. ssl証明書の目的
2. let's encryptについて
3. ssl証明書の取得手順
4. ssl証明書の更新方法
5. おわりに
1. ssl証明書の目的
- 結論から言うと、ssl証明書の目的は webサイトの通信を暗号化してセキュリティを高めることです。
- 暗号化されていないhttp通信では、平文のままサーバ間でやりとりをするため通信が傍受されてしまった場合、情報を悪用されるリスクがあります。
- 一方でssl通信は、通信の内容が暗号化されているため傍受された場合でも、傍受者はその通信内容を悪用することができません。
ssl証明書の目的
通信を暗号化して情報の漏洩を防ぐこと
ssl証明書の種類
- webサイトでssl通信をするために必要な ssl証明書 は無料のものから十数万するものまであります。
- ただ、無料だからといって、通信の暗号化の機能が劣っているということではないそうです。
- では、なぜ有料のssl証明書があるかというと、サイト運営者の身分証明 のためです。
- ssl証明書には大まかに下記の3種類があります。
証明書の種類 | 身分証明方法 | 価格(3つの中の比較になります) |
---|---|---|
ドメイン認証 | ドメイン所有者の情報(メールアドレス等) | 低 |
企業認証 | 組織・企業の情報 | 中 |
EV証明 | 組織・企業の情報(所在地含む) | 高 |
- 価格が高くなるにつれて、提示する個人情報が詳細になっていきます。
- 個人情報が詳細になるほどwebサイトのセキュリティ(信頼性)としては高いと言えます。
- これまで説明したように、ssl証明書は無料でメールアドレスの確認だけで作れてしまうものもあります。
- サイトの運用者が悪者だった場合は、いくらssl通信で暗号化されているからといって安全とは言えません。
- もし、これが組織がお金を払い、身分情報を提示して発行したssl証明書であれば、サイトの信頼性は高く安全と言えると思います。
有料のssl証明書が持つ役割
ドメイン所有者の個人情報を担保にして、サイトの信頼性を高めること
2. lets encryptについて
- これまで 有料のssl証明書の役割について説明してきましたが、個人のwebサイトを試しに立ち上げてみたい、というような方であれば最低限のssl通信を実現できるssl証明書があれば十分だと思います。
- 無料のssl証明書 としてlet's encrypt が有名どころとしてあります。
- この記事を作るにあたり他の無料ssl証明書も調べたところ zerossl というサービスもあるようです。全てブラウザで完結するとのことなので lets encrypt よりも簡単かもしれないです。
- 詳しい内容は下記の記事でまとめられていたので興味を感じられた方をご覧ください。
- 無料の SSL 証明書が得られる ZeroSSL を使ってみた
- この記事では let's encryptを使ってwebサイトにssl証明書を適用する方法を紹介していきます。
3. ssl証明書の取得手順
let's encrypt 公式手順は docker 環境では参考にできない
- let's encrypt 公式のgetting started を見ます。
- 公式によると、
certbot
というサービスを使うことで簡単に証明書の取得ができるようです。 -
certbot
のページを開くと、各環境に合ったcertbotのインストール手順を紹介してくれます- ただ、今回の環境は dockerのnginxコンテナで動いているため該当する選択肢はないように見えました。
certbot のdockerイメージを取得する
- 情報を探すと、公式が出している
certbot
のコンテナイメージがありました。 - こちらのイメージを使い、
Dockerfile
とdocker-compose.yml
を作成します - 筆者はこのような構成でディレクトリを作成しました。
.
└── nginx_docker
├── Dockerfile ... nginx用
├── certbot
│ └── Dockerfile ... certbot用
├── conf.d
│ ├── default.conf ...初めてssl証明書を取得するまで使うnginx設定ファイル
│ └── ssl_server.conf.txt ...ssl証明書の取得後、使うnginx設定ファイル
├── dist ... nginxでこちらのディレクトリの資材へルーティングします
│ └── index.html
└── docker-compose.yml
ここからは各種設定ファイルの準備をしていきます
1. certbotと、nginxののDockerfileを作る
-
certbotとnginxのDockerfileの中身です
-
certbot
# base image
FROM certbot/certbot:latest
- nginx
# base image
FROM nginx:latest
WORKDIR /dist
RUN nginx -t
RUN service nginx restart
2. docker-compose.ymlにcertbotを追加する
[1/6 更新] 記載漏れをご指摘いただきましたので一部更新いたしました。
https://qiita.com/mttt/items/aa2ba3a0677a803d0436#comment-838affca72c5a7837fa7
version: "3"
services:
nginx:
image: nginx:latest
# nginxをリスタートした時にコンテナが終了しないようにする
restart: always ... certbotで証明書を取得した後、nginxをリスタートします。こちらを入れないとリスタートでコンテナが落ちるので追加します。
container_name: nginx
build:
context: .
dockerfile: ./Dockerfile
ports:
- "80:80"
- "443:443" # コンテナへhttps通信が通るようにport解放します
# 20日おきにnginxを再読み込みする
command: "/bin/sh -c 'while :; do sleep 20d & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
volumes:
# nginxの設定ファイル
- ./conf.d:/etc/nginx/conf.d
# nginxのルートパス
- ./dist:/dist
- /etc/letsencrypt:/etc/letsencrypt #ssl証明書が配置されるパス
certbot:
image: certbot/certbot:latest
container_name: certbot
build:
context: .
dockerfile: ./certbot/Dockerfile
# 10日おきに証明書を更新する(期限切れまで30日以上あれば更新しない)
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 10d & wait $${!}; done;'"
volumes:
- /etc/letsencrypt:/etc/letsencrypt #こちらに取得したssl証明書が配置されます。nginxコンテナでも同じくマウントして共有します。
- ./dist:/dist
command: ["--version"]
- 補足したい部分についてはコメントを追記しています。
- 証明書を取得すると
/etc/letsencrypt
に配置されます。これはnginxコンテナでも使いたいので同じパスをマウントします。 - nginxコンテナで
restart: always
しておくと、コンテナ外からnginxのリロードをしてもコンテナが落ちなくなるのでつけておくと良いです
- 証明書を取得すると
3. nginxの設定ファイルを作成する
-
nginx設定ファイルは初回のssl証明書取得前後で、2つ使い分けます
-
まずは初めてssl証明書を取得するまで使う設定ファイル
default.conf
server {
listen 80;
server_name {ドメイン名}; # ★適宜変更してください
location / {
root /dist/;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
- 次に、ssl証明書取得後に使う設定ファイルです。
ssl_server.conf.txt
- 使うときは、
.txt
の拡張子を外します。
- 使うときは、
server {
server_name {ドメイン名}; # ★ドメイン名を適宜変えてください
listen 80;
listen [::]:80;
# 証明書更新のために:80/.well-knownにアクセスしたいので、ここだけリダイレクトはしない
location ^~ /.well-known {
root /dist/;
}
# 全てのリクエストをSSLサイトにリダイレクト
location / {
return 301 https://$host$request_uri;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
server {
server_name {ドメイン名}; # ★ドメイン名を適宜変えてください
listen 443 ssl http2;
listen [::]:443 ssl http2;
ssl_certificate /etc/letsencrypt/live/{ドメイン名}/fullchain.pem; # ★ドメイン名を適宜変えてください
ssl_certificate_key /etc/letsencrypt/live/{ドメイン名}/privkey.pem; # ★ドメイン名を適宜変えてください
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_protocols TLSv1.3 TLSv1.2;
ssl_ciphers '{任意のアルゴリズム}'; ★適宜変えてください。ssl_ciphers自体を省略でも動作するようです。
ssl_prefer_server_ciphers off;
add_header Strict-Transport-Security "max-age=2592000" always;
location / {
root /dist/;
index index.html index.htm;
}
}
- 80ポートに来た通信は全て、443ポートのhttps通信にリダイレクトされるようにしています。
- 2つの設定ファイルを使い分ける理由
- certbotは初めて証明書を取得するときに、80ポートで通信します。そのため、
ssl_server.conf.txt
で設定されているように、443ポートにリダイレクトされると失敗します。初回は.well-known
ディレクトリも使われないです。
- certbotは初めて証明書を取得するときに、80ポートで通信します。そのため、
設定ファイルを元にコンテナを立ち上げていきます。
- docker-compose.ymlのあるパスまで移動して、下記コマンドを実行します。(コンテナイメージの取得)
docker compose build
- dockerコンテナを立ち上げます
docker compose up -d nginx
- この時点だと、まだssl証明書が入っていないので、サイトは閲覧できないはずです
- certbotコンテナを実行して、ssl証明書を取得します。
-
docker compose run --rm certbot certonly --webroot -w {nginxのドキュメントルート} -d {ドメイン名}
- {nginxのドキュメントルート}は設定ファイルのままであれば、
/dist
になります - {ドメイン名}は適宜変更してください
- {nginxのドキュメントルート}は設定ファイルのままであれば、
- これで、
etc/letsencrypt
配下に証明書が配置されているはずです
-
- これで、ssl証明書を使う準備が整ったので、nginx.confを
ssl_server.conf.txt
に差し替えます。-
ssl_server.conf.txt
の.txt
拡張子を外します。 -
default.conf
に拡張子.txt
を追加します。(.conf拡張子が外れるとnginxで適用されなくなります)
-
- そして
docker compose exec nginx nginx -s reload
を実行してnginxをリロードします - 以上で、httpsで通信できれば作業は完了です。
2023/7/9更新 上記default.conf 8行目付近の記載に誤字があったため修正しました。
location ^~ /.well-known {
[誤] root /dsit/;
[正] root /dist/;
4. ssl証明書の更新方法
-
let's encrypt の証明書は
90日
で期限が切れてしまうために定期的に更新が必要です。 -
ただ、今回紹介した
ssl_server.conf
の設定であれば、docker-compose.ymlで設定したentrypoint
が働き、定期的に証明書を取り直してくれるため、基本的には更新作業は不要です。 -
証明書の自動更新方法については下記の記事を参考にさせていただきました。
- docker nginx + Let's encrypt 自動更新(cron使わないで)
- ※[注意] 自動更新が動作確認については本記事ではまだ確認できていないため、すぐに動作確認したい方は上記の記事をご覧ください。(時間が経過して動作確認がとれたら記事を更新する予定です)
5. おわりに
- 作業を振り返って、dockerを使った let's encrypt証明書の発行も意外と簡単でした。
- 公式からdockerイメージが出されているのが大きかったです。
- 一方で、Dockerfileや、docker-compose.ymlのノウハウがあれば、自動更新周りをもう少し見通しよく作れそうだと感じたので、個人的には証明書更新よりもdocker難しいという感想を持ちました。
- ssl証明書の作成を通じて、webサイトのセキュリティについても興味を持ち始めたので、今後はそちらについても記事発信していきたいです。