RAA - rye/0.3.1

rye / 0.3.1

Short description: Safely run remote commands via SSH in Ruby.
Category: Library/Net
Status: alpha
Created: 2009-04-05 02:50:24 GMT
Last update: 2009-04-05 02:50:24 GMT
Owner: delano (Projects of this owner)
Homepage: http://github.com/delano/rye
Download: http://github.com/delano/rye/tarball/rye-0.3.2
License: BSD-type
Dependency:
Requires: net-ssh/2.0.11(+)
Requires: highline/1.5.0(+)
(+): no such version
Description:

Rye - v0.3

Safely run remote commands via SSH in Ruby.

Rye is similar to Rush but everything happens over SSH (no HTTP daemon) and the default settings are less powerful (for safety). For example, file globs are disabled so unless otherwise specified, you can’t do this: rbox.rm(’-rf’, ’/etc/**/*’).

See bin/try for working examples.

Installation

One of:

    $ sudo gem install rye
    $ sudo gem install delano-rye --source http://gems.github.com/
    $ git clone git://github.com/delano/rye.git

EXAMPLE 1 — Basic Usage

    rbox = Rye::Box.new('localhost')

    # Commands are run as methods on the Rye::Box object
    puts rbox.uptime                # => 11:02  up 16:01, 3 users

    # The response value for all commands is a Rye::Rap object. The rap is a
    # subclass of Array so you can treat it as an Array, but it can also act
    # like a String if there's only one element.
    puts rbox.ls('rye.test')           # => ""
    puts rbox.ls('rye.test').stderr    # => ls: rye.test: No such file or directory

    puts rbox.touch('rye.test')        # => ""
    puts rbox.rm('rye.test')           # => ""

    # You can change directories
    puts rbox.pwd                      # => /home/rye
    puts rbox['/usr/bin'].pwd          # => /usr/bin
    puts rbox.pwd                      # => /usr/bin
    puts rbox.cd('/home/rye').pwd      # => /home/rye

    # You can specify environment variables
    rbox.add_env(:RYE, "Forty Creek")
    rbox.env                           # => ['HOME=/home/rye', 'RYE=Forty Creek', ...]

    # The commands method returns an Array of available commands:
    puts rbox.commands.join(', ')      # => pwd, touch, echo, wc, ...

    # When you're done you can disconnect explicitly.
    # (Although Rye does this automatically at exit.)
    rbox.disconnect

EXAMPLE 2 — Disabling Safe-Mode

    rbox_safe = Rye::Box.new('localhost')
    rbox_wild = Rye::Box.new('localhost', :safe => false)

    # Safe-mode is enabled by default. In safe-mode, all command
    # arguments are thoroughly escaped. This prevents access to
    # environment variables and file globs (among other things).
    p rbox_safe.echo('$HOME')                   # => "$HOME"
    p rbox_safe['/etc'].ls('host*')             # =>
    p rbox_safe.ls('-l | wc -l')                # =>
    p rbox_safe.echo('$HOME > /tmp/rye-home')   # => "$HOME > /tmp/home"
    p rbox_safe.cat('/tmp/rye-home')            # =>
    p rbox_safe.cat('/tmp/rye-home').stderr     # => "No such file or directory"

    # Here's the same commands with safe-mode disabled:
    p rbox_wild.echo('$HOME')                   # => "/home/rye"
    p rbox_wild['/etc'].ls('host*')             # => ["hostconfig", "hosts"]
    p rbox_wild.ls('-l | wc -l')                # => 110
    p rbox_wild.echo('$HOME > /tmp/rye-home')   # =>
    p rbox_wild.cat('/tmp/rye-home')            # => "/home/rye"
    p rbox_wild.rm('/tmp/rye-home')             # =>

EXAMPLE 3 — Custom Commands

    rbox = Rye::Box.new('localhost')
    rbox.add_keys('/private/key/path')   # Specify additional private keys

    # There's currently no rye900 command
    p rbox.commands.member?('rye9000')   # => false

    # But we can add our own commands to the Rye::Cmd class. They
    # automatically become available to all Rye::Box objects.
    module Rye::Cmd
      def rye9000(*args)
        add_command("ls", args)
      end
      def somescript(*args)
        add_command("/path/to/my/script", args)
      end
    end

    # We can now run rye9000 (with arguments)
    p rbox.rye9000('-a')                 # => [".", "..", ".bashrc", ...]
    p rbox.commands.member?('rye9000')   # => true

EXAMPLE 4 — Accessing Multiple Machines

    rset = Rye::Set.new
    rbox = Rye::Box.new

    rset.add_keys('/private/key/path')     # For passwordless logins
    rset.add_boxes(rbox, 'localhost')      # Add boxes as hostnames or objects

    # Calling methods on Rye::Set objects is very similar to calling them on
    # Rye::Box objects. In fact, it's identical:
    p rset.uptime        # => [[14:19:02 up 32 days, 19:35 ...], [14:19:02 up 30 days, 01:35]]
    p rset['/etc'].ls    # => [['file1', 'file2', ...], ['life1', 'life2', ...]]

    # Like Rye::Box, the response value is a Rye::Rap object containing the
    # responses from each box. Each response is itself a Rye::Rap object.
    unames = rset.uname
    p unames                               # => [["Darwin"], ["Darwin"]]
    puts unames.class                      # => Rye::Rap

    # The Rye::Rap object also keeps a reference to the object that called the
    # command. In this case, it will keep a reference to Rye::Set:
    puts unames.set.class                  # => Rye::Set
    puts unames.set == rset                # => true
    puts unames.size                       # => 2
    puts unames.first                      # => Darwin
    puts unames.first.class                # => Rye::Rap
    puts unames.first.box.class            # => Rye::Box
    puts unames.first.box == rbox          # => true

    # Envrionment variables can be set the same way as with Rye::Box
    rset.add_env(:RYE, "Forty Creek")
    p rset.env.first.select { |env| env =~ /RYE/ }  # => ["RYE=Forty Creek"]

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!
  • Command arguments cannot contain environment variables (however, environment variables are available to the commands you run). This means you can’t do this: rbox.echo(’$HOME’).
  • 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 2).

Credits

  • Delano Mandelbaum (delano@solutious.com)

Thanks

  • Solutious Incorporated (solutious.com) for all the orange juice.
  • The country of Canada for making Rye Whisky.

Kudos

More Info

Credits

  • Delano Mandelbaum (delano@solutious.com)

License

See: LICENSE.txt

Versions: [0.8.5 (2009-07-16)] [0.8.4 (2009-07-01)] [0.7.4 (2009-06-04)] [0.6.2 (2009-04-30)] [0.5.0 (2009-04-18)] [0.3.1 (2009-04-05)]

Edit this project (for project owner)

back to RAA top