138
137

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

SSHのknown_hostsをスマートに更新する

Last updated at Posted at 2014-07-24

今時のバージョンのOpenSSHではknown_hostsがハッシュ化されてて手動管理がしにくいし、スマートに決めたいのでコマンドでやってみる。

コマンド

結論から書くと↓こんな感じでOK。(target-hostnameのホストキーが変わった場合の例)

host="target-hostname"
ssh-keygen -R $host
ssh-keyscan -H $host >> ~/.ssh/known_hosts
  • ssh-keygen -R hostname~/.ssh/known_hosts から対象ホストのホストキーを削除してくれる。
    • 複数あれば全部消してくれるしハッシュ化されてるのもされてないのも全部消してくれるので便利。
    • known_hostsファイルをエディタで開いてエラーメッセージで指摘された行数まで移動して手で削除するのなんかより100倍楽。
  • ssh-keyscan は対象ホストのホストキー一覧を取得するコマンド。
    • 無言で実行できるから一旦手動でssh試みてプロンプトでyesとかするより使い勝手が良い。
    • -Hオプションを付けるとホスト名がハッシュ化される、無しなら昔ながらに生のホスト名で出力される。今時はハッシュ化しとけば良いと思う。
  • 注意点! 追記する為の >> を間違えて > て書くと既存のキーが全部消えるので気をつけること!

ちょっと進化系

HashKnownHosts yesな環境だとbash-completionのホスト名補完が効かなくなるので~/.ssh/config内で明示的にnoにしてる人もいそうな気がする。そういう人はssh-keyscanに勝手にハッシュオプション付けられるのは嫌がるだろうから環境チェックしてからオプションの出し入れするように修正してみた。

refresh_knownhosts.sh
#!/bin/bash

# ssh設定値の取得関数
sshconf() {
  local key=$1 targethost=$2
  local value inhost f k v m
  [[ -z $key ]] && { echo "Usage: $FUNCNAME key [hostname]"; return 1; }
  for f in ~/.ssh/config /etc/ssh_config; do
    [[ -r $f ]] || continue
    inhost=1
    while read k v; do
      if [[ $k == Host ]]; then
        inhost=
        for m in $v; do
          [[ $m =~ ^# ]] && break # skip comment
          m=${m//./\\.}; m=${m//\*/.*}; m=${m//\?/.}; m="^$m$" # wildcard -> regexp
          if [[ $targethost =~ $m ]]; then
            inhost=1
            break
          fi
        done
        continue
      fi
      [[ -z $inhost ]] && continue # 不一致Host内の設定は無視
      if [[ $k == $key ]]; then
        value=$v
      fi
      if [[ -n $value ]]; then
        break # 最初に見つけた設定優先なのでbreak
      fi
    done < <(cat $f | grep -v '^$' | egrep -v '^\s*#')
    if [[ -n $value ]]; then
      break # 最初のファイル優先なのでbreak
    fi
  done
  [[ -z $value ]] && return 1
  printf "%s\n" "$value"
}

# main
[[ -z $1 ]] && { echo "Usage: $0 hostname [hostname...]"; exit 1; }
hashopt=
if [[ $(sshconf HashKnownHosts) == yes ]]; then
  hashopt=-H
fi
for h in "$@"; do
  ssh-keygen -R "$h"
  ssh-keyscan $hashopt "$h" >> ~/.ssh/known_hosts
done

現在の環境上でのsshの設定値を取得するコマンドってありそうでないのね…。お陰でHashKnownHostsの値が知りたかっただけなのに長大な関数を書いてしまった(^^;

上記 sshconf 関数は結構まじめに書いたつもりなので単に指定のホストに対する設定値を取得するコマンドとして独立していても便利に使えそう。→独立したsshconf.shってファイルに分けといた。

ssh -G があったw

ssh -G ホスト名 でそのホスト名にマッチする最終的な ssh_cofig 値が取れるんじゃん!てことに上の関数を作ってから1年以上経過して気がついた…。

138
137
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
138
137

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?