Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

今時のバージョンの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ってファイルに分けといた。

ssh -G があったw

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

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away