~/.ssh/configを1行ずつ解剖
多段SSHやGitHubの複数アカウントで迷わなくなる。~/.ssh/configの現実的な実例をProxyJumpやIdentitiesOnlyなど1行ずつ解剖し、Host *を末尾に置くべき理由まで評価規則から理解できる。
- ssh_configは各パラメータについて最初にマッチした値を採用するため、個別のHost設定を上に、Host *のような全体の既定値を末尾に書く。順序を誤るとHost *側の値が先に確定し、個別の鍵やポートの指定が無視される。
- ProxyJumpは踏み台経由の多段SSHを1行で書ける仕組みで、従来のProxyCommandで書いていたssh -W %h:%p相当の中継を置き換える。内側の接続は端から端まで暗号化され、カンマ区切りで3段以上の経路にも対応する。
- ssh-agentは登録鍵を順に提示するため、多いとMaxAuthTries(既定6)へ達して正しい鍵の前に切断される。IdentityFileとIdentitiesOnly yesで使う鍵だけを提示すれば回避できる。
この設定ファイルは何者か
~/.ssh/configは、OpenSSHクライアントの接続設定を接続先ごとに記述するファイルだ。sshコマンドはもちろん、scpやsftp、内部でsshを使うrsyncやGitまで、OpenSSHクライアント経由の接続すべてに効く。宛先・ユーザー名・ポート・鍵・経路といった詳細をここへ集約しておけば、コマンドラインはssh bastionのような短い別名だけで済む。
設定は3つの層から読まれる。優先度の高い順に、コマンドライン引数、~/.ssh/config、システム全体の/etc/ssh/ssh_configだ。そして最大の特徴が評価規則にある。各パラメータは「最初に得られた値」で確定し、後から現れた値では上書きされない。一般的な設定ファイルの感覚とは逆であり、この先勝ちの規則が本記事を貫く主役になる。
なお、このファイルが決めるのはクライアントの振る舞いまでで、SSHというプロトコル自体の3層構造(トランスポート・ユーザー認証・コネクション)はRFC 4251が定めている。設定の各行は、この層構造のどこかを操作するスイッチだと考えると見通しがよい。
横にスクロール
1行ずつ解剖
題材はこれだ。踏み台を経由して本番サーバーへ入り、GitHubには専用鍵を使う、実務でよくある構成である。
Host bastion
HostName bastion.example.com
User taro
Port 2222
IdentityFile ~/.ssh/id_ed25519_bastion
IdentitiesOnly yes
Host prod-web
HostName 10.0.1.15
User deploy
ProxyJump bastion
IdentityFile ~/.ssh/id_ed25519_prod
IdentitiesOnly yes
Host github.com
User git
IdentityFile ~/.ssh/id_ed25519_github
IdentitiesOnly yes
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
ForwardAgent no
Hostは別名、HostNameが実際の宛先
Hostの行は設定ブロックの見出しで、コマンドラインに与えた名前がパターンにマッチしたブロックだけが適用される。ssh prod-webと打てば、Host prod-webのブロックと、すべてにマッチするHost のブロックの両方が効く。パターンにはと?のワイルドカードが使え、Host prod-*と書けばprod-webもprod-dbもまとめて扱える。空白区切りで複数のパターンを並べることも可能だ。
HostNameは実際に接続する宛先で、ホスト名でもIPアドレスでもよい。省略するとコマンドラインの名前がそのまま宛先になる。サンプルのgithub.comブロックは実名をHostに書いて省略する型、prod-webは覚えやすい別名から10.0.1.15という実際の宛先へ引き当てる型だ。つまりHostとHostNameの組は、覚えにくいIPや長いホスト名を意味のある名前へ変換する辞書として働く。
UserとPort
Userはログイン先のユーザー名、Portは接続先ポートで既定は22だ。この例では踏み台が2222番で待ち受けている想定で、設定に書いておけば以後-pの指定は不要になる。GitHubのSSH接続はユーザーが常にgitと決まっているから、これも固定してしまえば迷いがない。
IdentityFileとIdentitiesOnly
IdentityFileは認証に使う秘密鍵を指定する。未指定ならid_ed25519やid_rsaといった既定の名前が探される。細かいが重要な例外として、複数のブロックがマッチしたときのIdentityFileは先勝ちで1つに確定するのではなく、候補のリストへ追加されていく。
問題はssh-agentに鍵を多数登録している場合だ。クライアントはagentの鍵を順にサーバーへ提示するが、サーバー側には認証試行の上限があり(OpenSSHのMaxAuthTriesは既定6)、目的の鍵の番が来る前に上限へ達するとToo many authentication failuresで切断されてしまう。IdentitiesOnly yesを添えると、agentが何本の鍵を抱えていようと、設定で明示したIdentityFileだけを提示するようになる。鍵を数本以上使い分けるなら実質必須の組み合わせだ。
ProxyJumpで多段SSHを1行に
本番サーバー10.0.1.15はプライベートネットワーク内にあり、外からは直接届かない。ProxyJump bastionと書くだけで、sshはまずbastionへ接続し、そこに開いたトンネルを通して目的地とのSSH接続を確立する。内側の接続はクライアントと目的地の間でエンドツーエンドに暗号化されるため、踏み台から通信の中身は見えない。経由地が増えたらProxyJump gate1,gate2のようにカンマで連ねる。コマンドラインの-Jと同じ機能だ。
ProxyJumpが登場する前は、ProxyCommandで同じ構成を書いていた。
| 観点 | ProxyJump | ProxyCommand |
|---|---|---|
| 書き方 | ProxyJump bastion の1行 | ssh -W %h:%p bastion を指定 |
| 多段化 | カンマ区切りで列挙 | 定義が入れ子になり複雑 |
| 位置づけ | OpenSSH 7.3以降の標準的な書き方 | 任意コマンドを挟める汎用機構 |
ProxyCommandは経路に任意のコマンドを挟める汎用機構で、その最頻出パターンだった中継専用の書き方を専用構文へ昇格させたのがProxyJumpだ。今から書くならProxyJumpでよい。
ServerAliveInterval / ServerAliveCountMax
NATルータやファイアウォールは、しばらく無通信のTCP接続を静かに破棄することがある。ServerAliveInterval 60は、60秒間何も流れなかったら暗号化されたチャネル内で生存確認をサーバーへ送る設定だ。ServerAliveCountMax 3(既定値も3)は、応答のない生存確認が3回続いたら接続を諦めるという意味で、この組み合わせならおよそ180秒で異常を検知できる。放置したターミナルが固まったまま戻らないという日常の不快感は、この2行でほぼ解消する。
ForwardAgentは既定でno
ForwardAgent yesにすると、接続先のホストから手元のssh-agentへ問い合わせる経路が作られ、踏み台へ入った後さらに奥のサーバーへも手元の鍵で認証できる。鍵ファイルを踏み台に置かずに済む便利な機能だが、代償がある。転送中は踏み台上にagentへの中継ソケットが生まれ、そのホストのroot権限を持つ者はソケット越しにあなたの鍵へ署名させられる。秘密鍵そのものは盗めないものの、転送している間は第三者が「あなたとして」認証できてしまうわけだ。踏み台の管理者を無条件に信頼できない以上、Host *でnoを明示し、必要なホストだけ個別に許可するのが防御の基本になる。そもそもProxyJumpで目的地まで直接届くなら、踏み台上で鍵を使う場面自体が消える。こうした認証情報の預け先を最小にする発想はセキュリティ全般に通じる。
Host *は必ず末尾に
冒頭で述べた先勝ちの規則を、ここで設計原則に落とす。個別・具体的なHostブロックを上に、全ホスト共通の既定値であるHost *を末尾に置く。サンプルでHost *が最後にあるのは趣味ではなく、この評価規則の帰結だ。
ssh_configでは、各パラメータは最初にマッチした値で確定する。Host *を先頭に書くと、そこで決まった値を個別ブロックが変更する手段はない。「後で書いた方が勝つ」という他の設定ファイルの常識を持ち込まないこと。
パーミッションは600
OpenSSHはファイル権限を検査する。秘密鍵が他者から読める状態(644など)だと、Permissions are too openという警告とともにその鍵は無視される。/.ssh/config自体も、他者が書き込める状態ならBad owner or permissionsのエラーで接続を拒否される。/.sshディレクトリは700、秘密鍵とconfigは600が基本だ。設定が「なぜか効かない」「鍵が使われない」ときは、内容より先に権限を疑うと早い。
つまずきやすい点
まず、Host *をファイル先頭に書いてしまう事故。「基本を書いてから個別に上書き」という感覚で冒頭へ置くと、先勝ちの規則によってそこで確定したPortやIdentityFileを個別ブロックが変更できず、個別設定が丸ごと沈黙する。設定が効かないときは、より上にある広いパターンに同じパラメータがないかを最初に疑うとよい。次に、初回接続で表示されるknown_hostsの確認を惰性でyesと打つ癖。あの指紋の確認は、接続先が本物かを人間が検証できる唯一の機会であり、ここで偽物を受け入れると以後のなりすまし警告は機能しなくなる。せめて本番系では、事前に共有されたホスト鍵の指紋と突き合わせたい。もう一つがGitHubの複数アカウントだ。GitHubはどの鍵で認証したかでアカウントを識別するため、agentが別アカウントの鍵を先に差し出すと意図しない側のユーザーとして認証される。アカウントごとにHostの別名を切り、IdentityFileとIdentitiesOnly yesの組で提示する鍵を1本に固定するのが定石である。
まとめ
~/.ssh/configは、別名から宛先・ユーザー・ポート・鍵・経路までを1か所へ集約する、SSH運用の土台だ。押さえる規則は少ない。評価は先勝ちだからHost *は末尾へ、鍵が増えたらIdentitiesOnly yes、多段はProxyJump、権限は600。これだけで、踏み台の奥の本番サーバーにも複数鍵のGitHubにも、短い別名ひとつで確実に届くようになる。この設定が操るSSHの足元、TCP/IPの世界はネットワークの各記事で扱っている。
設定ファイル解剖の記事ガイド
~/.ssh/configを1行ずつ解剖を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
設定ファイル
比較で見る軸
難易度: advanced / カテゴリ: 設定ファイル解剖 / タグ数: 4
導入後に効く点
ProxyJumpは踏み台経由の多段SSHを1行で書ける仕組みで、従来のProxyCommandで書いていたssh -W %h:%p相当の中継を置き換える。内側の接続は端から端まで暗号化され、カンマ区切りで3段以上の経路にも対応する。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- 設定ファイル解剖
- タグ数
- 4
判断チェックリスト
- 自社の用途が「設定ファイル / SSH」に近いか確認する。
- 強みである「ssh_configは各パラメータについて最初にマッチした値を採用するため、個別のHost設定を上に、Host *のような全体の既定値を末尾に書く。順序を誤るとHost *側の値が先に確定し、個別の鍵やポートの指定が無視される。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。