Multiple Hostnames in SSH Configs

Introduction

OpenSSH 是很好用的連線工具,因為太常用了,所以我都會使用 .ssh/config 設定檔,把很常用的 host 寫到裡面,而許多 ShellTab Completion 也會 parse 設定檔,所以可以少打很多很多的字。

但是會有一個問題,就是會因為所在的位置不同,想連線的 host 會有不同的 hostname 設定,舉例在家裡的話,會想要連線到 NASLan IP, 但在外面會應該要對應到 NASVPN IP
偏偏 .ssh/configHostname 只能設定一個耶, 所以每一台主機上面的設定檔就會因為在不同的網路環境下不一樣, 要是像筆記型電腦帶來帶去,就會常常要改來改去,有點麻煩…

所以寫了一個 get-config.sh 可以自動更新 .ssh/config

.ssh/config.d

首先可以需要在家目錄的 .ssh/ 的設定目錄中,有 .ssh/config.d 的目錄, 裡面可以有多個類似原本 .ssh/config 的檔案 .ssh/config.d/config.in 。 但是跟原本的 config 不同的是,**config.in** 可以有多個 **Hostname**, 如範例中的有三個。

1
2
3
4
5
6
7
8
$ cat ~/.ssh/config.d/config.in
Host vpn-tunnel
Hostname 192.168.0.1 10.8.0.1 vpn.domanin.tw
ProxyCommand corkscrew vpn.domain.tw 80 %h %p ~/.ssh/screw.auth
User tunnel
LocalForward 1194 vpn.domain.tw:1194
ServerAliveInterval 30
ServerAliveCountMax 6

gen-config.sh

接著就可以使用 gen-config.sh 來掃描上面的 config.inHostname,最先把掃描到這個 Hostname 存在的話,就會用這個設定寫出 .ssh/config,以上面的 vpn-tunnel 為例,如果現在網路環境下這三個 Hostname 都可以存取得到的話,其實最想要的是 **LAN 上的 IP 192.168.0.1**。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/bin/bash

ssh_config_hostname() {
# echo $1
# echo $2
RHN=""
for HN in "$@"; do
[[ "$HN" == "Hostname" ]] && echo -n "$HN " && continue
ping -c 1 -W 1 2>&1 >/dev/null $HN && RHN=$HN && break
done
[[ -n $RHN ]] && echo $RHN || echo $HN
}

ssh_config_patch() {
[ -f $1 ] || return
while read L; do
# echo $L
# [[ "$L" == "Hostname "* ]] && ssh_config_hostname "$L"
[[ "$L" == "Hostname "* ]] && ssh_config_hostname $L || echo $L
done < $1
}

echo "# This file is auto-generated by gen-config"
echo "#"
for F in "$@"; do ssh_config_patch $F; done

配合之前提過的 Makefile,需要更新的時候,只要下 make ssh 就可以了:

1
2
3
4
ssh:
$(call mkdir_config, ~/.$@/config.d)
$(call update_config, ~/.$@/config.d/config.in, $@/config.d/config.in)
@ bash $@/gen-config.sh ~/.$@/config.d/*.in | tee ~/.$@/config

目前 gen-config.sh 還是使用 ping 來當作判斷的規則, 其實有想過真的用 ssh 來看看,不過發現 ssh 的回傳值沒有辦法分辦是 Hostname 不在還是沒有 Keys,反正在我的使用環境中 ping 的規則都可以符合需求。

1
2
3
4
5
6
7
8
9
$ ssh -o StrictHostKeyChecking=no -o BatchMode=yes 192.168.1.52
yumaokao@192.168.1.52: Permission denied (publickey,password,keyboard-interactive).
$ echo $?
255

$ ssh -o StrictHostKeyChecking=no -o BatchMode=yes 192.168.1.57
ssh: connect to host 192.168.1.57 port 22: No route to host
$ echo $?
255