ツナワタリマイライフ

日常ネタから技術ネタ、音楽ネタまで何でも書きます。

sshkitを使ってみた

はじめに

capistrano大好き芸人の私ですが、内部で使われているsshkitは使ったことがありません。

最近はcapiの仕様把握にsshkitのコードも読むようになったんですが、まずは使わないとということでササっと使ってみます。

sshkit

文字通り、リモートサーバに対してsshでコマンドを送り込むツールです。sshpassの簡単版、かつRuby製。

github.com

環境構築

Vagrantでササっとね。

take@MacBook-Air ~/v/sshkit> cat Vagrantfile 
Vagrant.configure(2) do |config|
  config.vm.box = "CentOS71"
  config.vm.define :sshkit do | sshkit |
    sshkit.vm.hostname = "sshkit"
    sshkit.vm.network :private_network, ip: "192.168.33.10"
  end
end

Vagrant up!

take@MacBook-Air ~/v/sshkit> vagrant up
Bringing machine 'sshkit' up with 'virtualbox' provider...
==> sshkit: Importing base box 'CentOS71'...
==> sshkit: Matching MAC address for NAT networking...
==> sshkit: Setting the name of the VM: sshkit_sshkit_1471449306450_7881
==> sshkit: Clearing any previously set network interfaces...
==> sshkit: Preparing network interfaces based on configuration...
    sshkit: Adapter 1: nat
    sshkit: Adapter 2: hostonly
==> sshkit: Forwarding ports...
    sshkit: 22 => 2222 (adapter 1)
==> sshkit: Booting VM...
==> sshkit: Waiting for machine to boot. This may take a few minutes...
    sshkit: SSH address: 127.0.0.1:2222
    sshkit: SSH username: vagrant
    sshkit: SSH auth method: private key
    sshkit: Warning: Connection timeout. Retrying...
    sshkit: 
    sshkit: Vagrant insecure key detected. Vagrant will automatically replace
    sshkit: this with a newly generated keypair for better security.
    sshkit: 
    sshkit: Inserting generated public key within guest...
    sshkit: Removing insecure key from the guest if it's present...
    sshkit: Key inserted! Disconnecting and reconnecting using new SSH key...
==> sshkit: Machine booted and ready!
GuestAdditions 4.3.30 running --- OK.
==> sshkit: Checking for guest additions in VM...
==> sshkit: [vagrant-hostsupdater] Checking for host entries
==> sshkit: [vagrant-hostsupdater] Writing the following entries to (/etc/hosts)
==> sshkit: [vagrant-hostsupdater]   192.168.33.10  sshkit  # VAGRANT: f84bf2d975137fc696642d0375f8d9dc (sshkit) / 75284f8d-ceb2-46ee-8043-7ced9bb6b630
==> sshkit: [vagrant-hostsupdater] This operation requires administrative access. You may skip it by manually adding equivalent entries to the hosts file.
Password:
==> sshkit: Setting hostname...
==> sshkit: Configuring and enabling network interfaces...
==> sshkit: Mounting shared folders...
    sshkit: /vagrant => /Users/take/vagrant/sshkit

pingssh、ok

take@MacBook-Air ~/v/sshkit> ping 192.168.33.10
PING 192.168.33.10 (192.168.33.10): 56 data bytes
64 bytes from 192.168.33.10: icmp_seq=0 ttl=64 time=0.596 ms
64 bytes from 192.168.33.10: icmp_seq=1 ttl=64 time=0.516 ms
^C
--- 192.168.33.10 ping statistics ---
2 packets transmitted, 2 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.516/0.556/0.596/0.040 ms
take@MacBook-Air ~/v/sshkit> vagrant ssh
Last login: Wed Aug 17 17:02:14 2016 from 10.0.2.2

sshkitを使う

capiで入ってるかと思ったけど、バージョンが更新されたのかな。

take@MacBook-Air ~/v/sshkit> sudo gem install sshkit
Password:
Fetching: sshkit-1.11.2.gem (100%)
Successfully installed sshkit-1.11.2
Parsing documentation for sshkit-1.11.2
Installing ri documentation for sshkit-1.11.2
Done installing documentation for sshkit after 0 seconds
1 gem installed

単純にuptimeを実行するだけのコードを書きます。

take@MacBook-Air ~/v/sshkit> cat sample.rb 
require 'sshkit'
require 'sshkit/dsl'
include SSHKit::DSL

on "192.168.33.10" do |host|
  execute :uptime
end

実行すると。。。失敗。

take@MacBook-Air ~/v/sshkit> ruby sample.rb 
  INFO [4b19d6d3] Running /usr/bin/env uptime on 192.168.33.10
/Library/Ruby/Gems/2.0.0/gems/net-ssh-2.9.1/lib/net/ssh/verifiers/secure.rb:50:in `process_cache_miss': fingerprint 11:48:65:bc:27:f5:30:82:7d:81:c2:b7:79:ac:36:ae does not match for "192.168.33.10" (Net::SSH::HostKeyMismatch)
  from /Library/Ruby/Gems/2.0.0/gems/net-ssh-2.9.1/lib/net/ssh/verifiers/secure.rb:35:in `verify'
   from /Library/Ruby/Gems/2.0.0/gems/net-ssh-2.9.1/lib/net/ssh/verifiers/strict.rb:16:in `verify'
  from /Library/Ruby/Gems/2.0.0/gems/net-ssh-2.9.1/lib/net/ssh/verifiers/lenient.rb:15:in `verify'
    from /Library/Ruby/Gems/2.0.0/gems/net-ssh-2.9.1/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb:173:in `verify_server_key'
  from /Library/Ruby/Gems/2.0.0/gems/net-ssh-2.9.1/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb:68:in `exchange_keys'
   from /Library/Ruby/Gems/2.0.0/gems/net-ssh-2.9.1/lib/net/ssh/transport/algorithms.rb:364:in `exchange_keys'
  from /Library/Ruby/Gems/2.0.0/gems/net-ssh-2.9.1/lib/net/ssh/transport/algorithms.rb:205:in `proceed!'
    from /Library/Ruby/Gems/2.0.0/gems/net-ssh-2.9.1/lib/net/ssh/transport/algorithms.rb:196:in `send_kexinit'
  from /Library/Ruby/Gems/2.0.0/gems/net-ssh-2.9.1/lib/net/ssh/transport/algorithms.rb:151:in `accept_kexinit'
   from /Library/Ruby/Gems/2.0.0/gems/net-ssh-2.9.1/lib/net/ssh/transport/session.rb:195:in `block in poll_message'
  from /Library/Ruby/Gems/2.0.0/gems/net-ssh-2.9.1/lib/net/ssh/transport/session.rb:173:in `loop'
    from /Library/Ruby/Gems/2.0.0/gems/net-ssh-2.9.1/lib/net/ssh/transport/session.rb:173:in `poll_message'
  from /Library/Ruby/Gems/2.0.0/gems/net-ssh-2.9.1/lib/net/ssh/transport/session.rb:210:in `block in wait'
   from /Library/Ruby/Gems/2.0.0/gems/net-ssh-2.9.1/lib/net/ssh/transport/session.rb:208:in `loop'
  from /Library/Ruby/Gems/2.0.0/gems/net-ssh-2.9.1/lib/net/ssh/transport/session.rb:208:in `wait'
    from /Library/Ruby/Gems/2.0.0/gems/net-ssh-2.9.1/lib/net/ssh/transport/session.rb:87:in `initialize'
  from /Library/Ruby/Gems/2.0.0/gems/net-ssh-2.9.1/lib/net/ssh.rb:202:in `new'
   from /Library/Ruby/Gems/2.0.0/gems/net-ssh-2.9.1/lib/net/ssh.rb:202:in `start'
  from /Library/Ruby/Gems/2.0.0/gems/sshkit-1.11.2/lib/sshkit/backends/connection_pool.rb:59:in `call'
    from /Library/Ruby/Gems/2.0.0/gems/sshkit-1.11.2/lib/sshkit/backends/connection_pool.rb:59:in `with'
  from /Library/Ruby/Gems/2.0.0/gems/sshkit-1.11.2/lib/sshkit/backends/netssh.rb:155:in `with_ssh'
   from /Library/Ruby/Gems/2.0.0/gems/sshkit-1.11.2/lib/sshkit/backends/netssh.rb:108:in `execute_command'
  from /Library/Ruby/Gems/2.0.0/gems/sshkit-1.11.2/lib/sshkit/backends/abstract.rb:141:in `block in create_command_and_execute'
    from /Library/Ruby/Gems/2.0.0/gems/sshkit-1.11.2/lib/sshkit/backends/abstract.rb:141:in `tap'
  from /Library/Ruby/Gems/2.0.0/gems/sshkit-1.11.2/lib/sshkit/backends/abstract.rb:141:in `create_command_and_execute'
   from /Library/Ruby/Gems/2.0.0/gems/sshkit-1.11.2/lib/sshkit/backends/abstract.rb:74:in `execute'
  from sample.rb:6:in `block in <main>'
    from /Library/Ruby/Gems/2.0.0/gems/sshkit-1.11.2/lib/sshkit/backends/abstract.rb:29:in `instance_exec'
  from /Library/Ruby/Gems/2.0.0/gems/sshkit-1.11.2/lib/sshkit/backends/abstract.rb:29:in `run'
   from /Library/Ruby/Gems/2.0.0/gems/sshkit-1.11.2/lib/sshkit/runners/parallel.rb:12:in `block (2 levels) in execute'

はい。vagrantで作ったのでssh関係で引っかかるかなと予想はしていたんですが、あたってしまいました。

しかし、finger printが違うといっているので、過去に同じIPに接続したときのknown_hostを見に行ってるのでしょう。該当のエントリを削除しました。

take@MacBook-Air ~/v/sshkit> ruby sample.rb 
  INFO [b6b8b67a] Running /usr/bin/env uptime as vagrant@192.168.33.10
/Library/Ruby/Gems/2.0.0/gems/net-ssh-2.9.1/lib/net/ssh.rb:219:in `start': Authentication failed for user vagrant@192.168.33.10 (Net::SSH::AuthenticationFailed)
  from /Library/Ruby/Gems/2.0.0/gems/sshkit-1.11.2/lib/sshkit/backends/connection_pool.rb:59:in `call'
  from /Library/Ruby/Gems/2.0.0/gems/sshkit-1.11.2/lib/sshkit/backends/connection_pool.rb:59:in `with'
    from /Library/Ruby/Gems/2.0.0/gems/sshkit-1.11.2/lib/sshkit/backends/netssh.rb:155:in `with_ssh'
  from /Library/Ruby/Gems/2.0.0/gems/sshkit-1.11.2/lib/sshkit/backends/netssh.rb:108:in `execute_command'
  from /Library/Ruby/Gems/2.0.0/gems/sshkit-1.11.2/lib/sshkit/backends/abstract.rb:141:in `block in create_command_and_execute'
    from /Library/Ruby/Gems/2.0.0/gems/sshkit-1.11.2/lib/sshkit/backends/abstract.rb:141:in `tap'
  from /Library/Ruby/Gems/2.0.0/gems/sshkit-1.11.2/lib/sshkit/backends/abstract.rb:141:in `create_command_and_execute'
  from /Library/Ruby/Gems/2.0.0/gems/sshkit-1.11.2/lib/sshkit/backends/abstract.rb:74:in `execute'
    from sample.rb:13:in `block in <main>'
  from /Library/Ruby/Gems/2.0.0/gems/sshkit-1.11.2/lib/sshkit/backends/abstract.rb:29:in `instance_exec'
  from /Library/Ruby/Gems/2.0.0/gems/sshkit-1.11.2/lib/sshkit/backends/abstract.rb:29:in `run'
    from /Library/Ruby/Gems/2.0.0/gems/sshkit-1.11.2/lib/sshkit/runners/parallel.rb:12:in `block (2 levels) in execute'

さてAuthentication failedということで、keyの位置を教えてあげたほうが良さそうです。

Vagrantsshの設定は以下で知ることができます。

take@MacBook-Air ~/v/sshkit> vagrant ssh-config
Host sshkit
  HostName 127.0.0.1
  User vagrant
  Port 2222
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile /Users/take/vagrant/sshkit/.vagrant/machines/sshkit/virtualbox/private_key
  IdentitiesOnly yes
  LogLevel FATAL

コードを以下のように変更

take@MacBook-Air ~/v/sshkit> cat sample.rb 
require 'sshkit'
require 'sshkit/dsl'
include SSHKit::DSL

remote_host = SSHKit::Host.new('192.168.33.10')
remote_host.user = "vagrant"
remote_host.ssh_options = {
    keys: %w(/Uers/take/vagrant/sshkit/.vagrant/machines/sshkit/virtualbox/private_key),
      auth_methods: %w(publickey)
}

on remote_host do |host|
  execute :uptime
end

無事成功!

take@MacBook-Air ~/v/sshkit> ruby sample.rb 
  INFO [a3837d1b] Running /usr/bin/env uptime as vagrant@192.168.33.10
  INFO [a3837d1b] Finished in 0.116 seconds with exit status 0 (successful).

参考 qiita.com

おわりに

capiをがっつり使ってきましたが、sshkitを使い倒すことも大事かと思います。capiより身軽に使えるので便利な気がする。ソースリーディングとドキュメント翻訳も今後はやっていきたい。