9
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

dockerコンテナ上で利用しているLet's Encryptを自動更新する

4月から転職しまして、自社設備の右も左もわからない状態で
「Let's Encryptがそろそろ更新だからやっといて〜」
とのタスクをいただいたのでついでに自動化をば。

dockerでアプリを実装していますが、マウントしているのは証明書だけだったのでホストのcronで回してます。

前提情報 

  • ホスト
    • Amazon Linux
  • コンテナ

    • nginx
  • 証明書保存先

$ ls -al foo/var/certs/
合計 28
drwxrwxr-x  2 ec2-user ec2-user 4096  3月  8 03:57 .
drwxrwxr-x 16 ec2-user ec2-user 4096  4月  8 02:32 ..
-rw-r--r--  1 root     root      692  1月 22 09:15 README
-rw-rw-r--  1 ec2-user ec2-user 1927  3月 26 00:47 cert.pem
-rw-r--r--  1 root     root     1647  1月 22 09:15 chain.pem
-rw-rw-r--  1 ec2-user ec2-user 3574  3月 26 00:47 fullchain.pem
-rw-rw-r--  1 ec2-user ec2-user 1704  3月 26 00:47 privkey.pem

1. どこでLet's Encryptが使われてるか確認

そうです、探すとこからです。

  • とりあえずpstree
$ pstree
略
    ├─dockerd─┬─docker-containe─┬─docker-containe─┬─mysqld───28*[{mysqld}]
     │         │                 │                 └─9*[{docker-containe}]
     │         │                 ├─docker-containe─┬─mysql
     │         │                 │                 ├─php-fpm───3*[php-fpm───3*[{php-fpm}]]
     │         │                 │                 └─9*[{docker-containe}]
     │         │                 ├─docker-containe─┬─nginx───nginx
     │         │                 │                 └─9*[{docker-containe}]
     │         │                 ├─docker-containe─┬─redis-server───2*[{redis-server}]
     │         │                 │                 └─9*[{docker-containe}]
     │         │                 ├─docker-containe─┬─celery───3*[celery]
     │         │                 │                 └─9*[{docker-containe}]
     │         │                 ├─docker-containe─┬─postgres───10*[postgres]
     │         │                 │                 └─9*[{docker-containe}]
     │         │                 ├─docker-containe─┬─gunicorn───4*[gunicorn]
     │         │                 │                 └─9*[{docker-containe}]
     │         │                 └─19*[{docker-containe}]
     │         ├─docker-proxy───7*[{docker-proxy}]
     │         ├─2*[docker-proxy───5*[{docker-proxy}]]
     │         ├─docker-proxy───6*[{docker-proxy}]
     │         └─21*[{dockerd}]

あ、dockerなんですね。

  • docker-compose情報
$ cat docker-compose.yml
version: '2'
services:
  app:
    build: ./foo/app
    volumes:
      - ./:/var/www
    links:
      - mysql
  web:
    build: ./bar/web
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./:/var/www
      - ./certs/:/etc/pki/certs
    links:
      - app
  mysql:
    build: ./nyaa/mysql
    ports:
      - "3306:3306"
ry

とりあえず./bar/web/以下のwebコンテナのみ証明書を使用していると判断

$ ls -al ./bar/web/
合計 20
drwxrwxr-x 2 ec2-user ec2-user 4096  3月 14 07:57 .
drwxrwxr-x 5 ec2-user ec2-user 4096  1月 22 08:58 ..
-rw-rw-r-- 1 ec2-user ec2-user  298  1月 22 08:58 Dockerfile
-rw-rw-r-- 1 ec2-user ec2-user  671  1月 22 08:58 nginx.conf
-rw-rw-r-- 1 ec2-user ec2-user  650  3月 14 07:57 vhost.conf
vhost.conf
ry
    ssl on;
    server_name your_domain.jp;
    ssl_certificate /etc/pki/certs/fullchain.pem;
    ssl_certificate_key /etc/pki/certs/privkey.pem;
ry

webコンテナのnginxで使用していることが判明  
(このタイミングで確認しましたがやはり使用しているのはnginxだけでした。)

2. 更新作業

  1. 証明書の更新
  2. dockerコンテナ内nginxのreload

上記手順を自動化します。

証明書の更新

とりあえず更新してみる。

  • 設定ファイル
$ cat /etc/letsencrypt/renewal/your.domain.conf
# renew_before_expiry = 30 days
version = 0.33.1
archive_dir = /etc/letsencrypt/archive/your.domain.jp
cert = /etc/letsencrypt/live/your.domain.jp/cert.pem
privkey = /etc/letsencrypt/live/your.domain.jp/privkey.pem
chain = /etc/letsencrypt/live/your.domain.jp/chain.pem
fullchain = /etc/letsencrypt/live/your.domain.jp/fullchain.pem

# Options used in the renewal process
[renewalparams]
authenticator = xxxxxx
account = your_account_key
server = https://acme-v02.api.letsencrypt.org/directory
[[webroot_map]]
your.domain.jp = /foo/bar/directory
  • 更新を実行
$ sudo /usr/bin/certbot-auto renew
Upgrading certbot-auto 0.30.0 to 0.33.1...
Replacing certbot-auto...
Creating virtual environment...
Installing Python packages...
Installation succeeded.
Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/your.domain.jp.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cert is due for renewal, auto-renewing...
Plugins selected: Authenticator xxxxxx, Installer None
Renewing an existing certificate
Performing the following challenges:
http-01 challenge for your.domain.jp
Waiting for verification...
Cleaning up challenges

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
new certificate deployed without reload, fullchain is
/etc/letsencrypt/live/your.domain.jp/fullchain.pem
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Congratulations, all renewals succeeded. The following certs have been renewed:
  /etc/letsencrypt/live/your.domain.jp/fullchain.pem (success)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  • 確認
$ sudo ls -al /etc/letsencrypt/live/your.domain.jp
合計 12
drwxr-xr-x 2 root root 4096  4月  8 03:31 .
drwx------ 3 root root 4096  1月 22 09:12 ..
-rw-r--r-- 1 root root  692  1月 22 09:12 README
lrwxrwxrwx 1 root root   45  4月  8 03:31 cert.pem -> ../../archive/your.domain.jp/cert2.pem
lrwxrwxrwx 1 root root   46  4月  8 03:31 chain.pem -> ../../archive/your.domain.jp/chain2.pem
lrwxrwxrwx 1 root root   50  4月  8 03:31 fullchain.pem -> ../../archive/your.domain.jp/fullchain2.pem
lrwxrwxrwx 1 root root   48  4月  8 03:31 privkey.pem -> ../../archive/your.domain.jp/privkey2.pem

3. 自動化と反映

証明書はちゃんと更新できることが判明したので、自動化してdockerコンテナに反映させます。

更新スクリプトを作成

現在dockerにマウントさせている証明書は上記証明書のコピーとなっている。
自動更新に対応させるため、上記シンボリックリンク先の証明書をvolumeディレクトリにコピーする必要がある。

/usr/local/libexec/lets_encrypt_renew.sh

#!/bin/sh

# 証明書の更新
/usr/bin/certbot-auto renew

# 証明書のコピー(docker_mountは上記docker-compose.ymlのvolumeマウント先です)
cp /etc/letsencrypt/live/your.domain.jp/cert.pem docker_mount/certs/
cp /etc/letsencrypt/live/your.domain.jp/chain.pem docker_mount/certs/
cp /etc/letsencrypt/live/your.domain.jp/fullchain.pem docker_mount/certs/
cp /etc/letsencrypt/live/your.domain.jp/privkey.pem docker_mount/certs/

# nginxのリロード
docker exec -i container_name service nginx reload

timezone設定

cron実行してテストしようとしたらtimezone設定されていなかった。(まじか)
時刻合わせを実施する(設定済みの方は飛ばしてください)

$ sudo cp -a /etc/localtime ~/org/
$ sudo ln -sf  /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

$ sudo cp -a /etc/sysconfig/clock ~/org/
$ sudo vi /etc/sysconfig/clock

$ diff -u ~/org/clock /etc/sysconfig/clock
--- /home/your_user/org/clock   2018-11-17 08:07:41.583229222 +0900
+++ /etc/sysconfig/clock    2019-04-08 14:06:53.465544040 +0900
@@ -1,2 +1,2 @@
-ZONE="UTC"
+ZONE="Asia/Tokyo"
 UTC=true
  • 確認
$ date
2019年  4月  8日 月曜日 14:09:02 JST (あってる。)

cron記載

  • /etc/cron.d/lets_encrypt
30 9 1 * * root sh /usr/local/libexec/lets_encrypt_renew.sh > /dev/null 2>&1

毎月1日9:30にスクリプト実行し、(デフォルトなので)有効期限が30日未満の場合に更新します。
ついでに次回更新時の確認のためにslackでリマインドしました。

cronの時間変えて検証

ちゃんと動作することを確認してください。(cronを実際に動かさないと、パーミッションと環境変数でエラーとかあるあるですよね。)

完了

ブラウザから確認

やったぜ。

どのサーバでなにやってるかわからない中、取り急ぎ感が満載ですが無事docker上で使用しているLet's Encryptを更新自動化できました。
これからもいろいろ整備していきます。

ホストがオートスケールだったりマネージドでホストがなかったりするとcronじゃ難しいですが、とりあえず基本としてメモまで。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
9
Help us understand the problem. What are the problem?