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
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. 更新作業
- 証明書の更新
- 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ディレクトリにコピーする必要がある。
#!/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じゃ難しいですが、とりあえず基本としてメモまで。