1. kawaz

    No comment

    kawaz
Changes in body
Source | HTML | Preview

今時のバージョンの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に勝手にハッシュオプション付けられるのは嫌がるだろうから環境チェックしてからオプションの出し入れするように修正してみた。

https://gist.github.com/kawaz/00dd296ffc86506854ae

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ってファイルに分けといた。