EC2インスタンスにsshで接続する際、もやもやした思いを抱きながらssh -oStrictHostKeyChecking=no
している人も多いのではないかと思います。理想はStrictHostKeyCheckingの際に不一致の場合はTTYでaskするのではなく、コマンドを呼び出してくれるといいのですが、手元の環境に降りてくるのがいつになるかもわからないし、今できることを考えていきます。
実際に何が必要か考えると、1.正しいホスト鍵を得る 2.known_hostsと比較する、になります。
前者はaws ec2 get-console-output
で得ることができます。インスタンスが再起動された場合(init.dとかで毎回表示するか、起動ごとにホスト鍵を作り直すか)や、起動後にコンソールに64KBの出力がある場合(こちらは対策が厄介なので、別の場所に保存するという戦略の方がいいかもしれない)の対策が必要かもしれません。
後者は特に悩む必要も無いので淡々とやります。
以下のスクリプトによって、正しいホスト鍵との比較が出来るようになります。ここではエラーにしていますが、ssh-keygen -R
で既存の鍵を消した上でknown_hostsにあたらしい鍵を登録するようにすると、sshコマンドを実行する前に叩いておくとそこそこ便利なヘルパースクリプトがつくれるでしょう。
#!/usr/bin/env ruby
require 'open3'
def hostname2instance_id(hostname)
case hostname
when /\.amazonaws.com\z/
key = 'dns-name'
when /\.internal\z/
key = 'private-dns-name'
when /\A10\./
key = 'private-ip-address'
when /\A\d+\.\d+.\d+.\d+\z/
key = 'ip-address'
else
abort "unknown hostname type '#{hostname}'"
end
o, s = Open3.capture2('aws', 'ec2', 'describe-instances', '--filters', "Name=#{key},Values=#{host<
res = o[/"InstanceId"\s*:\s*"([^"]+)"/, 1]
unless res
puts "cannot get InstanceId for '#{hostname}' #{s.inspect}"
puts o
abort
end
res
end
hostname = ARGV.shift
puts "hostname: #{hostname}"
instance_id = hostname2instance_id(hostname)
puts "InstanceId: #{instance_id}"
o, s = Open3.capture2('aws', 'ec2', 'get-console-output', '--instance-id', instance_id)
server_keys = o[/-----BEGIN SSH HOST KEY KEYS-----(.*)-----END SSH HOST KEY KEYS-----/, 1]
server_keys.gsub!(/\\r\\n/, "\n")
server_keys.strip!
puts server_keys
o, s = Open3.capture2('ssh-keygen', '-qF', hostname)
puts "known host key: #{o}"
unless o.scan(/^\S+\s+([a-zA-Z0-9=+\/]+)/).any?{ server_keys.include?($1) }
abort "server key doesn't match"
end