SELinuxとfirewalldでSSHポートを変更する方法
Rocky LinuxやAlmaLinuxでSSHポートを変更する手順です。firewalld、SELinuxのポートラベル、sshd_configをこの順に設定し、接続を切らずに安全に移行します。
ここで SSH ポートの変更に3つの手順が必要な理由
Rocky Linux、AlmaLinux、CentOS Stream、または Fedora で SSH ポートを変更する場合、1か所を編集するだけでは不十分です。新しいポートで接続できるかどうかは、3つの別々のシステムがそれぞれ判断します。firewalld はパケットをマシンまで通すかどうかを決定します。SELinux は sshd がそのポート番号にバインドすることを許可するかどうかを決定します。sshd_config はデーモンが要求するポートを決定します。SELinux の手順を省くと、デーモンは起動を拒否します。firewalld の手順を省くと、デーモンは起動して待ち受けますが、誰も接続できません。
Ubuntu では、Ubuntu が SELinux ではなく AppArmor を使用し、sshd がバインドできるポートを制限するプロファイルを標準で提供していないため、同じ作業は1回の編集と再起動で済みます。ufw が動作している場合は、ルールを1つ追加します。違いはそれだけです。RHEL 系では新規インストール時に firewalld が動作し、SELinux が enforcing になっており、どちらもポート番号を考慮します。
次の順序で作業すれば、すべての手順中に現在のセッションを維持できます。
- firewalld で新しいポートを開き、ここではポート 22 を開いたままにします。
semanageを使用して、新しいポートの SELinux ラベルを追加します。- sshd の設定でポートを指定します。
sshdを再起動し、最初の端末を閉じる前に、2つ目の端末から新しいポートでログインします。
作業を始める前に、プロバイダーの Web コンソール(VNC またはシリアル)を見つけ、そこからログインできることを確認してください。変更に失敗した場合、そのコンソールが復旧手段になります。ポートの変更は、料金を支払ったばかりのサーバーからテナント自身を締め出す原因として、特に多いものの1つです。
まず semanage をインストールします
semanage は SELinux ポリシー設定を編集するツールです。Rocky Linux または AlmaLinux の最小インストールには含まれていません。policycoreutils-python-utils に含まれています。
sudo dnf install -y policycoreutils-python-utilsこのパッケージをインストールする前にコマンドを実行すると、sudo: semanage: command not found が表示されます。この時点で、SELinux がインストールされていないと判断して手順を省略する読者が多くいます。SELinux はインストール済みです。不足しているのは管理ツールだけです。dnf の構文に慣れていない場合は、dnf と apt のコマンド対応表で、既知のコマンドとの対応を確認できます。
ポートを決め、使用されていないことを確認する
1024 から 65535 までの空いている TCP ポートを使用できます。決定する前に、次の 2 点を確認します。
sudo ss -tlnp | grep -w 2222
sudo semanage port -l | grep -w 22221 つ目のコマンドでは、その番号でプロセスがすでに待ち受けているかどうかを確認します。2 つ目のコマンドでは、SELinux ポリシーがそのポートを別のサービス種別に割り当てていないかを確認します。空いているポートでは、どちらのコマンドも何も返しません。ポリシーがすでにそのポートを使用している場合、手順 2 の semanage port -a は ValueError: Port tcp/2222 already defined で失敗します。その場合は、別の番号を選択します。
このガイドでは、例として 2222 を使用します。2222 は 22 の次にスキャナーが試す最初のポートでもあります。実際のサーバーでは、より推測されにくい番号を選択してください。
手順 1: firewalld でポートを開放する
sudo firewall-cmd --permanent --add-port=2222/tcp
sudo firewall-cmd --reload
sudo firewall-cmd --list-ports--permanent はルールをディスク上の zone ファイルに書き込みますが、稼働中の firewall には反映しません。--reload はディスク上の設定を稼働中の firewall に読み込みます。reload を省略すると、ルールは存在していても、firewalld が次回再起動するまで機能しません。このため、手順全体が理由なく失敗したように見えることがよくあります。
ここでは、ssh の service エントリはそのままにしておきます。このエントリによって port 22 が開いた状態に保たれるため、テスト中のフォールバックになります。
プロバイダーの control panel も確認してください。多くのホストでは、OS の外側で VPS の前段に network firewall を配置しています。そのため、firewalld で開放したポートでも、上流で破棄されることがあります。VPS 向け firewalld 基礎ガイドでは、zones と runtime と permanent の違いについて説明しています。この構成に慣れていない場合は参照してください。
手順 2: SELinux でポートにラベルを付ける
sudo semanage port -a -t ssh_port_t -p tcp 2222
sudo semanage port -l | grep ssh_port_t-a は新しいポート割り当てを追加します。-t ssh_port_t は SSH ポートに付与されるタイプです。2 つ目のコマンドは、現在 ssh_port_t が対象としているすべての項目を一覧表示します。デーモンを操作する前に、指定したポートが追加されたことを確認できます。
SELinux がポートをブロックする理由
SELinux(security-enhanced Linux)は、システム上のすべてのオブジェクトにラベルを付けます。TCP ポート番号も、ほかのオブジェクトと同様に対象になります。SSH デーモンは sshd_t というドメイン内で制限されて実行されます。ポリシーでは、sshd_t が ssh_port_t とラベル付けされた TCP ポートに bind することを許可しています。初期状態でこのラベルが付いているポートは 22 だけです。デーモンが 2222 への bind を要求すると、カーネルはラベルを確認します。その番号にポリシーが割り当てた汎用的なタイプを見つけると、ソケットに対する name_bind 権限を拒否します。
このため、この失敗はファイアウォールの問題とは異なります。待ち受けソケットが作成される前にカーネルが拒否するため、sshd がエラーを報告して終了します。ファイアウォールの問題はその逆です。デーモンは正常に実行されていますが、受信途中でパケットが破棄されます。
getenforce で、システムがどのモードで動作しているかを確認できます。Permissive では拒否が記録されますが、強制はされません。そのため、ポート変更が一見成功した後、誰かが setenforce 1 を実行した時点、またはシステムが enforcing モードで再起動した時点で機能しなくなります。どちらのモードでも、ポートにラベルを付けてください。サーバー向け SELinux 基礎ガイドでは、モード、コンテキスト、boolean について詳しく説明しています。
手順 3: sshd の設定でポートを指定する
Rocky Linux 9 と 10、AlmaLinux 9 と 10、および現行の Fedora では、/etc/ssh/sshd_config は include 行から始まるため、変更内容は drop-in ファイルに記述するのが適切です。これにより、パッケージの更新で編集内容が上書きされることはありません。
grep -n '^Include' /etc/ssh/sshd_config
echo 'Port 2222' | sudo tee /etc/ssh/sshd_config.d/10-port.conf
sudo sshd -tgrep の結果に Include 行がない場合、Rocky Linux 8 やその他の古いイメージが該当します。この場合は、Port 2222 を /etc/ssh/sshd_config に直接追加してください。sshd -t は drop-in を含む設定全体を解析し、構文エラーを報告します。再起動する前に、報告された問題をすべて修正してください。設定を解析できない場合、デーモンは再起動後に起動しません。
Port は複数回指定でき、sshd は指定されたすべてのポートで待ち受けます。最初の 1 日は Port 2222 と併せて Port 22 を残しておくと、問題が発生した場合の安全策になります。ただし、忘れずに削除してください。
sshd は socket unit で起動していますか?
一部のイメージでは、長時間実行するサービスとしてではなく、systemd の socket activation によって SSH を起動します。この構成では、systemd が待ち受けソケットを管理し、接続を sshd に渡します。そのため、sshd_config 内の Port 行は完全に無視されます。何かを再起動する前に確認してください。
systemctl is-enabled sshd.socketenabled という結果は、ポートが sshd_config ではなく socket unit に設定されていることを示します。
sudo systemctl edit sshd.socket[Socket]
ListenStream=
ListenStream=2222引数なしの ListenStream= が必要です。drop-in では値が累積するため、最初に空の設定でリストをクリアしないと、socket は 2222 だけでなく 22 でも待ち受け続けます。sudo systemctl daemon-reload で適用し、その後 sudo systemctl restart sshd.socket を実行してください。サーバー上で unit が無効になっているか、存在しない場合、このセクションは該当しません。
手順 4: 再起動後、2 つ目のターミナルからテストする
sudo systemctl restart sshd
systemctl status sshd
sudo ss -tlnp | grep sshdこのターミナルは開いたままにしてください。ログアウトしないでください。自分のマシンで 2 つ目のターミナルを開き、新しいポートで接続します。
ssh -p 2222 youruser@203.0.113.102 つ目のログインが成功してから、最初のセッションを閉じてください。接続できなくても、すべてを元に戻せるシェルが残っています。この習慣が、5 分で終わる変更と、プロバイダーのコンソールで午後を費やす事態を分けます。
ファイアウォールによる破棄か SELinux による拒否かを見分ける方法
ノート PC からは、2 つの失敗はほぼ同じに見えます。サーバー上では、まったく異なる状態です。
systemctl status sshdに unit の失敗が表示される場合、デーモンはソケットを取得できていません。設定エラーまたは SELinux による拒否です。- unit が active で、
ss -tlnpに新しいポートで sshd がバインドされていると表示される場合、デーモンに問題はありません。問題はネットワーク経路にあります。firewalld、プロバイダーが別途提供するファイアウォール、または接続先に指定したアドレスとポートを確認してください。
SELinux が原因の場合は、推測せずに audit レコードを確認します。
sudo ausearch -m AVC -ts recent
sudo journalctl -u sshd -n 50 --no-pagername_bind の拒否レコードは、class tcp_socket に対して、comm="sshd" にプロセス、src= にポート番号、tcontext= にそのポートが実際に持つラベルを示します。最後のフィールドが答えです。ssh_port_t 以外の値であれば、使用しているポートに手順 2 が適用されていません。通常は、ポート番号の入力ミスか、プロトコルの指定間違いです。レコードを文章に変換する sealert を使用したい場合は、setroubleshoot-server をインストールしてください。
カーネルがバインドを拒否したときに sshd 自身が出力するメッセージは、次のようになります。
error: Bind to port 2222 on 0.0.0.0 failed: Permission denied.root 権限なしでバインドできる 1024 より大きいポートでの Permission denied は、SELinux による拒否の特徴です。同じ行にある Address already in use は別の問題を示します。別のプロセスがそのポートを使用しています。クライアント側では、connection refused と connection timed out の違いによって、2 つのネットワーク上のケースを区別できます。refused はパケットがホストに到達し、待ち受けているプロセスがなかったことを意味します。一方、timeout は応答がまったくなかったことを意味します。
ポート 22 を閉じてクライアントを更新する
新しいポートで複数回のログインに成功したら、22 番ポートへのアクセスを停止します。
sudo firewall-cmd --permanent --remove-service=ssh
sudo firewall-cmd --reload
sudo firewall-cmd --list-allポート 22 の SELinux ラベルは変更しないでください。これはベースポリシーに由来するものであり、ファイアウォールがパケットの受信を停止すれば、何も許可しません。
次にクライアントを修正します。デフォルトポートを前提としていたすべてのツールに、新しいポートを指定する必要があるためです。毎回 -p と入力する代わりに、自分のマシンの ~/.ssh/config に一度だけ設定します。
Host myvps
HostName 203.0.113.10
Port 2222
User youruserscp、sftp、rsync、Ansible はすべてこのファイルを読み取ります。ポート 22 を直接指定しているバックアップジョブ、監視チェック、cron スクリプトは対象外です。変更内容を把握しているうちに、それらも探して修正してください。
ポート変更で得られることと得られないこと
ログのノイズを減らせます。自動スキャナーは常に 22 番ポートを攻撃するため、別のポートに移すと、journal に記録されるその種の行の大部分がなくなり、本当のイベントを確認しやすくなります。ただし、これはセキュリティ対策ではありません。全ポート範囲を走査するスキャナーなら、デーモンを見つけてバージョンバナーも読み取れます。ポート変更は整理のための対策と考え、実際の保護には、パスワードログインを無効にした鍵認証のみを使用してください。手順は VPS の SSH セキュリティ強化ガイドで順を追って説明しています。
上記の内容は、主要な 2 つの RHEL 再構築版で同じように機能します。どちらも同じソースから構築されているためです。まだどちらを選ぶか決めていない場合は、Rocky Linux と AlmaLinux の比較を参照してください。古いガイドに従う前に、cat /etc/os-release で実際に提供されたリリースを確認してください。Rocky Linux 8 向けに書かれたガイドは現在も検索上位に表示され、semanage と firewall-cmd の手順も正しいままです。ただし、Rocky 8 には sshd_config.d の include 行がなく、考慮すべき socket unit もありません。そのため、これらのガイドにある sshd 部分は現在のシステムには適合しません。
fail2ban に新しいポートを認識させる必要があります
fail2ban は標準リポジトリには含まれていません。EPEL(Enterprise Linux 用の追加パッケージ)から導入します。
sudo dnf install -y epel-release
sudo dnf install -y fail2ban fail2ban-firewalldfail2ban-firewalld サブパッケージを使うと、fail2ban は firewalld 経由で ban ルールを書き込みます。firewalld がルールセットを管理するサーバーでは、この構成を使用します。
標準の sshd jail では port = ssh が設定されており、この名前は /etc/services によって 22 に解決されます。変更後は、攻撃されていないポートを jail が監視するため、2222 へのログイン失敗が蓄積しても、誰も ban されません。/etc/fail2ban/jail.local にはポート番号を設定します。
[sshd]
enabled = true
port = 2222
backend = systemd
maxretry = 5
bantime = 3600backend = systemd は /var/log/secure ではなく journal から失敗を読み取ります。最小構成のインストールで rsyslog が存在しない可能性がある場合は、こちらの方が安全です。sudo systemctl enable --now fail2ban で起動し、sudo fail2ban-client status sshd で jail を確認します。jail の構文は、Ubuntu 24.04 での fail2ban による SSH の設定と同じです。異なるのは、パッケージの取得元と ban action だけです。
パッチ適用はポート変更より重要です
SSH ポートを変更していても、4 か月間セキュリティ更新を適用していないサーバーは、22 番ポートで稼働しながら毎晩自動的にパッチを適用するサーバーよりも危険な状態です。すでに root になっている間に、同じセッションで自動更新も有効にしてください。Rocky Linux と AlmaLinux の自動 dnf 更新では、タイマーの設定と、更新をダウンロードするだけにするか適用まで行うかの選択について説明しています。
FAQ
Rocky Linux でポートを変更した後、sshd の起動に失敗するのはなぜですか?
ほとんどの場合、SELinux のポートラベルが不足しています。sshd は sshd_t ドメインで制限された状態で動作し、ポリシーでは ssh_port_t とラベル付けされたポートにしかバインドできません。デフォルトでは、これはポート 22 だけです。カーネルがバインドを拒否するため、デーモンは待ち受けを開始せずに終了します。journalctl -u sshd には error: Bind to port 2222 on 0.0.0.0 failed: Permission denied. の形式の行があります。sudo semanage port -a -t ssh_port_t -p tcp 2222 を自分で使用するポート番号に置き換えて実行し、その後サービスを再起動してください。semanage が見つからない場合は、先に policycoreutils-python-utils をインストールしてください。
SELinux が permissive モードでも semanage は必要ですか?
はい。permissive モードでは拒否が記録される一方、バインド自体は許可されるため、変更が機能したように見えます。しかし、ラベルは不足したままです。誰かが setenforce 1 を実行した場合や、/etc/selinux/config に SELINUX=enforcing を設定した状態でホストを起動した場合、その時点で sshd はそのポートで起動しなくなります。ラベルの追加は 1 つのコマンドで済み、明確な原因がないまま数週間後に発生する障害を防げます。
ポートにラベルが付いていて sshd も実行中なのに、接続がタイムアウトするのはなぜですか?
デーモンが実行中なら SELinux による制限は満たされています。そのため、パケットは受信途中で破棄されています。sudo firewall-cmd --list-ports で使用ポートを確認し、--permanent ルールを追加した後に firewall-cmd --reload を実行したことを確認してください。永続ルールを追加しただけでは、実行中のファイアウォールには反映されません。次に、VPS の前段に別のネットワークファイアウォールがないか、ホストのコントロールパネルで確認します。ここでも通信が遮断されることがあり、その場合はオペレーティングシステム内の設定には何も表示されません。
22 の代わりにどのポートを使用すべきですか?
1024 から 65535 までの、空いている TCP ポートであれば使用できます。実際のサーバーでは 2222 と 22222 を避けてください。スキャナーは 22 の直後にこれらのポートも試すためです。sudo ss -tlnp で番号が空いていることを確認し、sudo semanage port -l で SELinux ポリシーがその番号をすでに使用していないことを確認してください。また、後でインストールする可能性があるサービスに割り当てられたポートは避けます。覚えにくい大きな番号でも問題ありません。~/.ssh/config に 1 度記述すれば、以後入力する必要はないためです。