1. bababachi

    Posted

    bababachi
Changes in title
+dockerコンテナ上で使用しているLet's Encryptを自動更新する。
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,271 @@
+4月から転職しまして、自社設備の右も左もわからない状態で
+「Let's Encryptがそろそろ更新だからやっといて〜」
+とのタスクをいただいたのでついでに自動化をば。
+
+dockerでアプリを実装していたんですが、マウントしているのは証明書だけだったのでホストのcronで回してます。
+
+## 前提情報 
+
+- ホスト
+ - Amazon Linux
+- コンテナ
+ - nginx
+
+- 証明書保存先
+
+```shell
+$ 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
+```
+
+```shell: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ディレクトリにコピーする必要がある。
+
+```shell:/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設定されていなかった。(まじか)
+時刻合わせを実施する(設定済みの方は飛ばしてください)
+
+```diff
+$ 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
+@@ -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
+```
+毎週月曜9:30にスクリプト実行し、デフォルトなので有効期限が30日未満の場合に更新します。
+ついでに次回更新時の確認のためにslackでリマインドしました。
+
+### cronの時間変えて検証
+
+ちゃんと動作することを確認してください。(cronを実際に動かさないと、パーミッションと環境変数でエラーとかあるあるですよね。)
+
+## 完了
+
+<img width="478" alt="スクリーンショット 2019-04-13 19.42.41.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93620/66476eb6-0a75-16ea-fcb2-ad212df4b055.png">
+
+**やったぜ。**
+
+どのサーバでなにやってるかわからない中、取り急ぎ感が満載ですが無事docker上で使用しているLet's Encryptを更新自動化できました。
+これからもいろいろ整備していきます。
+
+ホストがオートスケールだったりマネージドでホストがなかったりするとcronじゃ難しいですが、とりあえず基本としてメモまで。