外から自宅鯖に繋ぐ設定について
自宅内に複数のサーバがあって、外からssh出来るように例えば以下のように静的NATの設定をルータにしてあったとする。(この辺の設定は各ご家庭のルータによるので割愛)
グローバルIP:22016 → 192.168.1.16:22
グローバルIP:22017 → 192.168.1.17:22
この時点で外から自宅の 192.168.1.16
の鯖に繋ぐには以下のようにして繋がるわけだ。
ssh home.example.jp:22016
で、普段持ち歩く Macbook から外から簡単に繋げられるよう、まず以下の様な設定が元々してあったんだ。
Host h16
HostName home.example.jp
Port 22016
Host h17
HostName home.example.jp
Port 22017
これで外から自宅の 192.168.1.16
の鯖に繋ぐには以下のように出来るようになる。
ssh h16
うん、便利。
問題と解決
問題:自宅LAN内から同じホスト名で繋げられない
うちの自宅ルータは糞なので、LAN内のアドレスから自分のグローバルアドレスに繋げることが出来ない!(多分ルータ内部のNAT実装が微妙で書き換えしくってるんだろうなぁ…。)
なので自宅からは home.example.jp:22016
とかのアドレスではタイムアウトしちゃって繋ぐことが出来ない。ので当然 ssh h16
とかも出来ない。
しかたがないのでこないだ迄は以下のように設定を追加して ssh h16l
とかで繋げてたんだわ。
Host h16
HostName home.example.jp
Port 22016
Host h16l
HostName 192.168.1.16
Port 22
Host h17
HostName home.example.jp
Port 22017
Host h17l
HostName 192.168.1.17
Port 22
まぁ、何もないよりは大分楽っちゃ楽なんだがやっぱし時々、自宅なのに l 無しで繋げようとしたりその逆だったりで接続待ちと見せかけてタイムアウトとか言うことがしばしばありストレスが溜まってた。
ちなみに自宅LAN内にローカルDNSリゾレーションサーバ立てて一部ホストはローカルIP返してそれ以外は外部リゾルバにパススルーとかすればいいんじゃね?って意見もあるかと思う。が面倒くさい。昔やってたこともあるけど、DNS立てるのがまず面倒だし、DNS担当のLAN内サーバのメンテ中とか解決できなくてやっぱし不便だし、労多くして益少なしで嫌だった。
特に可用性確保が面倒くさい。こういうのは99.9%使えてても使えない0.1%の時のストレスが半端ないからなー。
解決: Match 便利!
でも最近は心穏やかさ。EL Capitanにアップデートした際に OpenSSH が上がって ~/.ssh/config
で Match
が使えるようになったからね!
今は以下の様な設定が書いてあり、外でも自宅でも ssh h16
で目的のサーバに繋げられるようになっている。
Match host h16 exec "/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -I | grep -qE '^ +SSID: KWZ-HOME-[ag]$'"
HostName 192.168.1.16
Port 22
Host h16
HostName home.example.jp
Port 22016
Match host h17 exec "/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -I | grep -qE '^ +SSID: KWZ-HOME-[ag]$'"
HostName 192.168.1.17
Port 22
Host h17
HostName home.example.jp
Port 22017
これは何かっていうと、今までは Host
によるホスト名の条件分岐(精々ワイルドカードが使える程度) しかできなかったのが、Match
を使うことでホスト名マッチングの他に、接続先または接続元のユーザ名や、果てはローカルコマンドの実行結果を複数ANDで組み合わせて条件が書けるようになったのだ(詳しくはman ssh_config
)。
上記の設定ではまず host h16
の部分がホスト名一致、続いて exec "(略)"
が書いてある。これは exec に渡したコマンドの終了ステータスが 0
なら真という条件になる。
ちなみに /(略)/airport -I
は例えば以下の様な出力が出てくる。
agrCtlRSSI: -57
agrExtRSSI: 0
agrCtlNoise: -87
agrExtNoise: 0
state: running
op mode: station
lastTxRate: 130
maxRate: 144
lastAssocStatus: 0
802.11 auth: open
link auth: wpa2-psk
BSSID: 1c:b1:7f:70:16:20
SSID: KWZ-HOME-a
MCS: 15
channel: 11
これは現在接続中の WiFi 情報で、Match exec
の中ではこの出力に grep -qE '^ +SSID: KWZ-HOME-[ag]$'
と grep をかけている。KWZ-HOME-[ag]
は自宅WiFiの SSID (例)です。電波帯別に2種類のSSIDがあるのでこのどっちかにつながってたら自宅と判定しているわけだ。
うん凄い快適。
ちなみに Match
に複数条件を書くと前の条件から順にテストして true なら次の式をテストし、一つでも失敗したらそれより右のチェックは行わないという仕様なので、頭に host h16
と書いておくことで h16
というホスト名で接続しようとした時のみに後半のコマンドが実行されるので無駄に exec される事が避けられるようにしている。
ちなみに ssh_config
の設定は上の方で確定した設定は後半でマッチしても上書きされない、設定項目毎に先勝ちルールなのでその点を考慮しつつ設定を書く場所の順番を決めましょう。