sshdのセキュリティベストプラクティス

# sshdのセキュリティベストプラクティス

忘れがちなので、個人的メモとして残します。
どなたかの参考になれば幸いです。

## サーバには標準以外のポートを使用すべし

```bash
# If you want to change the port on a SELinux system, you have to tell
# SELinux about this change.
# semanage port -a -t ssh_port_t -p tcp #PORTNUMBER
#
#Port 22 #ここです。
#AddressFamily any
#ListenAddress 0.0.0.0
#ListenAddress ::
```

デフォルトで「sshデーモン」はポート22番で受信接続をリッスンしている。
そのため悪意あるユーザーがまずすることとして、ポート22番が空いているかどうかを確認します。sshがマシン上で実行されている場合、ポート22をスキャンする可能性が高くなります。

根本的な解決にはなりませんが、このポート22番をランダムなポートに変更することにより、スキャンされる可能性を低減させることが可能です。

ポートを変更するには、sshd_configのファイルのPortディレクティブを任意の値に変更します。

```bash
#Port 22 コメントアウトを外します
Port 5649 #このような感じでランダムなポートを設定します。
```

ポートを変更した際は、ファイアウォールのルール設定を変更すること。
awsの場合は、セキュリティグループに「カスタムTCP 5649」をルール追加すること。
Linuxでselinuxを有効にしている場合は、デーモンであるfirewalldで以下の設定を追加すること。

`firewall-cmd --add-port=5649/tcp --zone=public --permanent`  
`firewall-cmd --reload`  
`firewall-cmd --list-all`

## ルートユーザの直接ログインを禁止すべし

一度奪還されるとなんでも操作できてしまう危ないアカウントは直接アクセスを禁止しましょう。これにより、最も危険なセキュリティホールの対策を打つことが可能です。

rootユーザの操作が必要な場合は、一度通常のユーザーとしてログインしてから、sudoまたはsuコマンドを使用してroot権限でコマンドを実行します。

```bash
#PermitRootLogin prohibit-password
#prohibit-passwordは公開鍵認証によるログインを許可するものですが、pemファイル自体を漏洩してしまっては意味がないので、一番安全なnoを指定しましょう

#コメントアウトを外して noを指定します。
PermitRootLogin no
```

## パスワード認証を無効にし、公開鍵認証のみのログインを許可しよう

パスワード認証を無効化できないケースでは、強力なパスワードを設定することをお勧めします。また、パスワードについてはローテーションをするのではなく、一度強固なパスワードを設定したらなるべく変更しないようにしましょう。これは、パスワードを何度も変更を促されると人間は比較的覚えやすいパスワードを設定してしまうという心理からくるものです。事実、[総務省](https://www.soumu.go.jp/main_sosiki/cybersecurity/kokumin/business/business_staff_01.html)からも定期的なパスワードの変更は不要であるという発表がされております。

強力なパスワードの指針としては、文字と数字と記号で（大文字含む）構成される少なくとも10文字以上の長さのランダムなパスワードを設定しましょう。

## SSHで接続可能なユーザを制限しよう

デフォルトでは、管理者が数人しかいない場合でも、すべてのシステムユーザーは自分のパスワードまたは公開キーを使用して、SSH経由でログインが可能です。

この場合は「AllowUsers」ディレクティブのオプションを追加してSSHログインできるユーザを制限します。

```bash
#sshログインが可能なユーザーを制限する
#AllowUsers yunisan1211 #yunisan1211のみ許可
AllowUsers yunisan1211@x.x.x.x #yunisan1211かつIPがx.x.x.xであればSSH接続許可
```

特定のIP接続のみを許可する場合は、事前にグローバルIPを確認しておきましょう。[CMAN](https://www.cman.jp/network/support/go_access.cgi)

ローカル内のみの接続制御であれば、ローカルアドレスの許可で問題ないです。

sshd_configでもIP制御は可能ですが、firewalldによるIP接続制御も可能です。その場合は下記のようにします。

```bash
#sshd_config
AllowUsers yunisan1211 admin

#許可されているサービスを表示する
firewall-cmd --list-services --zone=public

#sshがデフォルトで許可になっているが、今回は限定するのでサービスから消す
firewall-cmd --remove-service=ssh --zone=public --permanent

#richルールにより特定IPアドレス（ここでは192.168.11.29)のみ許可
firewall-cmd --permanent --zone=public --add-rich-rule="rule family="ipv4" source address="x.x.x.x" port protocol="tcp" port="x" accept"

#サービス再読み込み
firewall-cmd --reload

#設定確認
firewall-cmd --list-all --zone=public

# nmapを実行して、接続が許可されているIPアドレスレンジと許可されていないレンジでポートスキャニングができないことを確認する
nmap -p 5649 x.x.x.x
```

なお、sshd_configでAllowUsersで設定したユーザー以外は暗黙denyになるため、IP制御を掛けなくても問題はない。
しかし、すべてを疑うことを前提に考えれば設定することを推奨する。

Tips. awsを使用している場合のグローバルIPについて  
CMANで調べたグローバルIPを許可したがなぜか接続ができなかった。
その場合は、一度該当の装置にログインして、以下のコマンドで、TTYに接続しているホストIPを調べて許可してあげるとよい

コマンド  
`w -i`  

## タイムアウトを設定すべし

タイムアウトを設定することにより、一定時間操作がなかった場合には自動的にポートを閉じます。これにより不必要なポートの開放がなくなります。ただし、一定時間操作しないと閉じられてしまうためそのようなことがあっては困る場合は、他の対処法を考える必要性がありそうです。（例えば操作が終わったらログアウトするなど）

```bash
#生存確認を行うインターバルタイム（秒）を指定する
ClientAliveInterval 300
#生存確認をn回試行して応答がなければ閉じる
ClientAliveCountMax 6
#上記設定だと30分間はセッションが維持される
```

## その他のオプション

```bash
#SSH クライアントからの接続が何回失敗したら、リトライを打ち切るかを指定
MaxAuthTries 1

#ログインしようとしてからパスワードを入力するまでにかかる時間を制限する（15秒以内に正規パスを入力してログインをしないと例え正規のパスワードであってもログインに失敗する）
LoginGraceTime 15s

#公開鍵認証のみの接続を許可する
PasswordAuthentication no           # パスワードによる接続を拒否
ChallengeResponseAuthentication no  # これもパスワード認証の一貫なので拒否
PubkeyAuthentication yes  # これはデフォルトで yes なのでそのままでも OK

#ssh 接続した瞬間から認証OKとなるまでの間の最大接続数
MaxStartUps x:y:z

#非認証な接続が x を超えるとそれ以降の接続を y% の確率で拒否し、さらに z に達した時点でそれ以降の接続を全て拒否
```