Skip to content

Instantly share code, notes, and snippets.

@maxivak
Last active May 4, 2017 09:34
Show Gist options
  • Save maxivak/0a7dcb248997ce637a5915b066d6dd95 to your computer and use it in GitHub Desktop.
Save maxivak/0a7dcb248997ce637a5915b066d6dd95 to your computer and use it in GitHub Desktop.
Running commands on remote server with SSH using SSHKit

Running commands on remote server with SSH using SSHKit

We have a server which we can connect by SSH and we want to run commands there.

We will use SSHKit gem for it.

Gems:

gem 'net-ssh'
gem 'sshkit'
gem 'sshkit-sudo'

require libs in Ruby code:

  require 'net/ssh'

  require 'sshkit'
  require 'sshkit/dsl'
  include SSHKit::DSL
  require 'sshkit/sudo'
  

Run command

  def self.run_ssh_cmd(server_ip, username, pwd, cmd)
      require 'sshkit'
      require 'sshkit/dsl'
      include SSHKit::DSL

      host = SSHKit::Host.new({hostname: server_ip, user: username})
      host.password = pwd

      output = ''

      SSHKit::Coordinator.new(host).each in: :sequence do
        output = capture cmd
      end


      #
      return {res: 1, output: output}

    rescue => e
      {
          res: 0,
          output: output,
          error: e.message
      }
    end


  • run command
res = run_ssh_cmd('127.0.0.1', 'myuser', 'mypwd', 'touch /tmp/1.txt')

# see output

puts res[:output]


Run command with sudo

def run_ssh_cmd_sudo(hostname, ssh_user, ssh_pass, cmd, handler=nil)
    host = SSHKit::Host.new("#{ssh_user}@#{hostname}")
    host.password = ssh_pass

    on host do |host|
      execute("#{cmd}", interaction_handler: handler)
    end

    #
    return {res: 1, output: ""}

  rescue => e
    {
        res: 0,
        error: e.message
    }
  end
  
  
  • run command

to run command we need to define a method which represents interaction with SSH session and provides passwords for input.

def interaction_handler_pwd(user, pwd, host='')
    {
        "#{user}@#{host}'s password:" => "#{pwd}\n",
        /#{user}@#{host}'s password: */ => "#{pwd}\n",
        "password: " => "#{pwd}\n",
        "password:" => "#{pwd}\n",
        "Password: " => "#{pwd}\n",
    }
end

# run command

res = run_ssh_cmd_sudo(
  '192.168.1.1', 'myuser', 'pwd', 
  'sudo touch /var/www/1.txt',      
  interaction_handler_pwd('myuser', 'pwd', 'hostname')
    )

puts "output: #{res[:output]}"

Passwordless

  • if you don't want to enter password each time

  • edit visudo

myuser ALL=(ALL) NOPASSWD: ALL
  • execute command

all_servers = ['myserver.com']
myuser = 'myuser'
pwd = 'mypwd'

    on all_servers do |srv|
      as(user: myuser) do
        execute('sudo apt-get update', interaction_handler: {
            "password: " => "#{pwd}\n",
        })
      end

    end
  end

Upload file to remote server

  • use upload! inside ssh session to upload file
def ssh_upload_file(hostname, ssh_user, ssh_pass, source_file, dest_file, handler=nil)
 
    host = SSHKit::Host.new("#{ssh_user}@#{hostname}")
    host.password = ssh_pass

    f_temp = "/tmp/#{SecureRandom.uuid}"

    # sshkit
    SSHKit::Coordinator.new(host).each in: :sequence do
      # upload to temp file
      upload! source_file, f_temp

      # upload to dest
      execute("cp #{f_temp} #{dest_file}", interaction_handler: handler)

    end

    #
    return     {res: 1, output: ""}
  rescue => e
    {
        res: 0,
        error: e.message
    }
  end
  • using DSL
  • this variant doesn't work if server has no sudo
def ssh_upload_file(hostname, ssh_user, ssh_pass, source_file, dest_file, handler=nil)
 
    host = SSHKit::Host.new("#{ssh_user}@#{hostname}")
    host.password = ssh_pass

    f_temp = "/tmp/#{SecureRandom.uuid}"

    # sshkit
    on host do |host|
      as(user: ssh_user) do

      end

      # upload to temp file
      upload! source_file, f_temp

      # upload to dest
      execute("cp #{f_temp} #{dest_file}", interaction_handler: handler)

    end

    #
    return     {res: 1, output: ""}
  rescue => e
    {
        res: 0,
        error: e.message
    }
  end
  

Run

in case when your command runs capistrano. We have to use interaction handler.

cmd = "cap production task:name"

user = "myuser"
pwd = "mypwd"
host = "myhost"

ih = {
          "#{user}@#{host}'s password:" => "#{pwd}\n",
          /#{user}@#{host}'s password: */ => "#{pwd}\n",
          "password: " => "#{pwd}\n",
          "password:" => "#{pwd}\n",
          "Password: " => "#{pwd}\n",
      }
      
SSHKit::Coordinator.new(host).each in: :sequence do
  execute(cmd, interaction_handler: ih)
end

More examples

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment