Upload files from local directory to SFTP using Ruby - ruby

For some reason SFTP upload in Ruby (copy files from local directory to SFTP server) doesn't seem to work. I'm currently using Ruby 2.5.3. I would really appreciate any ideas :
My code below:
require 'net/ssh'
require 'net/sftp'
server = 'sftp.secure.net'
username = '123456'
password = "Mypassword*"
uid = '123456'
files = Dir.entries(outdir)
Net::SFTP.start(server, username, :password=>password) do |sftp|
for filename in files
#puts files
puts "Browsing files..."
puts "File: #{filename}"
#puts new_filename
####### replacing , for | ########
if /#{uid}_test_[0-9]{8}_[0-9]{8}.txt$/ =~ filename
file = "#{outdir}\\#{filename}"
puts "SFTPing #{file}"
sftp.upload(file)
puts "SFTP Complete for file #{file}"
puts "Cleanup"
puts "Deleting #{file}."
File.delete(file)
puts "Files were deleted."
end
end
puts "Closing SFTP connection..."
sftp.close
puts "SFTP connection closed."
end

Thank you Kennycoc! That upload! was definitely helpful. Also, sftp.close() should be deleted for sftp. The SFTP connection automatically close. This is needed for FTP I found out, but not for SFTP.
Thanks!
Finalized Version:
files = Dir.entries(outdir)
Net::SFTP.start(hostname, username, :password=>password) do |sftp|
for filename in files
#puts files
puts "Browsing files..."
puts "File: #{filename}"
#puts new_filename
####### replacing , for | ########
if /#{uid}_test_[0-9]{8}_[0-9]{8}.txt$/ =~ filename
file = "#{outdir}\\#{filename}"
puts "SFTPing #{file}"
sftp.upload!(file)
puts "SFTP Complete for file #{file}"
puts "Cleanup"
puts "Deleting #{file}."
File.delete(file)
puts "Files were deleted."
end
end
#puts "Closing SFTP connection..."
#sftp.close()
puts "SFTP connection closed."
end

SFTP is an entirely different protocol based on the network protocol SSH (Secure Shell).
Read SFTP vs. FTPS: The Key Differences
.
Use gem net-sftp.
Ex:
require "net/sftp"
Net::SFTP.start("host", "username", :password: "password") do |sftp|
#from your system(local)
sftp.upload!("/path/to/local", "/path/to/remote")
# through URL
open(your_file_url) do |file_data|
sftp.upload!(file_data, /path/to/remote)
end
end

Related

Changing Directory by Ruby

I am trying to create a simple script to delete all the files from my Desktop(I am using Ubuntu).
puts "Started at #{Time.now}"
Dir.chdir("/Desktop")
Dir.entries(".").each do |file|
if file.to_s.include?("xlsx")
puts "Deleting file #{file}" unless file == "." || file == ".."
File.delete "#{Dir.pwd}/#{file}" unless file == "." || file == ".."
end
end
puts "Ended on #{Time.now}"
But when I generate the code it throws the below error:
chdir': No such file or directory # dir_chdir - /Desktop
(Errno::ENOENT)
What I am doing wrong?
puts "Started at #{Time.now}"
Dir.chdir("#{ENV['HOME']}/Desktop")
Dir.entries(".").select { |file| file.ends_with?('.xlsx') }.each do |file|
puts "Deleting file #{file}"
File.delete "#{Dir.pwd}/#{file}"
end
puts "Ended on #{Time.now}"

Ruby: Net::SSH::Multi using keys

I'm having problems getting Net::SSH::Multi library to work, it should connect to each box and run that command, I'm trying to get the output.
Here's my code:
#!/usr/bin/env ruby
require 'rubygems'
require 'net/ssh'
require 'net/ssh/multi'
#ssh_responses = Array.new
puts "Starting vhost grab..."
# SSH into each instance, and download Vhost Dump
Net::SSH::Multi.start(:on_error => :ignore) do |ssh|
ssh.use 'ec2-1-1-1-1.compute-1.amazonaws.com', :user => 'portal_user', :keys => ['~/.ssh/portal_user.pem']
ssh.use 'ec2-1-1-1-2.compute-1.amazonaws.com', :user => 'portal_user', :keys => ['~/.ssh/portal_user.pem']
puts "Added servers..."
stdout = ""
# run this on all boxes
ssh.exec 'pwd' do |channel, stream, data|
puts "Trying execution..."
channel.request_pty do |c, success|
puts "Trying to request tty..."
if success
puts "Successfully requested tty"
command = "sudo ls /root"
c.exec(command) do |c, success, data|
puts "Executing commands..."
stdout << data if stream == :stdout
end
#ssh_responses << stdout
puts "Storing responses..."
end
end
end
ssh.loop
end
puts #ssh_responses
puts "Script executed."
And the response I get from running is:
$ ruby serverAction.rb
Starting vhost grab...
Added servers...
Trying execution...
Script executed.

Ruby symlink not working in OS X

The following script creates symlinks as expected, but the original file can never be found. Can someone tell me why? They appear to be valid symlinks because they register as aliases in OS X and File.symlink? returns true once they have been created.
#!/usr/bin/env ruby
case ARGV.first when 'link'
file = ARGV[1]
if !File.exist?(file)
puts "Unfortunately, \"#{file}\" was not found."
exit 0
end
bin = "/usr/local/bin/"
if !File.directory?(bin)
puts "#{bin} does not exist!"
puts "creating #{bin}..."
system "mkdir -p #{bin}"
end
if File.extname(file).empty?
if File.symlink?(bin + file)
puts "Unfortunately, \"#{bin + file}\" already exists."
exit 0
end
name = bin + file
puts "Symlinking #{file} to #{name}..."
File.symlink(file, name)
system "chmod +x #{name}"
else
name = file.split(File.extname(file))
name = bin + name.first
if File.symlink?(name)
puts "Unfortunately, \"#{name}\" already exists."
exit 0
end
puts "Symlinking #{file} to #{name}..."
File.symlink(file, name)
system "chmod +x #{name}"
end
else
puts "try: bin link <file>"
end
The script is run in the following way:
ruby script.rb link myfile.rb
To answer my own question, replacing the instances of
File.symlink(file, name)
with
File.symlink(File.expand_path(file), name)
worked perfectly.

use ruby Net::SSH to read a remote file via sudo

I have to read the contents of a remote file I have permissions to (sudo) read with
cat,less or tail.
I am going to be doing this in Ruby so I assume I should be using Net::SSH to do it.
The file is a log file so it can be quite big.
This is the code I am trying now:
require 'rubygems'
require 'net/ssh'
cmd = "sudo cat /var/logs/httpd/ACCESS_log.2012.03.23"
Net::SSH.start( "SERVER" , "USER", :password => "PASSWORD") do |ssh|
ssh.open_channel do |channel|
channel.request_pty
channel.exec(cmd);
channel.on_close do
puts "shell terminated"
end
channel.on_eof do |ch|
puts "remote end is done sending data"
end
channel.on_extended_data do |ch, type, data|
puts "got stderr: #{data.inspect}"
end
channel.on_data do |channel, data|
if data =~ /^\[sudo\] password for USER:/
puts "data works"
channel.send_data 'PASSWORD'
end
channel.on_data do |ch,data|
puts "in third"
puts data.inspect
end
end
channel.on_process do |ch|
puts "in process"
end
ssh.loop
end
end
When I run that I get the following output:
in process
in process
in process
data works
in process
in process
in process
in third
"\r\n"
remote end is done sending data
shell terminated
The log actually currently has a few thousand lines of data in it, because I can read it from the actual server using putty.
How do I get that out from channel.on_data ?
Thanks
I think you need to add a \n to the password you send. This works for me. Note, The place where I commented out the else clause, you could possibly get the info from there too, but it works as you have it, but with a \n in the password.
require 'rubygems'
require 'net/ssh'
cmd = "sudo cat /var/log/mail.log"
HOSTNAME = "myhost.example.com"
USERNAME = "me"
PASSWORD = "12345"
Net::SSH.start( HOSTNAME , USERNAME, :password => PASSWORD) do |ssh|
ssh.open_channel do |channel|
channel.request_pty
channel.exec(cmd);
channel.on_close do
puts "shell terminated"
end
channel.on_eof do |ch|
puts "remote end is done sending data"
end
channel.on_extended_data do |ch, type, data|
puts "got stderr: #{data.inspect}"
end
channel.on_data do |channel, data|
if data =~ /^\[sudo\] password for #{USERNAME}:/
puts "data works"
channel.send_data "#{PASSWORD}\n"
else
#puts "OUTPUT NOT MATCHED: #{data}"
end
channel.on_data do |ch,data|
puts "in third"
puts data.inspect
end
end
channel.on_process do |ch|
puts "in process"
end
ssh.loop
end
end
You are replacing a new on_data callback while executing an on_data callback. I haven't spelunked the internals of Net::SSH, but that could produce surprising behavior.
Try changing your code in your two on_data callbacks to be one, and see if that helps.
channel.on_data do |channel, data|
if data =~ /^\[sudo\] password for USER:/
puts "data works"
channel.send_data 'PASSWORD'
else
puts "in third"
puts data.inspect
if
end
As a side note, since you need sudo to read the logs, someone thinks they and that server are worth protecting. It looks like you're embedding passwords which give privileged access to the server in this ruby program. That implies anyone who can read the program gains the same privileged access. What will you do to limit access to the password in this program?
require 'net/ssh'
Net::SSH.start('host', 'user', :password => "password") do |ssh|
# capture all stderr and stdout output from a remote process
output = ssh.exec!("hostname")
puts output
# capture only stdout matching a particular pattern
stdout = ""
ssh.exec!("ls -l /home/jamis") do |channel, stream, data|
stdout << data if stream == :stdout
end
puts stdout
# run multiple processes in parallel to completion
ssh.exec "sed ..."
ssh.exec "awk ..."
ssh.exec "rm -rf ..."
ssh.loop
# open a new channel and configure a minimal set of callbacks, then run
# the event loop until the channel finishes (closes)
channel = ssh.open_channel do |ch|
ch.exec "/usr/local/bin/ruby /path/to/file.rb" do |ch, success|
raise "could not execute command" unless success
# "on_data" is called when the process writes something to stdout
ch.on_data do |c, data|
$stdout.print data
end
# "on_extended_data" is called when the process writes something to stderr
ch.on_extended_data do |c, type, data|
$stderr.print data
end
ch.on_close { puts "done!" }
end
end
channel.wait
# forward connections on local port 1234 to port 80 of www.capify.org
ssh.forward.local(1234, "www.capify.org", 80)
ssh.loop { true }
end
Latest Document 17.11.25

Executing Password Change over Ruby Net-SSH

I am looking to execute a password change over Net-ssh and this code seems to hang:
Net::SSH.start(server_ip, "user", :verbose => :debug ) do |session|
session.process.popen3("ls") do |input, output, error|
["old_pass","test", "test"].each do |x|
input.puts x
end
end
end
I know the connection works because using a simple exec I can get the output from ls on the remote server, but this hangs.
Any ideas?
The last message from debug is that the public key succeeded.
This one will solve your issue ,, note this script to change password for a list of servers list in file
#~~~~~~~~~~~~~~~~~~~~~~~
# Change Password is a simple script to change the password for a list of servers
# Coded by : Sabry Saleh
# License : GPL2
#~~~~~~~~~~~~~~~~~~~~~~~
#=-Notes-=
# You have to install ruby + net-ssh gems
# sudo gem install net-ssh
#~~~~~~~~~~~~~~~~~~~~~~~
require 'net/ssh'
host = IO.readlines('test1.txt') # full path of servers' list
port = 22 # SSH port
user = 'username' # username
i = 0
while i < host.length
Net::SSH.start(host[i], user , :password => "User pass" , :port=> port) do |ssh|
ssh.open_channel do |channel|
channel.on_request "exit-status" do |channel, data|
$exit_status = data.read_long
end
channel.request_pty do |channel, success|
channel.exec("sudo passwd UserName") # Logged user shuold be root or sudoers memeber
if success
channel.on_data do |channel, data|
puts data.inspect.chomp("\r\n")
channel.send_data("New pass\n") # put the New password you need to set
sleep 0.1
end
else
puts "FAILED!!"
end
end
channel.wait
puts "SUCCESS!!" if $exit_status == 0
end
end
i += 1
end

Resources