• Stars
    star
    232
  • Rank 171,557 (Top 4 %)
  • Language
    Ruby
  • License
    MIT License
  • Created over 15 years ago
  • Updated about 9 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Safe, parallel access to Unix shells from Ruby

Rye - v0.9

Safely run SSH commands on a bunch of machines at the same time (from Ruby).

Inspired by Rush and compatible with Ruby 1.8, 1.9, and JRuby 1.3+!

As of 0.9.8, all gem releases are signed. See Installation.

Overview

Rye is a Ruby abstraction for executing shell commands via SSH. By default, Rye errs on the side of caution by running in “safe-mode” which specifies a default whitelist of commands and aggressively escapes all command arguments. For example, file globs and the “rm” command are not available in safe-mode, so you can’t do this: rbox.rm('-rf', '/etc/*/').

Rye does not require anything to be installed on the server side (other than an SSH daemon) so it can be run from any machine with Ruby, OpenSSL, and OpenSSH.

Example 1 – Execute commands on a remote machine

Shell commands are executed by calling methods on a Rye::Box object.

rbox = Rye::Box.new('hostname')
rbox.pwd                               # => "/home/rye"
rbox.uname :a                          # => "Darwin rye-stage 9.7.0 ..."

Method arguments are sent directly as arguments to the shell command. Single-character Symbols are assumed to be single-character switches. e.g. rbox.uname :a becomes uname -a.

The return value for a command is a modified Array containing the contents of STDOUT split by line. It also gives access to STDERR and the exit code

ret = rbox.uptime                      # => "11:02  up 16:01, 3 users"
ret.stderr                             # => []
ret.exit_status                          # => 0
ret.stdout                             # => "11:02  up 16:01, 3 users"
ret.stdout.class                       # => Array
ret.class                              # => Rye::Rap

Example 2 – Paths and environment variables

You can change directories.

rbox.cd '/tmp'
rbox.pwd                               # => '/tmp'
rbox['/etc'].ls                        # => ['apache', 'init.d', ...]
rbox.pwd                               # => '/etc'
rbox.cd                                # => '/home/rye'

You can specify environment variables.

rbox.setenv('TIPPLE', "Forty Creek")
rbox.getenv 'TIPPLE'                   # => "Forty Creek"

Example 3 – Adding and removing commands

You can add and remove commands to the whitelist.

Rye::Cmd.add_command :anything, '/usr/bin/uptime'
rbox = Rye::Box.new
rbox.anything
Rye::Cmd.remove_command :anything
rbox.anything                          # => Rye::CommandNotFound exception

Example 4 – Disabling Safe Mode

Safe mode can be disabled on one of the following ways.

rbox = Rye::Box.new 'HOST', :safe => false
  OR
rbox.disable_safe_mode

When safe-mode is disabled, you can run any command (regardless of what is defined in the whitelist) with any valid arguments (fileglobs, tildas, etc…).

rbox.kill '-SIGHUP', 1928111
rbox.rm 'path/2/*'

You can also execute any valid shell command.

rbox.execute 'ps aux | grep ruby > /tmp/ruby-process-list'

See the “About Safe Mode” section below for more information.

Example 5a – Accessing Multiple Machines

Shell commands can be executed on multiple machines using a Rye::Set object. Create a “set” of machines.

rbox = Rye::Box.new 'HOST1'
rset = Rye::Set.new
rset.add_boxes rbox, 'HOST2'           # Add boxes as hostnames or objects

Then call methods just like with Rye::Box, except now the return value is an Array of Arrays. The order of return values matches the order the machines were added to the set.

rset.hostname                          # => [["HOST1"], ["HOST2"]]
rset.uname                             # => [["Darwin"], ["Linux"]]

Example 5b – Accessing Multiple Machines in Parallel

By default, Rye::Set connects to each machine sequentially in the order they were added to the set. Commands can also be run in parallel.

rset = Rye::Set.new "SETNAME", :parallel => true
  OR
rset.parallel = true

Example 6 – File Transfers

rbox = Rye::Box.new "localhost"

rbox.file_upload "README.rdoc", "/tmp"

applejack = StringIO.new "Some in-memory content"
rbox.file_upload applejack, "/tmp/applejack.txt"

rbox.ls "/tmp/"                 # => [README.rdoc, applejack.txt]
rbox.cat "/tmp/applejack.txt"   # => "Some in-memory content"

filecontent = StringIO.new
rbox.file_download "/tmp/applejack.txt", filecontent

filecontent.read                # => "Some in-memory content"

Example 7 – Local processes

For local processes, you can bypass Rye::Box and execute commands directly with Rye.shell:

Rye.shell :uptime    # => 11:02  up 16:01, 3 users

The first argument must be the command name and the remaining arguments are sent directly as arguments to the command. They’re not escaped like with Rye::Box so you can use the asterisk, environment variables, pipes, and redirects etc. Also note that you can specify single character switches as symbols and you can separate arguments or put them into a single String.

Rye.shell :ls, '*'
Rye.shell :ls, '-l $HOME'
Rye.shell :ls, :l, '$HOME > $TMPDIR/crazy.txt'

The return value is a Rye::Rap object (just like with Rye::Box) so you have access to the exit code and STDERR output:

ret = Rye.shell :ls, 'nofile'
ret.exit_status      # => 1
ret.stderr           # => "sh: nofile: No such file or directory"
ret.class            # => Rye::Rap

Example 8a – Hopping Firewalls

When working with machines that are behind another host (assuming that you have ssh access to the firewall host):

   rhop = Rye::Hop.new('firewall.lan')
   rbox = Rye::Box.new('filibuster', :via => rhop)
   rbox.uptime     # => 20:53  up 1 day,  1:52, 4 users

Or

   rbox = Rye::Box.new('filibuster', :via => 'firewall.lan')

The information for the Rye::Box is then relative from the position of the firewall. So, the hostname ‘filibuster’ is used from ‘firewall.lan’

Example 8b – Hopping Firewalls, in groups

rset = Rye::Set.new "guarded_few", :parallel => true
rhop = Rye::Hop.new "firewall.lan"
rbox1 = Rye::Box.new "192.168.1.10", :via => rhop
rbox2 = Rye::Box.new "192.168.1.15", :via => rhop
rset.add_boxes rbox1, rbox2
rset.uptime
    # => [[17:17:44 up 548 days, 13:37, 20 users,  load average: 0.12, 0.07, 0.06], [01:17:49 up 6 days,  1:39,  9 users,  load average: 0.13, 0.09, 0.09]]

Example 9 – Disable password prompt

If you’re running in a terminal but you want Net::SSH::AuthenticationFailed to be raised instead of getting a password prompt when authentication fails, set :password_prompt option to false:

rbox = Rye::Box.new("foo.com", :user => "dan", :password => "inkorrect", :password_prompt => false)
rbox.uptime # => raises Net::SSH::AuthenticationFailed

About Safe-Mode

In safe-mode:

  • You can’t use file globs. This means you can’t do this: rbox.ls('*.rb'). ~ also doesn’t work!

  • You can’t use environment variables as arguments. This means you can’t do this: rbox.echo('$HOME'). However, environment variables are available to the commands you run.

  • Pipes and operators don’t work: |, &&, >, <, ||, ~, etc…

  • Backticks don’t work either: procs=`ps aux`

Why? In safe-mode, all command arguments are escaped which turns all arguments into their literal values.

Using a Ruby interface to execute shell commands is pretty awesome, particularly to run them on several machines simultaneously. That’s a lot of power and it’s potentially very dangerous. That’s why Rye disables this stuff by default. There’s probably a way to do it safely but it’s not obvious yet (to me). If you have any ideas, I’d love to hear them!

Command Whitelist

Rye permits only a limited number of system commands to be run. This default whitelist is defined in Rye::Cmd but you can add your own commands as you please (see Example 3).

Dependencies

  • OpenSSL (The C library)

  • Ruby Gems:

    • net-ssh

    • net-scp

    • highline

    • drydock

    • sysinfo

    • storable

Installation

Via Rubygems:

$ gem install rye

or via download:

However, in order to be sure the code you’re installing hasn’t been tampered with, it’s recommended that you verify the signiture. To do this, you need to add my public key as a trusted certificate (you only need to do this once):

# Add the public key as a trusted certificate
# (You only need to do this once)
$ curl -O https://raw.github.com/delano/rye/master/gem-public_cert.pem
$ gem cert --add gem-public_cert.pem

Then, when install the gem, do so with high security:

$ gem install rye -P HighSecurity

If you don’t add the public key, you’ll see an error like “Couldn’t verify data signature”. If you’re still having trouble let me know and I’ll give you a hand.

Contributing

The easiest way to contribute is to open an issue for bugs and feature requests. I’m also very open to thoughtful and interesting pull requests[github.com/delano/rye/pulls. Fork and be merry.

Thanks

More Info

Related Projects

Credits

  • Delano (@solutious.com)

  • Escape, Copyright © 2006,2007 Tanaka Akira <[email protected]>

  • Rye::Box#instance_exec (for Ruby 1.8) Mauricio Fernandez

License

See: LICENSE.txt

More Repositories

1

redis-dump

Backup and restore your Redis data to and from JSON.
Ruby
795
star
2

gibbler

Implementation-agnostic git-like hashes and history for Ruby objects
Ruby
146
star
3

storable

Marshal Ruby classes into and out of multiple formats (yaml, json, csv, tsv)
Ruby
31
star
4

scruffy

An unofficial fork of the Ruby graphing library with sexy defaults for hi-res charts. (NOT MAINTAINED)
Ruby
31
star
5

sysinfo

All your system-independent infoz in one handy (Ruby) class
Ruby
23
star
6

otto

Auto-define your rack-apps in plaintext.
Ruby
19
star
7

tryouts

Ruby tests that read like documentation.
Ruby
19
star
8

net-scp

Pure Ruby implementation of the SCP protocol
Ruby
15
star
9

caesars

Rapid and delicious DSL prototyping in Ruby (NOT MAINTAINED)
Ruby
14
star
10

benelux

A mad timeline for your Ruby codes.
Ruby
6
star
11

attic

A place for Ruby objects to hide instance variables
Ruby
5
star
12

delano.github.com

I'm ❤️ building healthy, high-performing teams.
HTML
4
star
13

bluth

A simple queuing system built on Redis (with worker and scheduler daemons!)
Ruby
4
star
14

saxony

Parse gigantic XML files with pleasure and without running out of memory. (NOT MAINTAINED)
Ruby
3
star
15

uri-redis

URI support for Redis connection settings
Ruby
3
star
16

jerkstore

A database alternative for mostly-read applications NOT MAINTAINED
Ruby
3
star
17

hexoid

Generate Ruby style object ids
Ruby
3
star
18

insults

An insulting Ruby library.
Ruby
2
star
19

familia

A little help with Redis.
Ruby
2
star
20

data-all

A Perl module that provides access to data in many formats from many places
Perl
2
star
21

mrbelvedere

Basic operational stats for web apps. IN PROGRESS
Ruby
2
star
22

delanotes

The Delano Entertainment System
Ruby
2
star
23

annoy

Like your annoying friend that asks you questions all the time.
Ruby
2
star