PK
パーネルカニック研究所
今日もどこかでkernel panic。
root@pk-lab:~#

SSHサーバーを構築する

連載ナビ

この連載は「Linuxを使えるようになる」ではなく、「Linuxがどう動いているのか」を理解するための実践シリーズです。

今回は Linuxを理解する 〜 LFSで学ぶLinux内部構造 〜 の Part 6 / 第3回として、仕組みのつながりを意識しながら学びます。

—

SSHサーバーとは「TCP 22で待つ sshd デーモン + 鍵による認証」のこと

EC2やVPSに ssh で入るとき、接続先で何が動いているのか。結論から言うと、SSHサーバーの実体は次の組み合わせです。

  • sshd(OpenSSHサーバーのデーモン)が、TCPの22番ポートで接続を待ち受けている
  • クライアントと鍵交換を行って通信を暗号化し、ホスト鍵(/etc/ssh/ssh_host_*_key)でサーバーの正当性を、公開鍵認証やパスワードでユーザーの正当性を確認する
  • 認証が通ると、そのユーザー権限でシェルセッションを起動する

設定はほぼすべて /etc/ssh/sshd_config という1つのファイルに集約されています。つまりSSHサーバーの構築とは、「sshd を起動して22番でLISTENさせ、認証方式を設定すること」に尽きます。今回はこれを最小構成で実際に立ち上げます。

デーモン・ポート・認証の3層が分かれば、接続トラブルを切り分けられるから

なぜ内部構造まで理解するのか。理由は、SSHの接続トラブルが「どの層で失敗したか」でエラーメッセージが明確に変わるため、構造を知っていれば原因をほぼ即断できるからです。

クライアント側のエラー 失敗した層 主な原因
Connection timed out ネットワーク層 ファイアウォール、AWSのセキュリティグループ、経路なし
Connection refused デーモン層 sshd が起動していない、別ポートでLISTENしている
Permission denied (publickey). 認証層 公開鍵未登録、~/.ssh の権限不正、ユーザー名間違い

たとえばEC2で Permission denied (publickey). が出たときに、セキュリティグループを疑って時間を溶かす必要はありません。このメッセージが返ってきた時点で、TCP接続とsshdへの到達は成功しているからです。残る容疑者は鍵とユーザー名だけ、と即座に絞れます。これが前回までの「経路 → ソケット → アプリ」という層の見方の、実践での使い方です。

最小構成でSSHサーバーを立ち上げる

Part 1で作ったLinux VM(Ubuntu想定)で実際に構築します。


# 1. OpenSSHサーバーをインストール
sudo apt update && sudo apt install -y openssh-server

# 2. サービスを起動し、自動起動も有効化
#    (サービス名はUbuntuでは ssh、RHEL系では sshd)
sudo systemctl enable --now ssh 2>/dev/null || sudo systemctl enable --now sshd

# 3. 22番ポートでLISTENしているか確認
sudo ss -ltnp | grep :22

3のコマンドで次のような行が出れば、デーモン層まではできあがりです。


LISTEN 0 128  0.0.0.0:22  0.0.0.0:*  users:(("sshd",pid=612,fd=3))

0.0.0.0:22 は「すべてのインターフェースの22番で待ち受け」という意味でした(第1回参照)。あとは接続してみます。


# 自分自身に接続してみる(初回は fingerprint の確認で yes と答える)
ssh localhost

# ホスト側のIP(ip addr で確認)に対して、Macなど別マシンから接続する場合
ssh ユーザー名@192.168.64.5

初回接続時の The authenticity of host ... can't be established. という確認は、サーバーのホスト鍵を初めて受け取る際のものです。ここで yes と答えると ~/.ssh/known_hosts に記録され、以後同じサーバーの鍵が変わっていないかを毎回検証してくれます。

sshd_config を触るなら、sshd -t と「セッション維持」が命綱

構築の次は設定です。実運用で最初に行う定番の変更は「パスワード認証を無効化し、公開鍵認証のみにする」ことです。


# 1. クライアント側(例: Mac)で鍵ペアを作り、公開鍵をサーバーへ登録
ssh-keygen -t ed25519
ssh-copy-id ユーザー名@192.168.64.5

# 2. 鍵でログインできることを確認してから、サーバー側の設定を変更
sudo vi /etc/ssh/sshd_config

sshd_config では次の行を設定します(# でコメントアウトされていたら外します)。


PasswordAuthentication no
PubkeyAuthentication yes

ここで重要な作法が2つあります。

1. 反映前に必ず sudo sshd -t を実行する。 これは設定ファイルの文法チェックで、問題なければ何も出力されません。誤りがあれば /etc/ssh/sshd_config: line 57: Bad configuration option: ... のように行番号つきで教えてくれます。文法エラーのまま再起動すると sshd が起動できなくなります

2. 反映(sudo systemctl restart ssh)後も、いまのSSHセッションは切らずに、別ターミナルから新規接続を試す。 既存セッションは再起動後も維持されるので、もし設定ミスで入れなくなっても、生かしておいたセッションから修正できます。リモートサーバーで自分を締め出す事故を防ぐ、実務の基本動作です

認証で失敗する場合は、クライアント側で ssh -v ユーザー名@ホスト と実行すると、どの鍵を試してどの段階で拒否されたかが逐一表示されます。サーバー側のログは sudo journalctl -u ssh -f(Part 5参照)で追えます。

SSHは「デーモン・ポート・認証」の3層で理解し、3層で切り分ける

SSHサーバーの実体は、TCP 22で待つ sshd デーモンと /etc/ssh/sshd_config による認証設定であり、トラブルは「timeout=ネットワーク層 / refused=デーモン層 / Permission denied=認証層」で即座に切り分けられる——これが今回の結論です。

これでリモートのLinuxを安全に操作する入り口ができました。次のPart 7では、このサーバー上でnginxをソースコードからビルドします。apt install が隠している「configure → make → make install」の世界に入り、LFSで学んだビルドの知識を実際のミドルウェアに適用します。

図解テキスト


client ssh
  -> port 22                 … timeout / refused はここまでの問題
  -> sshd daemon             … /etc/ssh/sshd_config が設定
  -> 鍵交換 + 認証            … Permission denied はここの問題
  -> shell session

この回で実行するコマンド

上から順番に実行すれば、この回の内容を体験できます。

まずはそのままコピペして実行し、次に1行ずつ意味を確認してください。


sudo apt update && sudo apt install -y openssh-server
sudo systemctl enable --now ssh 2>/dev/null || sudo systemctl enable --now sshd
sudo ss -ltnp | grep :22
sudo sshd -t
ssh localhost

ハンズオン手順

1. ターミナルを開いて、上のコマンドを1行ずつ実行する

2. ss -ltnp | grep :22 の出力から、bindアドレスとPIDをメモする

3. ssh localhost で初回に表示される fingerprint の確認メッセージを読んでから yes と答える

4. 余裕があれば ssh -v localhost を実行し、認証がどの方式で通ったかをログから探す

5. エラーが出たら、そのエラーメッセージをそのまま残す

チェックポイント

  • コマンドが最後まで実行できた
  • Connection timed out / Connection refused / Permission denied がそれぞれどの層の失敗かを言える
  • sshd -t が何のためのコマンドか説明できる
  • 次の回に進む前に、分からない単語を1つ調べた

今回理解できたこと

  • SSHサーバーの実体は sshd デーモン + /etc/ssh/sshd_config + 鍵による認証
  • 接続可否は「ネットワーク(port)/ デーモン(listen)/ 認証(auth)」の3層で切り分ける
  • 設定変更時は sshd -t で文法確認し、既存セッションを残したまま新規接続で検証する