The maze book for programmers!
mazesforprogrammers.com

Algorithms, circle mazes, hex grids, masking, weaving, braiding, 3D and 4D grids, spheres, and more!

DRM-Free Ebook

The Buckblog

assorted ramblings by Jamis Buck

Net::SSH 2.0 (and friends)

2 May 2008 — 3-minute read

At last! Net::SSH 2.0 is available! Also available are Net::SFTP 2.0, Net::SCP 1.0, Net::SSH::Gateway 1.0, and Net::SSH::Multi 1.0.

1
2
3
4
5
$ gem install net-ssh \
  net-sftp \
  net-scp \
  net-ssh-gateway \
  net-ssh-multi

All of these Ruby libraries are for communicating with remote servers via the SSH protocol in different ways.

Net::SSH and Net::SFTP are both significant upgrades from their previous incarnations; if you have used either library in the past, you’ll want to read the documentation (Net::SSH, Net::SFTP). The Net::SSH API is still fairly similar to the way it was before, but the Net::SFTP API is entirely different.

All have pretty complete RDoc documentation, so you should be able to employ “ri” to good effect to find your way around the libraries. (Try “ri Net::SSH”, for example, to get started.)

Per-library synopses follow.

Net::SSH 2.0

This is a significant upgrade from Net::SSH 1.x. Changes from 1.x include (but are not limited to):

  • Net::SSH 2.0 no longer requires the Needle dependency-injection library. This has made the library faster, lighter, and easier to understand.
  • The “sync” and “sync-shell” services have been removed and will not return. (If you need the functionality of those services, I strongly encourage you to port them to Net::SSH 2 and release them as separate extensions.)
  • Options in ~/.ssh/config files are (partially) supported, and are loaded by default now.
  • The Net::SSH::start method now requires both a host and a username as the first two parameters, rather than inferring the username.
  • There is now a Session#exec method that makes it easier than ever to just execute and interact with a command.
  • Channel now sports an #on_process method that you can send a block to, which will be executed on every pass of the event loop.
  • Channel#on_request now accepts a string to indicate which request you want to respond to.
  • Channel open failures may be captured via Channel#on_open_failure.
  • Lots, lots, LOTS, more—refer to the rdocs for all the gory details.
1
2
3
4
5
6
require 'net/ssh'

Net::SSH.start('localhost', 'jamis') do |ssh|
  ssh.exec('hostname') # prints the results to $stdout
  ssh.loop
end

Net::SFTP 2.0

This is a complete rewrite of the original Net::SFTP 1.x code, and shares very, very little in common with it. The new version has a much cleaner implementation and API, and provides some really handy methods for transferring files and directories.

1
2
3
4
5
6
7
8
9
10
require 'net/sftp'

Net::SFTP.start('localhost', 'jamis') do |sftp|
  sftp.upload! "/local/file", "/remote/file"
  sftp.download! "/remote/file", "/local/file"

  sftp.file.open("/remote/file", "w") do |file|
    file.puts "here is some data"
  end
end

Net::SCP 1.0

This provides a way to transfer files and directories via the SCP protocol, over Net::SSH.

1
2
3
4
5
6
require 'net/scp'

Net::SCP.start('localhost', 'jamis') do |scp|
  scp.upload! "/local/file", "/remote/file"
  scp.download! "/remote/file", "/local/file"
end

Net::SSH::Gateway 1.0

This library makes it easy to tunnel connections though firewalls. You simply connect to the gateway machine, and then specify which ports you want forwarded.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
require 'net/ssh/gateway'

gateway = Net::SSH::Gateway.new('host', 'user')

gateway.ssh("host.private", "user") do |ssh|
  puts ssh.exec!("hostname")
end

gateway.open("host.private", 80) do |port|
  require 'net/http'
  Net::HTTP.get_print("127.0.0.1", "/path", port)
end

gateway.shutdown!

Net::SSH::Multi 1.0

This library makes it simple to open multiple Net::SSH connections and tie them all together, running commands in parallel (much like Capistrano does).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
require 'net/ssh/multi'

Net::SSH::Multi.start do |session|
  # access servers via a gateway
  session.via 'gateway', 'gateway-user'

  # define the servers we want to use
  session.use 'user1@host1'
  session.use 'user2@host2'

  # define servers in groups for more granular access
  session.group :app do
    session.use 'user@app1'
    session.use 'user@app2'
  end

  # execute commands on all servers
  session.exec "uptime"

  # execute commands on a subset of servers
  session.with(:app).exec "hostname"

  # run the aggregated event loop
  session.loop
end

Enjoy!

Reader Comments

All this looks great! Your work on these libraries really helps Ruby be a successful language.

I have one suggestion, though: session.via(gateway info here) seems like it’s a perfect match for the decorator or wrapper pattern. That is, the following code makes more sense to me:

Net::SSH::Multi.start do |outer_session|
  # access servers via a gateway
  outer_session.via 'gateway', 'gateway-user' do |session|
    ... above code here ...
  end
end

That is, session.via should return a new session object rather than being mutable. This would allow chained gateways, though I’m not really sure anybody needs that. The real advantage is the decrease on the conceptual weight of Session – it need only return a WrappedSession or a GatewaySessionWrapper or something instead of needing to keep track of the gateway information itself.

Bravo on the release!

@James, thanks for the suggestion, I’ll take it under consideration. What session#via really does, though, is set the default gateway instance to use. You can specify per-session gateways with session.use(‘host’, ‘name’, :via => Net::SSH::Gateway.new(...)).

Congrats Jamis!! This is awesome!

Yeah this is great. Have been using v1 of Net::SSH & Net::SFTP quite a bit.

The cleaned up SFTP api is a nice touch – mimicing the Dir and File methods similar to how “zip/zipfilesystem” works makes it easy to write code that is agnostic of the method it uses to access files (local fs, zip, remote sftp, etc). Kind of a de-facto ruby vfs.

I like how SSH:Multi + Rake gives you a very low-magic version of capistrano.

I try to update my gem and not find the 2.0 version :

  1. gem search net-ssh -r
  • REMOTE GEMS *

net-ssh (1.1.2, 1.1.1, 1.1.0, 1.0.10, 1.0.9, 1.0.8, 1.0.7, 1.0.6, 1.0.5, 1.0.4, 1.0.3, 1.0.2, 1.0.1, 1.0.0, 0.9.0, 0.6.0, 0.5.0, 0.1.0, 0.0.5, 0.0.4, 0.0.3, 0.0.2, 0.0.1) Net::SSH is a pure-Ruby implementation of the SSH2 client protocol.

Where is the 2.0 ??

Solved with, gem update—system

Great work, thanks.

net/ssh requires jruby-openssl when when running on JRuby, is there some way for it to be added as dependency on the net/ssh gem?

Also, running on JRuby+Windows I had to add ‘net/ssh/pageant.rb’ to $LOADED_FEATURES before requiring net/ssh, jruby does not have a ‘dl/import’

http://www.nabble.com/dl-import-on-JRuby-td16465077.html

Can you provide information on the SSH v1.1 methods that you have removed, and what equivalent methods should be used?

I’ve been using Net::SSH for a while now with great success. For the first time today I attempted to setup a forwarded port to access mysql on a remote host that only listens to 127.0.0.1.

I can set the tunnel up fine and then access mysql using the standard mysql binary client, or I can setup the tunnel using ‘ssh’ itself and then access the database via ruby, but not both.

Any thoughts?

Jamis, trying SSH 2.0.2 however I seem to have a different path set in my profile from when I used SSH 1.1.2, the path doesn’t contain sudo. This is using the same server and account.

Any thoughts?

Thanks, Neil.

/usr/lib/ruby/gems/1.8/gems/net-ssh-2.0.2/lib/net/ssh/transport/algorithms.rb:192:in `prepare_preferred_algorithms!’: unsupported hmac algorithm: `[email protected]’ (NotImplementedError) from /usr/lib/ruby/gems/1.8/gems/net-ssh-2.0.2/lib/net/ssh/transport/algorithms.rb:185:in `each’ from /usr/lib/ruby/gems/1.8/gems/net-ssh-2.0.2/lib/net/ssh/transport/algorithms.rb:185:in `prepare_preferred_algorithms!’ from /usr/lib/ruby/gems/1.8/gems/net-ssh-2.0.2/lib/net/ssh/transport/algorithms.rb:96:in `initialize’ from /usr/lib/ruby/gems/1.8/gems/net-ssh-2.0.2/lib/net/ssh/transport/session.rb:77:in `new’ from /usr/lib/ruby/gems/1.8/gems/net-ssh-2.0.2/lib/net/ssh/transport/session.rb:77:in `initialize’ from /usr/lib/ruby/gems/1.8/gems/net-ssh-2.0.2/lib/net/ssh.rb:182:in `new’ from /usr/lib/ruby/gems/1.8/gems/net-ssh-2.0.2/lib/net/ssh.rb:182:in `start’ from /home/sysnjk/scripts/wlinstances.rb:7

:(

fixed it with

Net::SSH.start(hostname, username, :hmac => ‘hmac-md5’) do |ssh|

end

but i think [email protected] is quite common?

However, I can’t find a way to put the :hmac option in net/ssh/multi.

:(

p.s. this makes net/ssh incompatible with the default solaris 10 install.

@Anko, I’m afraid I’ve all but given up trying to keep any solaris flavor happy. They all seem to do their own thing. As for “[email protected]” being common, this is the first time in 4 years of working with Net::SSH that I’ve ever even heard of it. If you really want it supported, though, please look at providing a patch. I’d certainly consider one for that.

Okay will do, but it might take some time as i don’t have solaris at home and finding work time for it is near impossible.

I’ve found a public domain umac implementation @ http://fastcrypto.org/ so i’ll start reading the ssh specs tonight :)