SSH
暗号化した通信路で遠隔ログインやコマンド実行を行うプロトコルである。接続先のサーバと利用者を認証し、通信内容の機密性と完全性を保護する。通常はTCPの22番ポートを使う。
イマシルちゃんの解説
離れた場所のサーバを操作するとき、入力したパスワードやコマンドが途中で読まれたら困るよね。
SSHは、そのやり取りを暗号化して遠隔操作できるプロトコルだよ。
通信を始めるときには接続先が本物かを確かめてから、ログインが許可された利用者かも確認するんだ。
この確認を省くと、偽物のサーバと暗号化して話してしまうおそれがあるの。
接続できたあとは操作の指示も実行結果も保護されるから、サーバやルータを手元の端末から管理できるよ。
暗号化と相手の確認を組み合わせることが大切なんだね。
この解説はわかりやすかったですか?
より深い解説
遠隔操作を保護する仕組み
SSHはSecure Shellの略である。管理者の端末からサーバやネットワーク機器へ接続し、シェルでの操作やコマンド実行を行う。平文で通信するTelnetと異なり、認証と暗号によって通信を保護する。SSHはアプリケーション層のプロトコルであり、通常は信頼できるデータの配送をTCPに任せる。
SSHの中でも役割は分かれている。最初に安全な通信路を作り、その上で利用者を認証し、最後に操作用の通信を開始する。RFC 4251では次の三つのプロトコルで構成される。
| 構成 | 担当する処理 |
|---|---|
| トランスポートプロトコル | サーバ認証、鍵交換、暗号化、改ざん検知を行う |
| ユーザ認証プロトコル | 接続してきた利用者を認証する |
| コネクションプロトコル | シェルや転送など複数の論理チャネルを一つの接続で扱う |
ここでいう「トランスポート」はSSH内部の役割名である。OSI基本参照モデルのトランスポート層そのものをSSHが置き換えるわけではない。
本記事はSSHバージョン2を扱う。旧バージョンのSSH1を前提に設定してはならない。代表的な実装であるOpenSSHも、バージョン7.6でSSH1の対応を削除している。
接続を開始してから操作できるまで
通常の接続では、クライアントからサーバのTCP 22番ポートへ接続する。待受ポートを変更した環境では設定された番号を使うが、番号の変更だけで認証や暗号化が強くなるわけではない。
接続開始時の大まかな順序は次のようになる。
- TCP接続を確立し、SSHのバージョン情報を交換する。
- 双方が対応する暗号方式や鍵交換方式を提示し、使用する組合せを決める。
- 鍵交換で通信に使う鍵を導き、サーバが示した鍵と署名を検証する。
- 暗号化と改ざん検知が有効になった通信路で利用者を認証する。
- 認証に成功すると論理チャネルを開き、シェルやファイル転送などを開始する。
大量の操作データを毎回公開鍵暗号で暗号化するわけではない。実際の通信内容は効率よく処理できる共通鍵暗号で保護し、鍵交換や署名には別の役割を持たせる。改ざんの検知にはメッセージ認証コードであるMAC、または暗号化と認証を組み合わせたAEAD方式などを用いる。具体的な組合せは双方の対応状況と設定で決まる。通信路の確立と方式の交渉はRFC 4253で定義されている。
ここで保護するのは、盗み見されない性質である機密性と、データが改変されていないことを確かめる完全性である。暗号化されていても、接続した端末自体が乗っ取られていれば操作内容や認証情報を守れない。
接続先と利用者の確認を分ける
SSHでは「利用者が正しいサーバへ接続しているか」と「サーバを使おうとする利用者が誰か」を別々に確認する。公開鍵認証で使う鍵も一組とは限らない。
| 確認する相手 | 鍵の役割 | OpenSSHでよく使う管理場所 |
|---|---|---|
| 接続先のサーバ | サーバを識別するホスト鍵を使う | クライアントの ~/.ssh/ ディレクトリにある known_hosts ファイルに、信頼したサーバの公開鍵を記録する |
| ログインする利用者 | 利用者が自分の秘密鍵を持つことを署名で示す | サーバ側の利用者ごとの ~/.ssh/ ディレクトリにある authorized_keys ファイルに、許可する公開鍵を登録する |
サーバ側へ登録する利用者の公開鍵は、サーバ自身を識別する鍵ではない。また、利用者の秘密鍵をサーバへ配布して認証する方式でもない。利用者側の鍵を交換しても、サーバ側の鍵の変更警告が解消するとは限らない。
公開鍵認証では、クライアントが接続に固有の情報などへデジタル署名を付ける。サーバは登録された公開鍵で署名を検証し、対応する秘密鍵を持つ利用者であることを確認する。秘密鍵そのものは送信しない。これは「公開鍵で暗号化したパスワードを送る」という処理とは異なる。認証手順はRFC 4252に規定されている。
パスワード認証も利用できる。パスワードは先に確立した暗号化通信路の中で送るため、SSHを利用したパスワード認証を平文のTelnet認証と同一視してはならない。ただし、弱いパスワードや使い回し、偽サーバへの接続まで暗号化で解決できるわけではない。
初回接続と鍵の変更をどう扱うか
初めてのサーバへ接続すると、クライアントは提示された公開鍵をまだ知らないことがある。OpenSSHでは鍵の指紋であるフィンガープリントを表示して確認を求める。表示内容を管理者から別の信頼できる経路で受け取った値と照合し、正しい鍵か確かめる。接続相手自身から得た情報だけを信じると、偽物の鍵を登録するおそれがある。
一度信頼した鍵を記録しておけば、次回からは接続先が以前と同じ鍵を使っているか確認できる。鍵の変更警告には、サーバの再構築や正規の鍵更新のほか、別の機器への誤接続や中間者攻撃の可能性もある。警告を消すためだけに登録を削除せず、変更理由を確かめる。
OpenSSHの StrictHostKeyChecking はこの確認方法を制御する。ホスト証明書を使い、信頼する認証局の署名で多数のサーバの鍵を管理する構成もある。OpenSSHの証明書は、HTTPSなどで使うX.509証明書とは形式が異なる。確認動作はクライアント設定の公式マニュアル、証明書の管理はssh-keygenの公式マニュアルで確認できる。
利用者の秘密鍵には適切なアクセス権を設定し、必要に応じてパスフレーズで保護する。パスフレーズは秘密鍵を利用するための保護であり、サーバへ送信するログインパスワードとは役割が違う。多数の鍵を扱う場合はssh-agentが署名処理を仲介できるが、エージェント転送先から鍵を使われる可能性があるため、転送を無条件に有効化しない。
ポート転送では入口と出口を確認する
SSHのポート転送は、別のTCP接続のデータをSSHの暗号化通信路へ通す機能である。シェルと転送は独立した論理チャネルで運べる。RFC 4254はこの多重化とTCP転送を定めている。
OpenSSHの主な指定は次の三つである。ローカルとリモートはSSHクライアントから見た位置を表す。
| 指定 | 接続を受け付ける場所 | 転送先への接続を作る側 |
|---|---|---|
| -L | クライアント側の指定したポート | SSHサーバ側 |
| -R | SSHサーバ側の指定したポート | クライアント側 |
| -D | クライアント側のSOCKSプロキシ | SSHサーバ側。宛先はSOCKSの要求ごとに決まる |
例えば、管理端末から踏み台サーバへSSH接続し、その先にある社内Webサーバへ接続する場合を考える。
ssh -N -L 127.0.0.1:8080:intranet.example:80 [email protected]
管理端末の 127.0.0.1:8080 へ接続すると、SSHサーバ側から intranet.example:80 への接続が作られる。-N は遠隔コマンドを実行せず転送に使う指定である。入口をループバックアドレスへ限定しているため、他の端末へ不用意に公開する構成にはならない。
この例でSSHが暗号化するのは管理端末から踏み台サーバまでである。踏み台から社内Webサーバまでの区間は通常のHTTP通信となる。終点まで保護する必要があれば、その区間にもHTTPSなどを使う。「SSHのトンネルに入れたから経路全体が暗号化された」とは判断できない。
サーバ側では AllowTcpForwarding や PermitOpen などで転送の許可範囲を制御できる。ただし、任意のシェル操作を許可された利用者は別の転送手段を実行できる場合がある。転送設定だけを完全なアクセス制御と考えず、利用者の権限と接続先も合わせて制限する。コマンドの指定はsshのマニュアル、許可設定はsshd_configのマニュアルを参照する。
ファイル転送と踏み台接続に利用する
SSHの上でファイル操作を行う代表的なプロトコルがSFTPである。サーバと利用者の認証や暗号化はSSHを利用し、ファイルの送受信や一覧取得を行う。FTPをTLSで保護するFTPSとは別の方式である。SFTPの動作はOpenSSHの公式マニュアルで確認できる。
scp というコマンド名と、旧来のSCPプロトコルも区別する。現在のOpenSSHの scp コマンドは、既定ではSFTPを使って転送する。コマンド名だけで内部のプロトコルを断定してはならない。scpの公式マニュアルには旧方式へ切り替える指定との違いが示されている。
管理対象へ直接接続できない構成では、踏み台を経由する ProxyJump、または短い指定の -J を使える。管理端末から最終接続先までのSSH接続を踏み台経由で作るため、踏み台と最終接続先の認証・到達性をそれぞれ確認する。秘密鍵を踏み台へコピーすることを前提にした構成ではない。
設定と障害を段階ごとに切り分ける
OpenSSHではクライアント側の設定を ssh_config、サーバ側の設定を sshd_config で行う。利用者認証の許可方式や待受アドレスを変えるのは主に後者である。ファイアウォールでは管理用ネットワークなど必要な送信元に接続を限定し、SSHの認証とネットワーク上のアクセス制御を組み合わせる。
接続できない場合は、暗号化より前の問題と認証以降の問題を分ける。ssh -v user@host では処理の進行状況を調べられる。
| 観測した状態 | 主に確認する箇所 |
|---|---|
| タイムアウトし、接続開始まで進まない | 宛先アドレス、経路、ACL、ファイアウォール、待受アドレスを確認する |
Connection refused が返る | 指定先で待ち受けているか、ポート番号や拒否設定が正しいか確認する |
no matching ... が出る | 双方で合意できる鍵交換方式・暗号方式・署名方式があるか確認する |
| サーバの鍵が変わったと警告される | 正規の鍵更新、接続先の変更、不正な介在を切り分ける |
Permission denied が出る | 利用者名、提示した鍵、公開鍵の登録、ファイルのアクセス権、許可する認証方式を確認する |
| ログインは成功するが転送先へつながらない | 転送の許可設定と、SSHの出口から最終宛先までの接続を確認する |
古い機器との暗号方式の不一致を、パスワードの誤りと混同してはならない。弱い方式を全接続で再び有効にする前に、機器更新や対象を限定した互換設定を検討する。OpenSSHの旧方式に関する説明では、鍵交換・ホスト認証・暗号・改ざん検知それぞれで共通の方式が必要だと説明されている。
設計問題で確認すること
「SSHで暗号化しているのに接続先の指紋を確認する理由」は、通信相手が本物でなければ偽物との通信を暗号化するだけになるためである。「公開鍵認証で秘密鍵を送らない理由」は、署名によって所持を証明でき、鍵そのものを相手へ預ける必要がないためである。
構成図にポート転送が出てきた場合は、接続を受け付ける入口、SSHの両端、最後の接続先を区別する。どの装置が最終宛先への接続を開始するかが分かれば、必要なACLや名前解決、暗号化が及ぶ区間を根拠を持って説明できる。