Net::SFTP::Session can't run stat, apparently has a Nil channel - ruby

So I have some code that checks if there's a certain file on remote SFTP server:
def size
adapter.sftp.stat(path).size
end
where sftp is a Net::SFTP::Session object defined in this case as
#sftp = Net::SFTP.start(host, username, password: password)
and path is the file path to the object that I want to call stat() on.
Unfortunately, when I try to excecute this code, I get this error:
NoMethodError:
undefined method `send_data' for nil:NilClass
# /usr/local/lib/ruby/gems/2.2.0/gems/net-sftp-2.1.2/lib/net/sftp/session.rb:814:in `send_packet'
# /usr/local/lib/ruby/gems/2.2.0/gems/net-sftp-2.1.2/lib/net/sftp/protocol/base.rb:45:in `send_request'
# /usr/local/lib/ruby/gems/2.2.0/gems/net-sftp-2.1.2/lib/net/sftp/protocol/01/base.rb:90:in `open'
# /usr/local/lib/ruby/gems/2.2.0/gems/net-sftp-2.1.2/lib/net/sftp/session.rb:830:in `request'
# /usr/local/lib/ruby/gems/2.2.0/gems/net-sftp-2.1.2/lib/net/sftp/session.rb:182:in `open'
# /usr/local/lib/ruby/gems/2.2.0/gems/net-sftp-2.1.2/lib/net/sftp/session.rb:191:in `open!'
# /usr/local/lib/ruby/gems/2.2.0/gems/net-sftp-2.1.2/lib/net/sftp/operations/file_factory.rb:40:in `open'
# /Users/Ben/remote_filesystem/lib/remote_filesystem/path/sftp.rb:46:in `size'
# ./sftp_spec.rb:72:in `block (3 levels) in <top (required)>'
As far as I can tell from looking at the source code for Net::SFTP::Session, on line 814 of session.rb, channel.send_data is called, but apparently my SFTP Session has a Nil channel for some reason. Can anyone explain how to fix this issue?

If you're caching sftp, the cache might have been invalidated. I came across this exception, because I had tried to call ftp.file.open on an ftp connection that was no longer open.

As mentioned earlier, it means your SFTP session is terminated.
Check TCP logs (wireshark is your friend), the session may be terminated by the peer in the meantime.
A case when such error is happening is if you are doing write(data) operation with data length that exceeds TCP window size on the receiving side. A fix would be to repeat write operations with a buffer, like
io = StringIO.new(data)
sftp_session.file.open(filename, "w") do |file|
while buffer = io.read(BUFFER_SIZE)
file.write(buffer)
end
end

Related

How to pass -m argument to sambal gem?

I have downloaded the sambal gem. Here is my smbclient command which I know works:
smbclient \\10.9.167.71\Dept -U CORP\thinger -m SMB2
I want to instantiate a sambal client object using those exact same parameters. Here is what I tried:
client = Sambal::Client.new(domain: 'CORP', host: '10.9.167.71', share: 'Dept', user: 'thinger', password: '--no-pass')
-m SMB2 gets left out of the equation, but I know it has to exist in order for this to work. Reviewing the documentation I see no way to pass this argument. So then it's not surprising I get this:
Traceback (most recent call last):
3: from test.rb:6:in `<main>'
2: from test.rb:6:in `new'
1: from /usr/local/bundle/gems/sambal-0.2.2/lib/sambal/client.rb:29:in `initialize'
/usr/local/bundle/gems/sambal-0.2.2/lib/sambal/client.rb:67:in `rescue in initialize': Unknown Process Failed!! (Failed to connect): "Failed to connect" (RuntimeError)
/usr/local/bundle/gems/sambal-0.2.2/lib/sambal/client.rb:64:in `initialize'
test.rb:6:in `new'
test.rb:6:in `<main>'
Is there a way? Reviewing this turns up nothing I can see, but it is plausible I am just not experienced enough to understand:
https://github.com/johnae/sambal/blob/master/lib/sambal/client.rb
I want to try to avoid using the smbclient commands directly in my ruby script - it seems, simply, a painful way to do things. I will do it if I have to, though.
The -m flag to smbclient is an alias to --max-protocol.
This flag can be set in sambal by passing the the max_protocol option to the initializer. With your other options, this will look like:
client = Sambal::Client.new(domain: 'CORP', host: '10.9.167.71', share: 'Dept', user: 'thinger', password: '--no-pass', max_protocol: 'SMB2')

How to SSH to a jumpoff server and SSH from that jumpoff server in Ruby

I was looking for a way to SSH to a jumpoff server using ruby and from that server SSH to another server. The jumpoff server is the only server with access to the final server. I was trying Net::SSH but I can't seem to get an SSH connection from within the established SSH connection.
irb(main):021:0> require "net/ssh"
=> false
irb(main):022:0> Net::SSH.start("10.10.10.10", "admin") do |ssh|
irb(main):023:1* result = ssh.exec!("mm | grep 'dv ' | awk '{print $1}'").strip
irb(main):024:1> p "Going to try to login to #{result}"
irb(main):025:1> Net::SSH.start(result, "admin") do |ssh1|
irb(main):026:2* hostname = ssh1.exec!("hostname")
irb(main):027:2> p "In #{hostname}"
irb(main):029:2> end
irb(main):030:1> end
"Going to try to login to vz-int-api02"
SocketError: initialize: name or service not known
from org/jruby/ext/socket/RubyTCPSocket.java:129:in `initialize'
from org/jruby/RubyIO.java:1178:in `open'
from /usr/local/jruby-1.7.9/lib/ruby/gems/shared/gems/net-ssh-2.9.2 /lib/net/ssh/transport/session.rb:70:in `initialize'
from org/jruby/ext/timeout/Timeout.java:105:in `timeout'
from /usr/local/jruby-1.7.9/lib/ruby/gems/shared/gems/net-ssh-2.9.2 /lib/net/ssh/transport/session.rb:67:in `initialize'
from /usr/local/jruby-1.7.9/lib/ruby/gems/shared/gems/net-ssh-2.9.2 /lib/net/ssh.rb:207:in `start'
from (irb):25:in `evaluate'
from /usr/local/jruby-1.7.9/lib/ruby/gems/shared/gems/net-ssh-2.9.2 /lib/net/ssh.rb:215:in `start'
from (irb):22:in `evaluate'
from org/jruby/RubyKernel.java:1119:in `eval'
from org/jruby/RubyKernel.java:1519:in `loop'
from org/jruby/RubyKernel.java:1282:in `catch'
from org/jruby/RubyKernel.java:1282:in `catch'
from /usr/local/jruby-1.7.9/bin/irb:13:in `(root)'
I also tried running the SSH connection directly from the established SSH connection but there seems to be no way to initiate an new session from a session object:
irb(main):035:1> ssh.start(result, "admin") do |ssh1|
gives:
NoMethodError: undefined method `start' for #<Net::SSH::Connection::Session:0x5b11d0d8>
I'm probably tackling this from the wrong angle but I could not find anyone who wanted to SSH from a SSH connection.
This is possible, but you should use SSH's built-in ProxyCommand setting to do it. If you have an ~/.ssh/config file, then Net::SSH will use that. For instance it could look like this:
Host the.protected.box
Hostname 10.10.10.20
ProxyCommand ssh 10.10.10.10 nc %h %p 2> /dev/null
Then you can say:
Net::SSH.start('the.protected.box', 'admin')
and the proxying will just work.
If you run into issues it is often easier to debug this using the ssh command-line tool before translating your work into Ruby.
If you don't want to use ~/.ssh/config, you can set up a proxy command in code instead:
require 'net/ssh/proxy/command'
proxy = Net::SSH::Proxy::Command.new('ssh 10.10.10.10 nc %h %p 2>/dev/null')
Net::SSH.start('10.10.10.20', 'admin', proxy: proxy)
You can do this with SSH port forwarding. Net::SSH gateway does this for you, but it's easy enough to do yourself.
It basically has some code that looks like this:
Net::SSH.start(...) do |ssh|
ssh.forward(12345, "internal host", 22)
ssh.loop { true }
end
That sets up the tunnel and then you make a new SSH connection to locahost port 12345.

Ruby DNS resolution hangs every other time

I have this piece of ruby code that works intermittently, I can call it in the shell multiple times ctrl-c-ing when it hangs and it will work instantaneously half of the times and hang forever the other half.
require 'resolv'
puts "initializing"
txt = Resolv::DNS.open do |dns|
records = dns.getresources("www.google.com", Resolv::DNS::Resource::IN::A)
records.empty? ? nil : records.map{|rec| rec.address}.compact
end
puts "records are #{txt}"
Here the output I see in both cases
[ ~]$ ruby test.rb
initializing
records are 216.58.217.132
[ ~]$ ruby test.rb
initializing
records are 216.58.217.132
[ ~]$ ruby test.rb
initializing
^C/usr/lib/ruby/1.8/resolv.rb:620:in `select': Interrupt
from /usr/lib/ruby/1.8/resolv.rb:620:in `request'
from /usr/lib/ruby/1.8/resolv.rb:489:in `each_resource'
from /usr/lib/ruby/1.8/resolv.rb:975:in `resolv'
from /usr/lib/ruby/1.8/resolv.rb:973:in `each'
from /usr/lib/ruby/1.8/resolv.rb:973:in `resolv'
from /usr/lib/ruby/1.8/resolv.rb:972:in `each'
from /usr/lib/ruby/1.8/resolv.rb:972:in `resolv'
from /usr/lib/ruby/1.8/resolv.rb:970:in `each'
from /usr/lib/ruby/1.8/resolv.rb:970:in `resolv'
from /usr/lib/ruby/1.8/resolv.rb:481:in `each_resource'
from /usr/lib/ruby/1.8/resolv.rb:468:in `getresources'
from test.rb:4
from /usr/lib/ruby/1.8/resolv.rb:307:in `open'
from test.rb:3
I understand that DNS is an external service and it might be flaky, but that does not explain how it can work immediately sometimes and hang forever other times, and also, when I use the host www.google.com command it always returns immediately.
How can I make this work predictably?
I tried your code and cannot reproduce the problem. Note that for your specific example the response is not always the same, which makes sense for www.google.com.
tmp> ruby resolv.rb
# initializing
# records are [#<Resolv::IPv4 216.58.211.100>]
tmp> ruby resolv.rb
# initializing
# records are [#<Resolv::IPv4 74.125.136.105>, #<Resolv::IPv4 74.125.136.106>, #<Resolv::IPv4 74.125.136.147>, #<Resolv::IPv4 74.125.136.99>, #<Resolv::IPv4 74.125.136.103>, #<Resolv::IPv4 74.125.136.104>]
I think that the problem you are experiencing lies outside of your code. The host command does a bit more than making a DNS request. It also checks the /etc/hosts file, and there might be some local cache involved. You should try to test your DNS, maybe with the dig command, and check if the answers are really always coming from the DNS server, not from a cache.
In your code, you might want to enable a timeout, e.g.:
dns.timeouts = 3

Vagrant: missing file id_rsa

I'm using windows with homestead and I'm trying to enter vagrant up on the command line but I'm getting this error:
C:/www/Homestead/scripts/homestead.rb:106:in read': No such file or directory # rb_sysopen - C:/Users/Niall/.ssh/id_rsa (Errno::ENOENT)
from C:/www/Homestead/scripts/homestead.rb:106:inblock (2 levels) in configure'
from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.8.1/plugins/kernel_v2/config/vm_provisioner.rb:72:in call'
from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.8.1/plugins/kernel_v2/config/vm_provisioner.rb:72:inadd_config'
from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.8.1/plugins/kernel_v2/config/vm.rb:321:in provision'
from C:/www/Homestead/scripts/homestead.rb:103:inblock in configure'
from C:/www/Homestead/scripts/homestead.rb:102:in each'
from C:/www/Homestead/scripts/homestead.rb:102:inconfigure'
from C:/www/Homestead/Vagrantfile:20:in block in <top (required)>'
from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.8.1/lib/vagrant/config/v2/loader.rb:37:incall'
from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.8.1/lib/vagrant/config/v2/loader.rb:37:in load'
from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.8.1/lib/vagrant/config/loader.rb:113:inblock (2 levels) in load'
from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.8.1/lib/vagrant/config/loader.rb:107:in each'
from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.8.1/lib/vagrant/config/loader.rb:107:inblock in load'
from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.8.1/lib/vagrant/config/loader.rb:104:in each'
from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.8.1/lib/vagrant/config/loader.rb:104:inload'
from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.8.1/lib/vagrant/vagrantfile.rb:28:in initialize'
from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.8.1/lib/vagrant/environment.rb:740:innew'
from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.8.1/lib/vagrant/environment.rb:740:in vagrantfile'
from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.8.1/lib/vagrant/environment.rb:486:inhost'
from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.8.1/lib/vagrant/environment.rb:208:in block in action_runner'
from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.8.1/lib/vagrant/action/runner.rb:33:incall'
from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.8.1/lib/vagrant/action/runner.rb:33:in run'
from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.8.1/lib/vagrant/environment.rb:473:inhook'
from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.8.1/lib/vagrant/environment.rb:722:in unload'
from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.8.1/bin/vagrant:177:inensure in '
from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.8.1/bin/vagrant:177:in `'
I searched online but all I could find was someone else asking the same question but no answer. What does this error mean? I went to the folder in particular and indeed there is no file called id_rsa. Can anyone shine some light on how to rectify this?
It looks like you need to generate an SSH key? You can do that by entering the command ssh-keygen -t rsa -C "your_email#example.com" (with your own email obviously) if you're using Git Bash.
When it prompts you for where to save the key just hit enter. Passphrase is also optional, just hit enter twice for a blank passphrase.

SCP did not finish successfully (1) Error when Trying to Upload from windows to Ubuntu using ruby

My code
IMAGE_DIR = 'D:\File_Server\Nisa_Costcutter\Master Nisa CC Logos'
require 'net/ssh'
require 'net/scp'
def scopy_file(file)
puts "Transferring #{file.path}"
Net::SCP.upload!('192.168.254.5',
'passenger',
file,
'/var/www/pinpointlms.co.uk/shared/logos',
:ssh => {password: '*****'})
end
puts "Starting Upload"
Dir.foreach(IMAGE_DIR) do |name|
if name.length > 4 && name[-4..-1].upcase == '.BMP'
filename=name.strip()
file = File.new(File.join(IMAGE_DIR, filename))
if (Time.now - file.mtime) > 86400
scopy_file(file)
end
end
end
puts "End of Transfer"
I am trying to copy some files from a windows box to an Ubuntu box using Ruby but I get the following output:
Starting Upload
Transferring D:\File_Server\Nisa_Costcutter\Master Nisa CC Logos/Z2579.BMP
C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/net-scp-1.1.2/lib/net/scp.rb:359:in `block (3 levels) in start_command': SCP did not finish successfully (1)
(Net::SCP::Error) from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/net-ssh-2.8.0/lib/net/ssh/connection/channel.rb:591:in `call'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/net-ssh-2.8.0/lib/net/ssh/connection/channel.rb:591:in `do_close'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/net-ssh-2.8.0/lib/net/ssh/connection/session.rb:586:in `channel_close'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/net-ssh-2.8.0/lib/net/ssh/connection/session.rb:465:in `dispatch_incoming_packets'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/net-ssh-2.8.0/lib/net/ssh/connection/session.rb:221:in `preprocess'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/net-ssh-2.8.0/lib/net/ssh/connection/session.rb:205:in `process'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/net-ssh-2.8.0/lib/net/ssh/connection/session.rb:169:in `block in loop'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/net-ssh-2.8.0/lib/net/ssh/connection/session.rb:169:in `loop'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/net-ssh-2.8.0/lib/net/ssh/connection/session.rb:169:in `loop'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/net-ssh-2.8.0/lib/net/ssh/connection/session.rb:118:in `close'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/net-scp-1.1.2/lib/net/scp.rb:205:in `ensure in start'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/net-scp-1.1.2/lib/net/scp.rb:205:in `start'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/net-scp-1.1.2/lib/net/scp.rb:221:in `upload!'
from C:/Users/administrator.GASKANDHAWLEY/Desktop/copy_images2.rb:8:in `scopy_file'
from C:/Users/administrator.GASKANDHAWLEY/Desktop/copy_images2.rb:24:in`block in <main>'
from C:/Users/administrator.GASKANDHAWLEY/Desktop/copy_images2.rb:17:in`foreach'
from C:/Users/administrator.GASKANDHAWLEY/Desktop/copy_images2.rb:17:in
`'
I am a ruby beginner so any help you can give me on how to debug this code further will be much appreciated.
Thanks
You probably don't have user access to upload (write) that file in the given directory '/var/www/pinpointlms.co.uk/shared/logos' on your Ubuntu server.
Try to save it without giving it a full path, so it ends up in the user's home directory on the Ubuntu server. If this will work, then your problem is related with the user permission on the server.
In case it's helpful for others, I got the exact same error as this when trying to upload a file to a path that didn't yet exist. scp from shell will allow you to do this, but Net::SCP will fail with this error.
i.e.
scp "my.file" "/foo/bar/"
if /foo exists but /foo/bar/ does not, scp will create /foo/bar and put your file there (assuming permissions allow you to do this).
However - under the same circumstances - this will fail with the error given in the question
scp.upload!(my_file, "/foo/bar/")
The only solution I've found is to first create the path you want locally, and then upload with the :recursive option on, like so:
scp.upload!("bar/", "/foo" :recursive => true)
where ./bar contains my_file.

Resources