🔒
ssh のセキュリティ対策をまとめてみた
いくつかのサイトを巡って ssh のセキュリティ対策をまとめてみました。
/etc/ssh/sshd_config で対応可能な対策
詳細は sshd_config(5) の man ページの参照をお勧めします。
方針
- 公開鍵認証方式だけ有効にし、それ以外の認証方式は無効
- ユーザー単位、接続元 IP 単位で接続を許可する
- グループ単位で接続を許可する
- ポートフォワーディングは禁止
- ログは詳細めに取得
施策
- プロトコルを Version 2 に限定する
Protocol 2 - 空のパスワードを使用するユーザーをブロックする
PermitEmptyPasswords no - root によるログインを禁止する
PermitRootLogin no - パスワード認証によるログインを禁止する
PasswordAuthentication no - チャレンジ / レスポンス認証を無効にする
ChallengeResponseAuthentication no - キーボードインターフェースによる認証を無効にする
KbdInteractiveAuthentication no - GSSAPI(Generic Security Service API)ベースのユーザ認証を無効にする
GSSAPIAuthentication no - Kerberos 認証を無効にする
KerberosAuthentication no - SSH 鍵認証を使用する
PubkeyAuthentication yes - 暗号強度の強い鍵に制限する
HostKey /etc/ssh/ssh_host_ed25519_key - IP アドレスによるアクセス制限をする
AllowUsers @131.113.0.0/16
AllowUsers @192.168.1. @10.0.0. !@192.168.1.2 - 特定のユーザーだけにアクセスを制限する
AllowUsers alice bob - 特定のグループだけにアクセスを制限する
AllowGroups example-group - 同時接続数を制限する
MaxStartups 10:30:100 - ポートフォワーディングを禁止する
AllowTcpForwarding no
AllowStreamLocalForwarding no
GatewayPorts no
PermitTunnel no - 認証サービスのログを出力する
SyslogFacility AUTHPRIV - 詳細なログを取得する
LogLevel VERBOSE
設定ルール
- /etc/ssh/sshd_config ファイルは編集しない(デフォルト状態のまま)
- /etc/ssh/sshd_config.d/ ディレクトリー内に以下の命名規則に則ったファイルを作成し、変更内容を記述する
- /etc/ssh/sshd_config.d/50-redhat.conf の前に読み込ませる場合
49 以下の 2 桁の数字 + 名前 + .conf
例 49-crypto-policy-override.conf
- /etc/ssh/sshd_config.d/50-redhat.conf の前に読み込ませる場合
- /etc/ssh/sshd_config.d/ ディレクトリー内のファイルは辞書式順序で読み込まれる[1]
構文チェック
sudo /usr/sbin/sshd -t
現在の設定値の確認
sudo /usr/sbin/sshd -T
変更後に sshd デーモンを再読み込みして設定を反映
sudo systemctl reload sshd.service
他のシステム等との組み合わせが必要な対策
- デフォルトのポート番号を変更する
Port 22 → Port xxxxx
未使用ポートをアサインする必要あり
ファイアウォールに新しいポートの穴開け、22 番ポートの穴を閉じる - 暗号化ポリシーをより強いものに変更する
DEFAULT → FUTURE
/etc/crypto-policies/config
update-crypto-policies --set FUTURE
要 reboot - 安全ではないプロトコルの使用禁止
telnet, rsh, rlogoin, ftp 等 - 踏み台サーバー(ジャンプホスト)経由で接続
- fail2ban をインストールする
- 2 要素認証を使用する ⇒ トークン(Google Authenticator 等)との組み合わせ
libpam-google-authenticator
Ansible を使用した設定
Alma Linux 9.3 のノードに対し、Ansible を使用して以下の範囲でセキュリティ対策を実施します。
- /etc/ssh/sshd_config で対応可能な対策 ※AllowGroups は除く
- 暗号化ポリシーをより強いものに変更する
現状確認
/etc/ssh/sshd_config を上書きする設定を確認します。
[opadmin@node1 ~]$ sudo ls -l /etc/ssh/sshd_config.d/
total 4
-rw-------. 1 root root 719 Sep 27 2023 50-redhat.conf
[opadmin@node1 ~]$
50-redhat.conf が存在します。記載内容を確認します。
[opadmin@node1 ~]$ sudo cat /etc/ssh/sshd_config.d/50-redhat.conf
# This system is following system-wide crypto policy. The changes to
# crypto properties (Ciphers, MACs, ...) will not have any effect in
# this or following included files. To override some configuration option,
# write it before this block or include it before this file.
# Please, see manual pages for update-crypto-policies(8) and sshd_config(5).
Include /etc/crypto-policies/back-ends/opensshserver.config
SyslogFacility AUTHPRIV
ChallengeResponseAuthentication no
GSSAPIAuthentication yes
GSSAPICleanupCredentials no
UsePAM yes
X11Forwarding yes
# It is recommended to use pam_motd in /etc/pam.d/sshd instead of PrintMotd,
# as it is more configurable and versatile than the built-in version.
PrintMotd no
[opadmin@node1 ~]$
今回の作業で関係する部分です。
パラメーター | 設定されている値 | 変更後の値 | 備考 |
---|---|---|---|
SyslogFacility | AUTHPRIV | - | 変更しない |
ChallengeResponseAuthentication | no | - | 変更しない |
GSSAPIAuthentication | yes | no |
play
/etc/ssh/sshd_config.d/49-crypto-policy-override.conf を作成し、その中に変更する設定を記述します。一部は /etc/ssh/sshd_config.d/50-redhat.conf を変更しています。 update-crypto-policies
コマンドの実行以外は ansible.builtin.lineinfile
モジュールで一つずつ設定します。
ssh_security.yml
---
- name: SSH security measures with Ansible
hosts: node1
gather_facts: false
vars:
FilePath: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf
FilePath2: /etc/ssh/sshd_config.d/50-redhat.conf
tasks:
- name: Limit protocol to Version 2
ansible.builtin.lineinfile:
path: "{{ FilePath }}"
create: true
state: present
regexp: '^Protocol'
line: 'Protocol 2'
owner: root
group: root
mode: 0600
become: true
- name: Block users with empty passwords
ansible.builtin.lineinfile:
path: "{{ FilePath }}"
create: true
state: present
regexp: '^PermitEmptyPasswords'
line: 'PermitEmptyPasswords no'
owner: root
group: root
mode: 0600
become: true
- name: Disallow login by root
ansible.builtin.lineinfile:
path: "{{ FilePath }}"
create: true
state: present
regexp: '^PermitRootLogin'
line: 'PermitRootLogin no'
owner: root
group: root
mode: 0600
become: true
- name: Prohibit login by password authentication
ansible.builtin.lineinfile:
path: "{{ FilePath }}"
create: true
state: present
regexp: '^PasswordAuthentication'
line: 'PasswordAuthentication no'
owner: root
group: root
mode: 0600
become: true
- name: Disable challenge/response authentication
ansible.builtin.lineinfile:
path: "{{ FilePath }}"
create: true
state: present
regexp: '^ChallengeResponseAuthentication'
line: 'ChallengeResponseAuthentication no'
owner: root
group: root
mode: 0600
become: true
- name: Disable authentication using the keyboard interface
ansible.builtin.lineinfile:
path: "{{ FilePath }}"
create: true
state: present
regexp: '^KbdInteractiveAuthentication'
line: 'KbdInteractiveAuthentication no'
owner: root
group: root
mode: 0600
become: true
- name: Disable GSSAPI-based user authentication
ansible.builtin.lineinfile:
path: "{{ FilePath2 }}"
create: true
state: present
regexp: '^GSSAPIAuthentication'
line: 'GSSAPIAuthentication no'
owner: root
group: root
mode: 0600
become: true
- name: Disable Kerberos authentication
ansible.builtin.lineinfile:
path: "{{ FilePath }}"
create: true
state: present
regexp: '^KerberosAuthentication'
line: 'KerberosAuthentication no'
owner: root
group: root
mode: 0600
become: true
- name: Using SSH Key Authentication
ansible.builtin.lineinfile:
path: "{{ FilePath }}"
create: true
state: present
regexp: '^PubkeyAuthentication'
line: 'PubkeyAuthentication yes'
owner: root
group: root
mode: 0600
become: true
- name: Restrict access by IP address
ansible.builtin.lineinfile:
path: "{{ FilePath }}"
create: true
state: present
regexp: '^AllowUsers'
line: 'AllowUsers *@192.168.0.0/24'
owner: root
group: root
mode: 0600
become: true
- name: Restrict to keys with strong cryptographic strength
ansible.builtin.lineinfile:
path: "{{ FilePath }}"
create: true
state: present
regexp: '^HostHostKey'
line: 'HostKey /etc/ssh/ssh_host_ed25519_key'
owner: root
group: root
mode: 0600
become: true
- name: Limit the number of simultaneous connections
ansible.builtin.lineinfile:
path: "{{ FilePath }}"
create: true
state: present
regexp: '^MaxStartups'
line: 'MaxStartups 3:30:10'
owner: root
group: root
mode: 0600
become: true
- name: Disable port forwarding
ansible.builtin.lineinfile:
path: "{{ FilePath }}"
create: true
state: present
regexp: '^AllowTcpForwarding'
line: 'AllowTcpForwarding no'
owner: root
group: root
mode: 0600
become: true
- name: Disallow local forwarding of streams
ansible.builtin.lineinfile:
path: "{{ FilePath }}"
create: true
state: present
regexp: '^AllowStreamLocalForwarding'
line: 'AllowStreamLocalForwarding no'
owner: root
group: root
mode: 0600
become: true
- name: Do not allow port relay
ansible.builtin.lineinfile:
path: "{{ FilePath }}"
create: true
state: present
regexp: '^GatewayPorts'
line: 'GatewayPorts no'
owner: root
group: root
mode: 0600
become: true
- name: Tunnels not allowed
ansible.builtin.lineinfile:
path: "{{ FilePath }}"
create: true
state: present
regexp: '^PermitTunnel'
line: 'PermitTunnel no'
owner: root
group: root
mode: 0600
become: true
- name: Output logs for authentication services
ansible.builtin.lineinfile:
path: "{{ FilePath }}"
create: true
state: present
regexp: '^SyslogFacility'
line: 'SyslogFacility AUTHPRIV'
owner: root
group: root
mode: 0600
become: true
- name: Obtain detailed logs
ansible.builtin.lineinfile:
path: "{{ FilePath }}"
create: true
state: present
regexp: '^LogLevel'
line: 'LogLevel VERBOSE'
owner: root
group: root
mode: 0600
become: true
- name: Change encryption policy to a stronger one
ansible.builtin.command:
cmd: 'update-crypto-policies --set FUTURE'
become: true
- name: Restart the node
ansible.builtin.reboot:
become: true
play の実行結果
各モジュールの実行前後の状態がわかるよう --diff
オプションを付けて実行しました。
y_mrok@ctrl:~/ex$ ansible-playbook ssh/ssh_security.yml --diff
PLAY [SSH security measures with Ansible] *********************************************************************************************
TASK [Limit protocol to Version 2] ****************************************************************************************************
--- before: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf (content)
+++ after: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf (content)
@@ -0,0 +1 @@
+Protocol 2
--- before: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf (file attributes)
+++ after: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf (file attributes)
@@ -1 +1 @@
-mode: '0644'
+mode: '0600'
changed: [node1]
TASK [Block users with empty passwords] ***********************************************************************************************
--- before: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf (content)
+++ after: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf (content)
@@ -1 +1,2 @@
Protocol 2
+PermitEmptyPasswords no
changed: [node1]
TASK [Disallow login by root] *********************************************************************************************************
--- before: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf (content)
+++ after: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf (content)
@@ -1,2 +1,3 @@
Protocol 2
PermitEmptyPasswords no
+PermitRootLogin no
changed: [node1]
TASK [Prohibit login by password authentication] **************************************************************************************
--- before: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf (content)
+++ after: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf (content)
@@ -1,3 +1,4 @@
Protocol 2
PermitEmptyPasswords no
PermitRootLogin no
+PasswordAuthentication no
changed: [node1]
TASK [Disable challenge/response authentication] **************************************************************************************
--- before: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf (content)
+++ after: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf (content)
@@ -2,3 +2,4 @@
PermitEmptyPasswords no
PermitRootLogin no
PasswordAuthentication no
+ChallengeResponseAuthentication no
changed: [node1]
TASK [Disable authentication using the keyboard interface] ****************************************************************************
--- before: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf (content)
+++ after: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf (content)
@@ -3,3 +3,4 @@
PermitRootLogin no
PasswordAuthentication no
ChallengeResponseAuthentication no
+KbdInteractiveAuthentication no
changed: [node1]
TASK [Disable GSSAPI-based user authentication] ***************************************************************************************
--- before: /etc/ssh/sshd_config.d/50-redhat.conf (content)
+++ after: /etc/ssh/sshd_config.d/50-redhat.conf (content)
@@ -9,7 +9,7 @@
ChallengeResponseAuthentication no
-GSSAPIAuthentication yes
+GSSAPIAuthentication no
GSSAPICleanupCredentials no
UsePAM yes
changed: [node1]
TASK [Disable Kerberos authentication] ************************************************************************************************
--- before: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf (content)
+++ after: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf (content)
@@ -4,3 +4,4 @@
PasswordAuthentication no
ChallengeResponseAuthentication no
KbdInteractiveAuthentication no
+KerberosAuthentication no
changed: [node1]
TASK [Using SSH Key Authentication] ***************************************************************************************************
--- before: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf (content)
+++ after: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf (content)
@@ -5,3 +5,4 @@
ChallengeResponseAuthentication no
KbdInteractiveAuthentication no
KerberosAuthentication no
+PubkeyAuthentication yes
changed: [node1]
TASK [Restrict access by IP address] **************************************************************************************************
--- before: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf (content)
+++ after: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf (content)
@@ -6,3 +6,4 @@
KbdInteractiveAuthentication no
KerberosAuthentication no
PubkeyAuthentication yes
+AllowUsers *@192.168.0.0/24
changed: [node1]
TASK [Restrict to keys with strong cryptographic strength] ****************************************************************************
--- before: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf (content)
+++ after: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf (content)
@@ -7,3 +7,4 @@
KerberosAuthentication no
PubkeyAuthentication yes
AllowUsers *@192.168.0.0/24
+HostKey /etc/ssh/ssh_host_ed25519_key
changed: [node1]
TASK [Limit the number of simultaneous connections] ***********************************************************************************
--- before: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf (content)
+++ after: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf (content)
@@ -8,3 +8,4 @@
PubkeyAuthentication yes
AllowUsers *@192.168.0.0/24
HostKey /etc/ssh/ssh_host_ed25519_key
+MaxStartups 3:30:10
changed: [node1]
TASK [Disable port forwarding] ********************************************************************************************************
--- before: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf (content)
+++ after: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf (content)
@@ -9,3 +9,4 @@
AllowUsers *@192.168.0.0/24
HostKey /etc/ssh/ssh_host_ed25519_key
MaxStartups 3:30:10
+AllowTcpForwarding no
changed: [node1]
TASK [Disallow local forwarding of streams] *******************************************************************************************
--- before: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf (content)
+++ after: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf (content)
@@ -10,3 +10,4 @@
HostKey /etc/ssh/ssh_host_ed25519_key
MaxStartups 3:30:10
AllowTcpForwarding no
+AllowStreamLocalForwarding no
changed: [node1]
TASK [Do not allow port relay] ********************************************************************************************************
--- before: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf (content)
+++ after: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf (content)
@@ -11,3 +11,4 @@
MaxStartups 3:30:10
AllowTcpForwarding no
AllowStreamLocalForwarding no
+GatewayPorts no
changed: [node1]
TASK [Tunnels not allowed] ************************************************************************************************************
--- before: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf (content)
+++ after: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf (content)
@@ -12,3 +12,4 @@
AllowTcpForwarding no
AllowStreamLocalForwarding no
GatewayPorts no
+PermitTunnel no
changed: [node1]
TASK [Output logs for authentication services] ****************************************************************************************
--- before: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf (content)
+++ after: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf (content)
@@ -13,3 +13,4 @@
AllowStreamLocalForwarding no
GatewayPorts no
PermitTunnel no
+SyslogFacility AUTHPRIV
changed: [node1]
TASK [Obtain detailed logs] ***********************************************************************************************************
--- before: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf (content)
+++ after: /etc/ssh/sshd_config.d/49-crypto-policy-override.conf (content)
@@ -14,3 +14,4 @@
GatewayPorts no
PermitTunnel no
SyslogFacility AUTHPRIV
+LogLevel VERBOSE
changed: [node1]
TASK [Change encryption policy to a stronger one] *************************************************************************************
changed: [node1]
TASK [Restart the node] ***************************************************************************************************************
changed: [node1]
PLAY RECAP ****************************************************************************************************************************
node1 : ok=20 changed=20 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
y_mrok@ctrl:~/ex$
実行前後の比較
play の実行前後の設定状態の比較結果です。左側が実行前、右側が実行後です。パラメーター名でソート済みです。/etc/ssh/sshd_config.d/49-crypto-policy-override.conf の設定内容や
/etc/ssh/sshd_config.d/50-redhat.conf の変更内容が反映されていることを確認できます。
暗号化ポリシーは FUTURE になっています。
[opadmin@node1 ~]$ sudo cat /etc/crypto-policies/config
FUTURE
[opadmin@node1 ~]$
Discussion