メールサーバに LDAP サーバに対するヘルスチェックを組み込む

# 概要

Postfix で3000人規模の利用者がいるメールサーバを運用しています。利用者の登録情報（誰が登録されているか、転送先アドレスは何処か、など）は、別サーバで稼働している LDAP サーバに格納されています。

メールサーバ管理者と LDAP サーバ管理者が別人の場合、連絡不行届などが理由で、LDAP サーバは停止しているのに、メールサーバを起動してしまうことが有り得ます。このような事故が発生すると、Postfix は接続元に対して一時的エラーを通知してメールを受け取らないという挙動をするはずですが、そもそも Postfix を止めてしまった方が良いという場合もあります。筆者の場合は、停電からの復電作業手順書に、メールサーバと LDAP サーバの電源投入順序に制約を追加し、作業時間を増やしたくないという動機です。

本稿では、そのような LDAP サーバに対するヘルスチェック付きのメールサーバを構築する方法を述べます。

# 設定手順

## ヘルスチェックスクリプトのインストール

[ldap-health-check スクリプト](https://gist.github.com/tsuchm/3f62b51514be34268a308123c25346c6)を `/usr/local/bin/` に置いて下さい。
そのスクリプトの引数として、Postfix の LDAP 設定ファイルを指定する `--conffile` オプションと `--debug` オプションの2つを指定して実行し、LDAP サーバと通信できていることを確認してください。

```shell-session
$ /usr/local/bin/ldap-health-check --conffile /etc/postfix/your-using-ldap-config --debug
DEBUG: LDAP search success: dn=ou=users,dc=example,dc=net
^C
```

## ヘルスチェックサービスの作成

以下のようなサービス定義ファイル `/etc/systemd/system/ldap-health-check.service` を用意します。

```conf:/etc/systemd/system/ldap-health-check.service
[Unit]
Description = ldap-health-check daemon

[Service]
ExecStart = /usr/local/bin/ldap-health-check --conffile your-using-ldap-configuration-file
Type = simple
Restart = always
StartLimitBurst = 1

[Install]
RequiredBy = postfix.service
```

この設定では、`Restart = always` と指定して「常に再起動を試みる」、かつ `StartLimitBurst = 1` と指定して「1回再起動を試みて失敗したら、そこで諦める」という指定をしています。ここで、「諦める」という指定になっているのが重要です。本稿の目的として、このヘルスチェックサービスが停止したら、その時点で Postfix も停止したいわけですが、うっかり `StartLimitBurst = 0` と設定して、無期限に再起動を試みるという指定をすると Postfix は停止してくれません。ヘルスチェックサービスの再起動を試行している間は、`RequiredBy` によって指定した依存関係が満たされていると見なされて、Postfix は停止されないようです。ですから、`StartLimitBurst` を適当に指定して、ヘルスチェックサービスを明示的に **失敗** させる必要があります。

作成した定義ファイルを systemd に読み込ませます。

```shell-session
$ sudo systemctl enable ldap-health-check.service
$ sudo systemctl start ldap-health-check.service
```

この状態で、以下のようなメッセージが表示されるはずです。

```shell-session
$ systemctl status ldap-health-check.service
● ldap-health-check.service - ldap-health-check daemon
   Loaded: loaded (/etc/systemd/system/ldap-health-check.service; enabled; vendor preset: enabled)
   Active: active (running) since Thu 2020-03-19 22:03:25 JST; 32min ago
 Main PID: 23220 (ldap-health-che)
    Tasks: 1 (limit: 4698)
   Memory: 11.8M
   CGroup: /system.slice/ldap-health-check.service
           └─23220 /usr/bin/perl /usr/local/bin/ldap-health-check
```

## LDAP サーバが応答していない時、Postfix が停止することを動作確認

LDAP サーバの IP アドレスが、198.51.100.8 であるとします。メールサーバから LDAP サーバへの通信を禁止するには、メールサーバ上で、以下のようにダミーの経路を設定することがもっとも簡単です。

```shell-session
$ sudo ip route add blackhole 198.51.100.8
```

ダミーの経路が設定されると、`ldap-health-check` スクリプトが、以下のようにエラーで異常終了するようになります。

```shell-session
$ systemctl status ldap-health-check.service
● ldap-health-check.service - ldap-health-check daemon
   Loaded: loaded (/etc/systemd/system/ldap-health-check.service; enabled; vendor preset: enabled)
   Active: failed (Result: exit-code) since Thu 2020-03-19 22:46:15 JST; 9s ago
  Process: 36413 ExecStart=/usr/local/bin/ldap-health-check (code=exited, status=1/FAILURE)
 Main PID: 36413 (code=exited, status=1/FAILURE)
```

すると、Postfix も以下のように異常終了しているはずです。

```shell-session
$ systemctl status postfix.service
● postfix.service - Postfix Mail Transport Agent
   Loaded: loaded (/lib/systemd/system/postfix.service; enabled; vendor preset: enabled)
   Active: inactive (dead) since Thu 2020-03-19 22:46:15 JST; 3min 53s ago
 Main PID: 31077 (code=exited, status=0/SUCCESS)

Mar 19 22:21:14 mx1.example.net systemd[1]: Starting Postfix Mail Transport Agent...
Mar 19 22:21:14 mx1.example.net systemd[1]: Started Postfix Mail Transport Agent.
Mar 19 22:46:15 mx1.example.net systemd[1]: postfix.service: Succeeded.
Mar 19 22:46:15 mx1.example.net systemd[1]: Stopped Postfix Mail Transport Agent.
Mar 19 22:46:15 mx1.example.net systemd[1]: Stopping Postfix Mail Transport Agent...
Mar 19 22:46:15 mx1.example.net systemd[1]: Dependency failed for Postfix Mail Transport Agent.
Mar 19 22:46:15 mx1.example.net systemd[1]: postfix.service: Job postfix.service/start failed with result 'dependency'.
```

末尾から2行目に `Dependency failed` と出力されていることがポイントで、ヘルスチェックサービスが停止しているために Postfix サービスを開始できないことが分かります。


## LDAP サーバが復活した時、Postfix が再開することを動作確認

以下のような cron を設定して、10分に少なくとも1回は、Postfix の起動を試みるようにします。

```conf:/etc/crontab
3,13,23,33,43,53 * * * *	root	/usr/bin/systemctl status postfix >/dev/null || /usr/bin/systemctl start postfix
```

LDAP サーバへのダミーの通信経路を削除して、正常に通信できるようにします。

```shell-session
$ sudo ip route del blackhole 198.51.100.8
```

すると、10分以内に cron により Postfix の起動が試行されます。

先ほどのヘルスチェックサービスの設定で、`StartLimitBurst = 1` と指定して、「再起動に1回失敗したら諦める」と設定してありますが、`systemctl start postfix` コマンドが実行されると、その時点で、Postfix サービスが依存している別サービスの失敗回数カウンタもリセットされるので、少なくとも1回はヘルスチェックサービスの再起動が試行されます。したがって、この時点でヘルスチェックが成功すれば、Postfix も無事に起動されます。 


# 参考文献

 * [systemd エッセンシャル](http://redhat.lookbookhq.com/systemd-essential?fbclid=IwAR2S1dQk8YfSQy5OFH0_GNvocWdEm0QeU6OPaUstzb3OXok9qzRf1V5Cjg4)