sshd_configファイルMatch条件不備時注意

# TL;DR;
* sshd設定のMatch行のAddress条件は起動時・リロード時チェックが入らない
* 設定に不備があると即ログイン不能になる可能性がある
* Match条件を設定する場合は条件指定した拡張テストで事前確認しよう

# はじめに
## 背景
最近、OpenSSHのsshdで、表題の通りMatch条件の不備で注意を要するケースがありましたので、備忘録として記事にしました。

## 検証環境

* サーバ
  * x86_64 CentOS7.6
  * openssh-server-7.4p1-16.el7
  * VirtualBox上のVMで動作
  * IPアドレス 10.0.0.200/24
* クライアント
  * x86_64 Windows10 + WSL + Ubuntu 18.04.2 LTS
  * openssh-client 7.6p1-4ubuntu0.3
  * IPアドレス 10.0.0.10/24

# 事例
## 問題
**前提:**

サーバ側には、既にrootログインを禁止するための`PermitRootLogin no`の設定を入れているとします。

ここで、一部からはrootログインを許可したいので、`sshd_config`ファイルの末尾に次の設定を入れました。

```txt:追加設定
Match Address 10.0.0.8/28
  PermitRootLogin without-password
```

そして、`sshd -t`コマンドでテストが通ることを確認して再起動しました。

```shell-session:再起動
$ sshd -t  # 何も出力されなければテストにパス
$ systemctl restart sshd
```

**質問:**
`systemctl status sshd`コマンドで見ても、特に動作に問題はないようでした。しかし実はもうトラブルが発生しています。それはどのようなものでしょうか?
※ログインに必要な公開鍵は適宜登録してあるものとします。

**解答:**
実はこの状態では、誰もどこからもsshログインできなくなっています。
公開鍵認証かパスワード認証か、はたまたrootユーザか一般ユーザかに関わりなく、です。

普通に運用しているところにほんのちょっと追加設定したつもりが、サービス全体がダメになるという悲惨な状況になります。

## 原因
賢明な皆さんはお気づきでしょうが、`Match Address 10.0.0.8/28` という条件設定に問題がありました。
実は、10.0.0.10を含む10.0.0.8～10.0.0.15 のアドレス帯を指定したいという思惑があったのですが、それであれば`10.0.0.8/29`とすべきでした。
あるいは`/28`に合わせるなら`10.0.0.0/28`で、これは 10.0.0.0～10.0.0.15 の範囲 ( 通常 10.0.0.0 はノードのアドレスとして使えないので、実質 10.0.0.1～10.0.0.15 ) を意味します。
※この`/28`や`/29`の指定は、対象ノードのアドレスのマスク`10.0.0.10/24`の`/24`とは全く関係ないことに注意が必要です。

sshdは、`Match`の設定を見て条件判定を試みるのですが、設定不備により条件のふるい分けができず、どういう設定を反映すればいいか混乱状態に陥って、結果どの接続も拒否する結果になってしまったと言えます。

## 設定のチェック
しかし、設定の不備ということであれば、起動時であったり、チェック用のコマンド`sshd -t`あるいは`sshd -T`で引っかからないのか、気になるところかと思います。

ところが実は、**`Match`での`Address`の指定は事前チェックされない**ようなのです。そうでない項目は ( 全部確認したわけではありませんが )、設定の不備を検出してくれるのですが。条件指定内の設定だからチェックされない、ということもなく、です。

```shell-session:検出してくれる例
[root@cent7]# tail -n 2 /etc/ssh/sshd_config
Match Address 10.0.0.8/29
  PermitRootLogin with-pleasure  # ハイヨロコンデー
[root@cent7]# sshd -t
/etc/ssh/sshd_config line XXX: unsupported option "with-pleasure".
```

## 拡張テストと条件指定
では、なんとかしてこういった条件でも事前チェックする方法はないでしょうか。
流石に**「目視チェックで分からないのが悪い」と言われるようだと辛い**所です。

こんな時は、拡張テストである`sshd -T`に、条件指定の`-C`オプションを併用する方法があります。

`sshd -T`は、`sshd -t`のテストに加え、設定内容のレポートも出力してくれる拡張テストです。
なお、`sshd -t`の場合もそうですが、ホスト鍵が正常に読み込めるかどうかの確認まで考えると、root権限でテストを実行するのが望ましいと言えます。

```shell-session:拡張テスト
[root@cent7]# sshd -T
port 22
addressfamily any
listenaddress [::]:22
…
```

続いて`-C`は、接続条件を指定するためのオプションです。例えば`10.0.0.10`からの接続の時に、どのような設定内容が反映されるのか、次のように確認することができます。

```shell-session:条件指定、正常時
[root@cent7]# tail -n 2 /etc/ssh/sshd_config
Match Address 10.0.0.8/29
  PermitRootLogin without-password
[root@cent7]# sshd -T
…
permitrootlogin no
…
[root@cent7]# sshd -T -C addr=10.0.0.10,user=hoge,host=hoge
…
permitrootlogin without-password
…
```

このように、Matchが正しく設定されていれば、`-C`で指定した条件で設定内容がどのように変わるか、見ることができます。
なお、試したい条件は今回IPアドレス(addr)だけですが、それでもuser,hostも何かしら指定する必要があります。

そして、Matchに設定不備がある場合、次のようにテストで引っかかります。さらに`-dd`でデバッグレベルを上げておけば、その詳細も分かります。
※不備の概要が"Bad Match condition"(Matchの条件が悪い)で、詳細が"inconsistent mask length for match netowrk …"(マスクの長さがおかしい)

```shell-session:条件指定、設定不備あり
[root@cent7]# tail -n 2 /etc/ssh/sshd_config
Match Address 10.0.0.8/28
  PermitRootLogin without-password
[root@cent7]# sshd -T -C addr=10.0.0.10,user=hoge,host=hoge
reprocess config line XXX: Bad Match condition
[root@cent7]# sshd -T -dd -C addr=10.0.0.10,user=hoge,host=hoge
…
debug2: addr_match_list: inconsistent mask length for match network "10.0.0.8/28"
reprocess config line XXX: Bad Match condition
```

# まとめ
ということで、sshdが設定を事前チェックしないケースがあることと、その場合でもチェックを行うための方法を紹介しました。
sshdの設定をミスると、とても影響が大きいことが多いので、こういったケースに限らず、事前チェックには気を付けたいところかと思います。

なお、事前チェックも重要ですが、設定を変更するときは**ssh接続を何か残した状態で変更を反映して、不備があっても残している接続から復旧操作できるようにしておく**ということを心がけるのは言うまでもありません。