Gitlab Pagesの独自ドメイン用Let's Encrypt証明書を自動更新する

## 2019/08/17 追記
Gitlab 12.1 から、Gitlab の Web UI のみで「カスタムドメイン用Let's Encrypt証明書発行」ができるようになったみたいです。
https://docs.gitlab.com/ee/user/project/pages/custom_domains_ssl_tls_certification/lets_encrypt_integration.html

以下の情報は古いものとして扱ってください（multidomain 環境ならまだ役に立つのかも？そこまでは調べてないです。）


## 環境
* gitlab.com
    * GitLab Enterprise Edition 11.11.1-ee （2019/06/02 現在）
* APIでレコード情報を更新できるDNSサービス
    * certbot のプラグインが対応している DNS であればどこでも良いです
        * https://certbot.eff.org/docs/using.html#dns-plugins
    * certbot 公式の Docker image が用意されている DNS だと、より楽になります
        * https://hub.docker.com/u/certbot
    * 今回はさくらのクラウド DNS を利用します

## 前提
### さくらのクラウド DNS
* Gitlab Pages を公開したいドメイン用のゾーンが作成されていること
* API key/Api secret が発行済であること
    * 必要なアクセスレベルは、「設定編集」以上です

### gitlab.com
* Gitlab Pages で静的ページが公開されていること
* 独自ドメイン＋SSL証明書で公開するための設定まで完了していること
    * https://docs.gitlab.com/ee/user/project/pages/getting_started_part_three.html#gitlab-pages-custom-domains-and-ssltls-certificates
    * 今回のやり方では、すでに設定されているドメインに対して、SSL証明書および秘密鍵を差し替えることしかできません。  
      初回設定は画面から行う必要があります
* API操作用のアクセストークンが発行済であること

## 手順
### Gitlab CI の環境変数を設定
* 設定する内容は「さくらのクラウド API key/API secret」と「Gitlab.comアクセストークン」
* 今回は名前をそれぞれ `SAKURA_SECRETS` `GITLAB_API_TOKEN` としました
* `SAKURA_SECRETS` は Type を File にしておくと後々便利です
![スクリーンショット 2019-06-02 11.01.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260472/0e7395c4-6cfd-c201-c898-9dfe6a8937c7.png)

### 証明書発行＆Gitlab Pagesに証明書をアップロードするためのスクリプトを作成
* DNS 認証でのSSL証明書発行となるため、ワイルドカード証明書も発行できます
    * ワイルドカード証明書の場合、証明書/秘密鍵の格納先ディレクトリが変わります
        * `www.epaew.net` を発行すると `/etc/letsencrypt/live/www.epaew.net/`
        * `*.epaew.net` を発行すると `/etc/letsencrypt/live/epaew.net/`
    * 複数ドメインに使える証明書だとどこに格納されるんだろうね :thinking: 
* `certbot renew` を使いたいのは使いたいんですが、発行済証明書をどこで保持するか悩んだ結果毎回新規で発行しています。  
キャッシュだといつ消えるかわからないですし、リポジトリにコミットするものでもないと思うので。。

```sh
#!/bin/sh -eu
certbot certonly --dns-sakuracloud --dns-sakuracloud-credentials $SAKURA_SECRETS \
  -d <設定したいドメイン> -m <メールアドレス> \
  --manual-public-ip-logging-ok --agree-tos

curl -X PUT -H "Private-Token: $GITLAB_API_TOKEN" \
    -F "key=@/etc/letsencrypt/live/<証明書のドメイン>/privkey.pem" \
    -F "certificate=@/etc/letsencrypt/live/<証明書のドメイン>/fullchain.pem" \
    https://gitlab.com/api/v4/projects/<GitlabプロジェクトのID>/pages/domains/<Gitlab Pagesに設定したドメイン>
```

### .gitlab-ci.yml にジョブを作成
* ハマったポイント
    * Docker の `certbot` イメージは、entrypoint に `[ "certbot" ]` が設定してあります
        * https://hub.docker.com/r/certbot/certbot/dockerfile
        * `script:` が動かないので、空にします
        * curl も入ってないので入れる必要があります
    * このあと Gitlab の設定画面からスケジュールを設定しますが、現状「ジョブごとのスケジュール」は設定できません。
        * 環境変数で頑張れば分岐できそうだけど今回は対象外
        * 各ジョブに `only/except: schedules` を設定するようにしてください

```diff
 pages:
   image: node:lts
   script:
     - yarn run generate
   artifacts:
     paths:
     - public
   only:
     - master
+  except:
+    - schedules
+
+renew_certs:
+  image:
+    name: certbot/dns-sakuracloud
+    entrypoint: [""]
+  before_script:
+    - apk --no-cache add curl
+  script:
+    - scripts/create-and-set-new-cert.sh
+  only:
+    - schedules
```

### Gitlab CI にスケジュールを設定
* 2ヶ月に1回発火するように設定しました
![スクリーンショット 2019-06-02 11.40.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260472/6a0bbdee-cb2b-786c-655e-d54a91e6a8e5.png)

### 動作確認
Gitlab CI の Scheduling Pipelines の画面で「▶」を押せば、スケジュールジョブを手動実行することができます

Gitlab Pagesのページに移動し、証明書が差し替わっていれば無事完了です。
お疲れ様でした。

## 参考
https://docs.gitlab.com/ee/user/project/pipelines/schedules.html
https://docs.gitlab.com/ee/api/pages_domains.html#update-pages-domain
https://qiita.com/chamaharun/items/c87dbfe865d8126433bf