iPhoneで Grafanaの グラフを 参照できる アプリ Grafanizer 作ってます。 詳しくは こちらへ

GAE の Let's Encrypt 証明書をコマンド一発で更新

Jul 18, 2017  
#appengine

9/19現在でGAEの純正機能で自動更新付きのSSLサポートが追加されてますので、以下の内容は不要です。 LINK

いつのまにか gcloud コマンドでSSL証明書の更新ができるようになってる!!!!

日本語のリリースノートは遅れてるようですので、 英語のリリースノート を見ると

159.0.0 (2017-06-14)
Google App Engine
  Added commands to support automatically managed certificates:
    gcloud alpha app ssl-certificates
155.0.0 (2017-05-11)
Google App Engine
  Added gcloud beta app ssl-certificates command group.

という感じで、5/11にコマンドが追加されて、6/14にGAEサーバー側でも更新が効くようになったよってことでしょうかね? betaからalphaってのが良くわかりませんが。

察しの良い方だったら多分

$ gcloud beta app ssl-certificates --help

NAME
    gcloud beta app ssl-certificates - view and manage your App Engine SSL
        certificates

SYNOPSIS
    gcloud beta app ssl-certificates COMMAND [GCLOUD_WIDE_FLAG ...]

DESCRIPTION
    (BETA) This set of commands can be used to view and manage your app's SSL
    certificates.

だけでも十分でしょう。

ってことで、Let'sEncryptの証明書をGooge AppEngineにコマンド一発で設定することができるようになりました。とりあえずメモしておきます。(2017/07/18時点

ざっくりした流れを説明すると、

  1. GCPの管理画面でGAE用にサブドメインを作成
  2. Certbotで証明書を取得
  3. GCPに証明書を送信
  4. Certbotで証明書を更新
  5. GCPに証明書を送信

という感じで、今までは数ヶ月毎に4,5の操作を手作業で、それも5についてはWebブラウザの画面をマウスポチポチキーボードコピペで行うことになってたのですが、そこをコマンド一発で実行できるようになったということです。

Cronを使って自動化させることもできますが、今回は自動化までは作り込まずGoogleCloudShellを使ってコマンドラインから証明書の更新を行ってみようと思います。

まず、GCPのコンソールからCloudShellにログオンします。いつのまにかGCP利用者であればずっと無料になってますね。

ログインが済んだらおもむろにCertbotをクローンします。

git clone https://github.com/certbot/certbot

CloudShellには大方のコマンドが入ってるので便利ですね。

次に、初回の証明書を作成します。

sudo ./certbot/certbot-auto certonly \
                            --manual \
                            --domain sub.example.com \
                            --email admin@example.com \
                            --agree-tos \
                            --manual-public-ip-logging-ok \
                            --preferred-challenges http \
                            --config-dir ./certbot/etc \
                            --work-dir ./certbot/work \
                            --logs-dir ./certbot/logs

ホーム以外は保存されないため、 ./certbot 内にデータを入れてますが、場所についてはお好みでどうぞ。

このコマンドで、足りないパッケージ一類一式がインストールされたあとに、httpサーバーを使った身元確認作業が発生します。対応方法については 「Google Cloud Platformでの独自ドメイン関連まとめ 」が参考になると思います。

無事に証明書が発行されたら、今度はその証明書をGAEにアップロードします。

sudo cp ./certbot/etc/live/sub.example.com/fullchain.pem ./
sudo cp ./certbot/etc/live/sub.example.com/privkey.pem ./
gcloud beta app ssl-certificates create \
                                 --display-name=hogehoge \
                                 --certificate=fullchain.pem \
                                 --private-key=privkey.pem

certbot はroot権限で動かす必要があるのですが、 gcloud は一般ユーザーで動かす必要があります。いや、一般ユーザーは認証済みなので手間がかからないけど、root権限で動かすには手間がかかるので面倒だから一般ユーザーで実行と言ったほうがいいでしょうか。

ですので、証明書を手元にコピーしたあとにアップロードしています。

アップロードに成功するとCertificateID?が表示されますので、それを控えておきます。この初回アップロードは一度だけですので、これまで通りWeb画面から行った方がわかりやすいかもしれません。その場合は証明書の編集画面を開いたときのURLにIDが表示されるのでそれを控えておきます。すでに証明書を登録済みの場合も同じ画面から証明書を控えておきます。

控えたIDとカスタムドメインを紐付けるには下記のコマンドを使いますがが、こちらもWeb画面から行ったほうが後々わかりやすいかもしれませんね。

gcloud beta app domain-mappings create sub.example.com --certificate-id CertificateID

これで準備完了です。あとは下記のスクリプトを作成して定期的に実行すればOKです。変数は各自で変更してください。

#!/bin/bash

CERTBOTDIR=./certbot
DOMAIN=sub.example.com
CERTIFICATEID=123456
DISPLAYNAME=sub

sudo $CERTBOTDIR/certbot-auto renew \
                              --config-dir $CERTBOTDIR/etc \
                              --work-dir $CERTBOTDIR/work \
                              --logs-dir $CERTBOTDIR/logs
sudo cp $CERTBOTDIR/etc/live/$DOMAIN/fullchain.pem ./fullchain.pem
sudo cp $CERTBOTDIR/etc/live/$DOMAIN/privkey.pem ./privkey.pem
gcloud beta app ssl-certificates update $CERTIFICATEID \
                                 --certificate=./fullchain.pem \
                                 --display-name=$DISPLAYNAME \
                                 --private-key=./privkey.pem

実行する度に必要なパッケージ類がインストールされますが、そういうものです気長に待ちましょう。

毎回待てない、もしくはcronで定期的に実行させたいという方はローカルマシン等で同じように設定すれば完全自動実行で更新できるようにも出来ます。 gcloud コマンドの認証を済ませておけば同じように利用できるはずです。

ですが、今回は以下の点から半自動位までにおさえておきました。

  1. 更新コマンドとWebアプリの実行環境は同じクラウドにしたい
  2. 各々のバージョンアップに追従し確実に動いてる保証を取りたい
  3. 使われなくなってもCronのみが回ってるということは避けたい

1.については、ローカルに環境を作ると環境が汚れそうで嫌だなという点につきます。数ヶ月に一回しか実行しない環境のために手元の環境が壊れたりしたらちょっと面倒です。また、数カ月に一度しか実行しないコマンドのためにマシンを準備(実機・仮想ともに)するというのも大げさです。定期的に環境がきれいになるCloudShellはその点ではぴったりのソリューションでした。Cronを使えれば最高だったのですが、3.にある理由から「まっいいか」という気持ちです。

2.も結構重要な点で、特に gcloud コマンドの更新は激しく、いつバージョンアップで動きが変更になるかわかりません。また、バージョンアップしないと動かないという可能性もあります。 certbot もまだ若いコマンドなので同じような可能性があります。殆どの場合で後方互換性は保証されるとは思うのですが、数カ月に一回程度であれば手動で動きを確認しながら実行させたほうが精神的に安心です。実行することさえ忘れなければですが、今時はスマートフォンでの通知もありますし問題ないでしょう。

3.は意識的なところですが、アプリは使われてないのにSSL証明書だけを更新するということになってしまうとちょっと気持ち悪い感じがします。現在は無料で使えるLet'sEncryptですが、将来的にはドメイン単位で制限が出たり、規定数以上は有料とかにもなりかねません。自動化で楽になるとどんどん証明書を取得してしまうと思うのですが、そのときCronで回ってるスクリプトはどこで実行されているか把握できますかね?主をなくして空回りしているスクリプト程可哀想なものはありません(ちょっといいすぎ)。


いつでも最新のGcloudコマンドが使えるCloudShellで、こちらもメンテナンスされているDockerなCertbotをCronで回せれば最高なんだけど、それはちょっと望み過ぎだよね。