【SSH】パスワード認証方式の場合、認証回数に制限を設ける

## はじめに
LinuxサーバにSSH接続して利用している。しかし、もっとセキュリティを高めたい。
そこで今回は、「パスワード認証方式を利用している場合に、認証回数制限を設ける」について書いていきます。
「認証回数に制限を設ける」とは、**決められた回数パスワード認証が失敗した場合、そのアカウントをロックする**といったものです。[^1]
何らかの理由で公開鍵認証方式が利用できない場合において、「パスワード総当たり攻撃」の脅威を和らげる対策となります。[^2]
[^1]: もちろんロックされてしまった場合、そのユーザではSSHログインできなくなります。完全にSSH接続不可とならないよう、救済措置を考えておきましょう。
[^2]: その他、パスワード流出の危険性もあるため、できる限り公開鍵認証方式を取り入れた方が良いと考えます。

## SSHとは
`SSH`は、ネットワーク経由でリモートにあるLinuxサーバにログインするためのプロトコルです。
通信が暗号化されているので覗き見されても問題なく、安全に作業することができます。
SSHプロトコルを利用してリモートログインするためには、Linuxサーバに`OpenSSH`をインストールする必要があります。
恐らくほとんどのディストリビューションに標準で組み込まれており、新たにインストールする必要はないと思います。

## パスワード認証方式とは
ログインIDとパスワードを入力して、LinuxサーバにリモートログインするSSHの標準機能です。
公開鍵認証方式とは異なり、「パスワード総当たり攻撃」を受けた場合には、不正にログインされてしまう危険性があります。

## パスワードの認証回数に制限を設ける
パスワード認証回数に制限を設けるためには`PAM`の`pam_faillock`モジュールを利用します。
`pam_faillock`モジュールは、指定された間隔でユーザごとの認証失敗のリストを保持し、 認証が連続して失敗した場合にアカウントをロックする機能を有します。

### faillock.confの設定をしよう
`pam_faillock`モジュールは`/etc/security/faillock.conf`を使用して設定値を定義します。
今回は`Ubuntu22.04`を対象に試したので、以下のページを参照しながら設定しました。
[faillock.conf](https://manpages.ubuntu.com/manpages/jammy/man5/faillock.conf.5.html)

### 設定例
例えば以下のように編集してみます。

```bash:/etc/security/faillock.conf
# Don't print informative messages.
# Enabled if option is present.
silent
# Deny access if the number of consecutive authentication failures
# for this user during the recent interval exceeds n tries.
# The default is 3.
deny = 3
#
# The length of the interval during which the consecutive
# authentication failures must happen for the user account
# lock out is <replaceable>n</replaceable> seconds.
# The default is 900 (15 minutes).
fail_interval = 900
# The access will be re-enabled after n seconds after the lock out.
# The value 0 has the same meaning as value `never` - the access
# will not be re-enabled without resetting the faillock
# entries by the `faillock` command.
# The default is 600 (10 minutes).
unlock_time = 600
```

- 上記の意味
	- silent アカウントが存在する場合も認証失敗のメッセージを出力しない
	- deny = 3 パスワード認証回数が3回を超えたらロックする
	- fail_interval = 900 15分(900s)の内に、上記回数制限に達した場合ロックする
	- ロックアウトから20分(600s)後にアクセスは再び有効になる

ちなみに、上記設定の場合、rootユーザはロックの対象とはなりません。

### OpenSSHの設定をしよう
次に、SSH接続の際に上記で設定した`pam_faillock`モジュールが使われるように設定していきましょう。
`/etc/ssh/sshd_config`を編集します。

```bash:/etc/ssh/sshd_config
# Set this to 'yes' to enable PAM authentication, account processing,
# and session processing. If this is enabled, PAM authentication will
# be allowed through the ChallengeResponseAuthentication and
# PasswordAuthentication.  Depending on your PAM configuration,
# PAM authentication via ChallengeResponseAuthentication may bypass
# the setting of "PermitRootLogin without-password".
# If you just want the PAM account and session checks to run without
# PAM authentication, then enable this but set PasswordAuthentication
# and ChallengeResponseAuthentication to 'no'.
UsePAM yes
```
上記でPAMを利用した認証が有効になります。
[sshd_config](https://manpages.ubuntu.com/manpages/jammy/man5/sshd_config.5.html)

また、`PAM`本体の定義として`pam_faillock`の認証定義を読み込むように設定します。
設定対象ファイルは`common-auth`と`common-account`です。

```bash:/etc/pam.d/common-auth
auth	required			pam_faillock.so	preauth
# here are the per-package modules (the "Primary" block)
auth	[success=1 default=ignore]	pam_unix.so nullok
auth	[default=die]			pam_faillock.so	authfail
auth	sufficient			pam_faillock.so	authsucc
```

`PAM`の設定ファイルには、以下の4種類をタブで区切って定義します。
- タイプ
- 制御
- モジュールのパス
- モジュールに渡す引数

モジュールは上から1行ずつ読み込まれて認証の判定が行われます。
`preauth`引数を渡して`pam_faillock`を呼び出す→パスワード認証をおこなう以前に、制限にかかっていないかチェックします。
実際のパスワード認証は`pam_unix`モジュールがおこないます。
`authfail`引数を渡して`pam_faillock`を呼び出す→パスワード認証に失敗した場合に、それを記録します。
`default=die`は、処理を終えたら直ちに処理を返すことを意味します。
後ほど`faillock`コマンドを利用して認証失敗状態を確認しますが、その際、記録がされていないと正しく表示されません。
`authsucc`引数を渡して`pam_faillock`を呼び出す→パスワード認証が成功の場合、これ以降のモジュールは処理されません。
[pam_faillock](https://manpages.ubuntu.com/manpages/jammy/man8/pam_faillock.8.html)

```bash:/etc/pam.d/common-account
account	required			pam_faillock.so
# here are the per-package modules (the "Primary" block)
account	[success=1 new_authtok_reqd=done default=ignore]	pam_unix.so 
```


## 変更結果を試してみよう
ここまで定義を終えたら、sshdを再起動します。
`sudo systemctl restart ssh`

誤ったパスワード入力を行い、SSH接続を失敗してみます。
`ssh tester1@[ホスト名]`

ユーザごとの認証失敗履歴を以下で確認できます。
`sudo faillock`

```bash:ターミナル
tester1:
When                Type  Source                                           Valid
2023-08-21 21:47:12 RHOST 192.168.1.123                                        V
```

一番右に`V`となっているのが認証失敗です。
3回失敗してみます。

```bash:ターミナル
tester1:
When                Type  Source                                           Valid
2023-08-21 21:47:12 RHOST 192.168.1.123                                        V
2023-08-21 21:51:16 RHOST 192.168.1.123                                        V
2023-08-21 21:51:22 RHOST 192.168.1.123                                        V
```

authlogを読んでみます。
```bash:/var/log/auth.log
Aug 21 21:47:12 [サーバ名] sshd[1523]: Failed password for tester1 from 192.168.1.123 port 58848 ssh2
Aug 21 21:51:16 [サーバ名] sshd[1523]: Failed password for tester1 from 192.168.1.123 port 58848 ssh2
Aug 21 21:51:22 [サーバ名] sshd[1531]: Failed password for tester1 from 192.168.1.123 port 58856 ssh2
Aug 21 21:53:48 [サーバ名] sshd[1531]: pam_faillock(sshd:auth): Consecutive login failures for user tester1 account temporarily locked
```
きちんとアカウントロックされたようです。
それから再度SSH接続を試みると以下のようになりました。
`ssh: connect to host [ホスト名] port 22: Connection refused`[^3]
[^3]: アカウントごとにロックされる認識でいましたが、22番ポートが閉じ、全ユーザが締め出されてしまいました。<br>どうやらこれはfail2banによるもののようです。

## アカウントロックの解除方法
一度アカウントが削除されると、有効期限が設定されている場合はそれを過ぎるまで、
未設定の場合はクリアコマンドが実行されるまで、そのユーザでログインすることができません。
以下のコマンドでアカウントの認証失敗回数をクリアできます。
`sudo faillock --user tester1 --reset`