こんにちは!!こんにちは!!
インフラエンジニアのyamamotoです。
Let's Encrypt、いつも活用させていただいております!
ただ、証明書を取得するときにサーバー上でいろいろ作業をしなければなりません。さらにdocker環境上ではどうするんだ!?となるかと思います。
そこで、dockerコンテナでもLet's Encryptを簡単に使えるように整備してみました。
既存のイメージでLet's Encrypt組み込み済みのものもありますが、ここではあえて自前で作ってみます。
Dockerfileまわり
dockerイメージは、nginxのオフィシャルイメージを元に、Let's Encryptで必要なプログラムのインストールと、ちょっとひと工夫入れた起動スクリプトを組み込みます。
Dockerfile
FROM nginx:latest ENV LETSENCRYPT_HOSTS 【ドメイン名 スペース区切りで複数指定可】 ENV LETSENCRYPT_MAIL 【窓口メールアドレス】 ENV LETSENCRYPT_SUBJECT "/C=JP/ST=Tokyo/L=Shinagawa/CN=default"【←仮証明書用の記載内容】 ENV DEBIAN_FRONTEND noninteractive RUN apt-get update \ && apt-get install --yes --no-install-recommends \ openssl \ certbot \ && rm -rf /var/lib/apt/lists/* COPY start.sh /start.sh RUN chmod +x /start.sh CMD /start.sh
nginxのオフィシャルイメージはDebianベースのものを使っています。apt-getでopensslとcertbot(Let's Encryptの証明書取得プログラム)をインストールしています。
上で指定している「start.sh」というシェルスクリプトが起動スクリプトになります。起動スクリプト内で証明書を自動発行・自動更新するようにしています。
start.sh
#!/bin/bash for HOST in ${LETSENCRYPT_HOSTS} do if [ ! -d "/etc/letsencrypt/live/${HOST}" ]; then mkdir -p /etc/letsencrypt/live/${HOST} mkdir -p /var/lib/letsencrypt crt_file="/etc/letsencrypt/live/${HOST}/fullchain.pem" && key_file="/etc/letsencrypt/live/${HOST}/privkey.pem" && subject="${LETSENCRYPT_SUBJECT}" && openssl req -new -newkey rsa:2048 -sha256 -x509 -nodes \ -set_serial 1 \ -days 3650 \ -subj "$subject" \ -out "$crt_file" \ -keyout "$key_file" && chmod 400 "$key_file" fi done nginx for HOST in ${LETSENCRYPT_HOSTS} do if [ ! -e "/etc/letsencrypt/initialize" ]; then rm -rf /etc/letsencrypt/live/${HOST} certbot certonly -n --keep-until-expiring --agree-tos \ --webroot --webroot-path /var/lib/letsencrypt \ -m ${LETSENCRYPT_MAIL} -d ${HOST} fi done touch /etc/letsencrypt/initialize certbot renew nginx -s reload while true do sleep 7 done
nginxの設定にSSLの設定を入れた状態だと、証明書が無いと起動ができないのでまずオレオレ証明書で起動して、その後Let's Encryptで証明書を取得して再起動します。
証明書の取得は最初だけにして、あとは「certbot renew」で証明書の更新を毎回チェックするようにしています。こうすることで、コンテナを再起動しただけで証明書が更新できます。
また、最後にループを入れています。これは、万が一nginxがエラー終了した場合でもコンテナに入ってチェックできるようするためです。
nginxの設定
nginxの設定は、Let's Encryptの証明書を読み込ませるようにしておきます。他は通常通りで構いません。複数ドメインにも対応可能です。
コンテナに取り込むことを考えて、なるべく一つのファイルで済ませられるようにしています。
nginx.conf
user nginx; worker_processes 1; error_log /var/log/nginx/error.log ; pid /var/run/nginx.pid; events { worker_connections 1024; } http { client_max_body_size 50m; include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; sendfile on; keepalive_timeout 15; gzip on; gzip_disable "msie6"; gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; server { listen 80 default_server; listen 443 ssl default_server; root /var/www/html; ssl_certificate /etc/letsencrypt/live/test.harmonicom.jp/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/test.harmonicom.jp/privkey.pem; } # test server { listen 80; server_name 【ドメイン名】; root /var/www/html; access_log /var/log/nginx/access.log main; error_log /var/log/nginx/error.log; # Let's Encrypt証明書の更新に必要なので残しておくこと location ^~ /.well-known/acme-challenge { alias /var/lib/letsencrypt/.well-known/acme-challenge; default_type "text/plain"; try_files $uri =404; } } server { listen 443 ssl http2; server_name 【ドメイン名】; root /var/www/html; access_log /var/log/nginx/access_ssl.log main; error_log /var/log/nginx/error_ssl.log; ssl_certificate /etc/letsencrypt/live/【ドメイン名】/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/【ドメイン名】/privkey.pem; ssl_session_timeout 5m; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv3:+EXP; ssl_prefer_server_ciphers on; } }
docker-compose.yml
最後にdocker-composeの設定ファイルです。ここではDockerfileで用意した環境変数を定義しています。Dockerfileの設定を書き換えるより、こちらを書き換えた方がいいと思います。
docker-compose.yml
version: '3' services: nginx: container_name: hogehoge-nginx build: ./ environment: TZ: Asia/Tokyo LETSENCRYPT_HOSTS: 【ドメイン名 スペース区切りで複数指定可】 LETSENCRYPT_MAIL: 【窓口メールアドレス】 LETSENCRYPT_SUBJECT: "/C=JP/ST=Tokyo/L=Shinagawa/CN=default"【←仮証明書用の記載内容】 ports: - '80:80' - '443:443' volumes: # nginx.conf - ./nginx.conf:/etc/nginx/nginx.conf # log directory - /var/log/nginx:/var/log/nginx # document root directory - /var/www/html:/var/www/html
ログディレクトリやドキュメントルートはここで設定可能です。
docker-composeの実行
上記の4つのファイルを全て一つのディレクトリに入れ、そのディレクトリの中でdocker-composeコマンドを実行します。
$ sudo docker-compose -f docker-compose.yml build $ sudo docker-compose -f docker-compose.yml up -d
イメージをビルドして、そのイメージからコンテナを起動します。
最初の起動時には、オレオレ証明書を作ったうえでnginxを起動し、Webでの認証でLet's Encryptの証明書を取得してnginxをリロードします。
起動後すぐにHTTPSでページの閲覧が可能です。
証明書の更新時はコンテナを再起動します。
$ sudo docker-compose -f docker-compose.yml restart
最後に
注意点としては、テストなどであまり何度も証明書を取得してしまうと取得制限に引っかかってしまうので、あまり繰り返しテストなどはしない方がいいと思います。
再起動する分には問題無いですが、イメージを再ビルドすると取得しなおしてしまいます。
一週間で5回証明書を発行したら、それ以上発行できなくなりますのでご注意を。
アクトインディではdockerやりたい!というインフラエンジニアも募集していますよ!