パッケージ版じゃあ無くて、pipでcertbotをインストールしたい

どうも、証明書にまだ取り込まれてない方の私です。

以前から、Ubuntuのパッケージ版`certbot`を使っていましたが、結構古い版を掴まされてたので、OSのアップグレードを機にパッケージ版`certbot`を使うのを止めて、茨の道へ進むことにしました。

----

ここで言う「パッケージ版`certbot`」とは、`apt`（あるいは`deb`）や`snap`でインストールできる`certbot`の事を指します。
少なくとも後者なら古い版を掴まされる事は無く、システムリソースに十分な余力があるのと`snap`依存に抵抗がなければそちらを使ってみるのも良いかもしれません。

----

**利点：**
* バージョンアップにすぐ対応できる
  * パッケージ版の場合はすぐに更新できない、或いは永遠に更新が無かったりする

**欠点：**
* 初回だけ面倒くさい作業するから忍耐力が要る
  * インストール作業以外はパッケージ版`certbot`と操作に違いは無い

**筆者の当時の環境：**
* Google Cloud VM
* Ubuntu 20.04(focal) → **24.04**(noble) に、ほかほかアップデートしたての頃
* Python 3.12（デフォルト）

:::note info
この記事は証明書の作成に関するHow toモノではありません！
:::

# 1. Pythonをインストールする

と、言ってもPythonはデフォルトでインストール済みなケースが多いので、必要なのは`venv`ぐらい。コレが無いと仮想環境を作る際、正常動作しないので、しっかりインストールしておく。

インストールするならパフォーマンスの観点から、Python 3.11以上がおすすめだが、Pythonに詳しい・詳しくないにかかわらず、この点はあまり気にする必要が無い。

以前に`certbot`をインストールしている場合は、このタイミングで消しましょう。<br>もし既にアンインストールしているなら次のコマンドは飛ばして下さい。

```bash
sudo apt remove certbot
# 恐らく`python3-certbot`は関連パッケージまで消してしまうので使わない方が良いかも。
# パージすると再設定しないければならない余分な仕事が増えるぞ。
# aptではなく、snapでインストールした記憶があるなら、まずは`snap list --all`でインストールされているか確認しよう。
```

`venv`をインストールします。

```bash
sudo apt update && sudo apt install python3-venv libaugeas0
```

# 2. `venv`を使用して`certbot`専用の仮想環境を作る

パッケージ版`certbot`ライブラリと、`pip`版`certbot`ライブラリが競合することを防げるので、仮想環境作りはとても大事。基本的に`/opt`へ仮想環境を作成するのが無難。

```bash
# certbotは、rootレベルで動作させるので、sudoを付けるのを忘れずに。
sudo python3 -m venv /opt/certbot/
```

エラーメッセージ以外、成功しても何も表示しない。

# 3. `certbot`専用の仮想環境に`certbot`をインストールする

初回は`pip`が古かったりするので、インストールに支障が出ないよう、最新版にする。

```bash
sudo /opt/certbot/bin/python3 -m pip install --upgrade pip
```

`certbot`をインストールする。

```bash
sudo /opt/certbot/bin/python3 -m pip install certbot
```

## （余談）偶に面倒見てあげて

`certbot`は一応[**毎年何かしらの更新**](https://github.com/certbot/certbot/releases)があるので、気づいたら更新してあげよう。

```bash
sudo /opt/certbot/bin/python3 -m pip install --upgrade certbot
```

rootユーザーの`cron`か何かで定期的なアップグレードを噛ますのも良いかもしれませんが、自動更新ほど愚かな行為は無いよ。
自動更新するにしても、しっかりログを確認するように。

# 4. `certbot`のシンボリックリンクを張る

ターミナルに`/opt/certbot/bin/certbot`と長いコマンドをいちいち打たなくても良いようにする。

一応パッケージ版が`/usr/bin/certbot`を使っているはずなので、競合回避と決別という意味を込めて`/usr/local/bin/certbot`にリンクを張る。私情なので好きな方で。

```bash
sudo ln -s /opt/certbot/bin/certbot /usr/local/bin/certbot
```

リンクが張れたなら`certbot`が最新版であるか確認してみる。

```bash
certbot --version
```

----

余談だが、元々パッケージ版を使ってた影響なのか、うちではPython 3.8時代の`certbot`が`/usr/local/bin/certbot`に潜んでいたため、リンクを張る際にエラーが発生した。同事情の方はリンクを張る前に`ls -la /usr/local/bin/`して確認したほうが良いかも。

# （おまけ）Systemd timerを使って証明書を定期更新する

cronではなく、Systemd timerを使った証明書の定期更新についてメモしていく。<br>もちろんcronを使っても良い。

ここまで来たならパッケージ版と同調したいのと、クールにタイマー管理をキメたいのでSystemdを駆使してみる。

**利点：**
* SystemdはUbuntuやRed Hatのシステムに組み込まれていている
* セキュリティ特性が高い
* いつ`certbot`が実行して、次回どの時間に動作するか、タイマー管理が繊細
* 雑なログ管理になりにくい

**欠点：**
* パッケージ版`certbot`なら、これらのサービス登録は自動化されている
* cronなら一行の記載で済むのに対して、Systemdは色々しなきゃ成らない

## `certbot.service`と`certbot.timer`のファイルを作る

Systemd timerの場合は、サービスの他にタイマーの設定ファイルを作る必要がある。

いちいち解説しながら書くのは面倒なので、簡単なテンプレートを用意した。上記の通り変えずに環境作成してるならそのまま使えるはず。

* `certbot.service`と`certbot.timer`の名前を統一する
* これらのファイルは`/etc/systemd/system`に格納する
  * 収納しただけではアクティブにはならない
  * パッケージが`/usr/lib/systemd/system`を使ってくるので、ここへの収納は避けたい
* パーミッション`644`、オーナー:グループを*root:root*に統一する

```systemd:certbot.service
[Unit]
Description=Certbot
# このUnitはネットワークがオンラインになるまで待つ
After=network-online.target
[Service]
# ExecStartの内容を一度だけ実行（oneshot）する
Type=oneshot
# コマンド内容
ExecStart=/usr/local/bin/certbot --quiet renew --no-random-sleep-on-renew
# certbot専用のプライベート`/tmp`を作って貰う
PrivateTmp=true
[Install]
# ランレベル3相当で有効化させる
WantedBy=multi-user.target
```

`certbot`に使われているオプションの意味は次の通り：
* `--quiet` - エラー以外は何も表示しない（cronやsystemdに最適）
* `--no-random-sleep-on-renew` - ランダムスリープを無効にする（`RandomizedDelaySec`が代わりに仕事する） → [参考](https://community.letsencrypt.org/t/disabling-random-sleep-of-certbot/83201)

```systemd:certbot.timer
[Unit]
Description=Certbot Schedule
[Timer]
# タイマー
OnCalendar=*-*-01/2 09:00:00
# 0から指定された時間値までの間、タイマーの実行をランダムに遅らせる
RandomizedDelaySec=3600
# 指定の時間に実行できなかった場合、アクティブ成った際に即実行する
Persistent=true
[Install]
WantedBy=timers.target
```

`OnCalendar`については [#参考文献](#参考文献) にある記事が詳しいのでそちらを参照して下さい。ここでは簡単にしか書きません。

書式は`年-月-日 時:分:秒`で、年以外2桁のゼロ埋めします。<br>テンプレートに書いた内容では
* 2日おき（`*-*-01/2`）
* 9時（`09:00:00`）から10時（`RandomizedDelaySec=3600`）の間

この内容で実行してくる。もちろん好きに変えて良い。

キチンとできたら次のコマンドを使って有効化していく。

```bash
# サービスとタイマーを有効化する
sudo systemctl enable certbot
sudo systemctl enable certbot.timer
# タイマーをスタートさせる
sudo systemctl start certbot.timer
```

コマンド`systemctl status certbot`で、次のように`certbot.timer`のトリガーがアクティブ状態になっていれば良い。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70146/05f98a74-0f05-4705-8875-eb55a9bd2bd4.png)

コマンド`systemctl list-timers`あるいは`certbot.timer`のステータスでも今回と次回の実行スケジュールが確認できる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70146/dc83c7d9-b6e2-4f25-a83d-cfba4a797aa3.png)

もし設定が反映されて無い場合は`sudo systemctl daemon-reload`でリロードをかけてみる。

怪しいと思ったらログも確認するように。保存場所は`/var/log/letsencrypt/*`にある。

---

面倒と言いつつも、それなりにまとめたので「いいね」して貰えるとうれしみです。あとSystemdのシンタックスハイライトまで用意されてるQiita強い…。

# 参考文献

* インストール方法など - https://certbot.eff.org/instructions?ws=other&os=pip
* OnCalendar について詳しい記事 - https://takuya-1st.hatenablog.jp/entry/2020/04/24/032822