tunneler 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +15 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +54 -0
- data/Rakefile +10 -0
- data/bin/tunneler +7 -0
- data/lib/tunneler.rb +26 -0
- data/lib/tunneler/cli.rb +123 -0
- data/lib/tunneler/config.rb +21 -0
- data/lib/tunneler/logger.rb +43 -0
- data/lib/tunneler/ssh.rb +85 -0
- data/lib/tunneler/ssh_tunnel.rb +103 -0
- data/lib/tunneler/version.rb +3 -0
- data/tunneler.gemspec +29 -0
- metadata +174 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Nels Broberg
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
Tunneler
|
2
|
+
=========
|
3
|
+
|
4
|
+
Tunneler is a Ruby command line interface for multi-hop SSH tunneling.
|
5
|
+
|
6
|
+
- SCP files through a bastion host
|
7
|
+
- Execute remote commands through a bastion host
|
8
|
+
- SSH through a bastion host using native terminal
|
9
|
+
|
10
|
+
Version
|
11
|
+
----
|
12
|
+
|
13
|
+
0.0.1
|
14
|
+
|
15
|
+
Installation
|
16
|
+
--------------
|
17
|
+
|
18
|
+
```sh
|
19
|
+
gem install tunneler
|
20
|
+
```
|
21
|
+
|
22
|
+
Example Command-Line Usage
|
23
|
+
--------------
|
24
|
+
|
25
|
+
```sh
|
26
|
+
tunneler --bastion-host 150.1.2.3 --destination-host 151.2.3.4 ssh
|
27
|
+
tunneler --bastion-host 150.1.2.3 --destination-host 151.2.3.4 scp local_file destination_file
|
28
|
+
tunneler --bastion-host 150.1.2.3 -d 151.2.3.4 execute 'whoami'
|
29
|
+
```
|
30
|
+
|
31
|
+
Example Gem Usage
|
32
|
+
--------------
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
require "tunneler"
|
36
|
+
|
37
|
+
# Create SSH tunnel
|
38
|
+
tunnel = SshTunnel.new(bastion_user, bastion_host, {:keys => [bastion_key]})
|
39
|
+
|
40
|
+
# Establish remote connection
|
41
|
+
destination_host_connection = tunnel.remote(destination_user, destination_host, {:keys => [destination_key]})
|
42
|
+
|
43
|
+
# Upload file to destination host via tunnel
|
44
|
+
destination_host_connection.scp(local_file_path, destination_file_path)
|
45
|
+
|
46
|
+
# Execute common on destination host via tunnel
|
47
|
+
response = destination_host_connection.ssh(command)
|
48
|
+
```
|
49
|
+
|
50
|
+
|
51
|
+
License
|
52
|
+
----
|
53
|
+
|
54
|
+
MIT
|
data/Rakefile
ADDED
data/bin/tunneler
ADDED
data/lib/tunneler.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require "net/scp"
|
2
|
+
require "net/ssh/gateway"
|
3
|
+
require "trollop"
|
4
|
+
require "singleton"
|
5
|
+
require "time"
|
6
|
+
|
7
|
+
module Tunneler
|
8
|
+
|
9
|
+
APPLICATION_ROOT = File.expand_path("..",File.dirname(__FILE__))
|
10
|
+
|
11
|
+
def self.debug
|
12
|
+
Config.instance.debug
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.debug=(boolean)
|
16
|
+
require "debugger" if boolean
|
17
|
+
Config.instance.debug = boolean
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.log(message, level=:info)
|
21
|
+
Logger.log(message, level)
|
22
|
+
end
|
23
|
+
|
24
|
+
Gem.find_files("tunneler/**/*.rb").each { |path| require path }
|
25
|
+
|
26
|
+
end
|
data/lib/tunneler/cli.rb
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
module Tunneler
|
2
|
+
class CommandLine
|
3
|
+
|
4
|
+
def run
|
5
|
+
setup_options
|
6
|
+
subcommand = process_sub_command
|
7
|
+
if @strict_host_checking
|
8
|
+
@strict_host_checking_options = ""
|
9
|
+
else
|
10
|
+
@strict_host_checking_options = "-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
|
11
|
+
end
|
12
|
+
|
13
|
+
Tunneler.debug = @debug
|
14
|
+
|
15
|
+
if subcommand == "ssh"
|
16
|
+
ssh
|
17
|
+
elsif subcommand == "scp"
|
18
|
+
local_file_path = ARGV.shift
|
19
|
+
destination_file_path = ARGV.shift
|
20
|
+
if local_file_path && destination_file_path
|
21
|
+
scp(local_file_path, destination_file_path)
|
22
|
+
end
|
23
|
+
elsif subcommand == "execute"
|
24
|
+
if command = ARGV.shift
|
25
|
+
execute(command)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def execute(command)
|
31
|
+
tunnel = SshTunnel.new(@bastion_user, @bastion_host, {:keys => [@bastion_key]})
|
32
|
+
destination_host_connection = tunnel.remote(@destination_user, @destination_host, {:keys => [@destination_key]})
|
33
|
+
Tunneler.log "Executing remote command '#{command}' on '#{@destination_host}' via '#{@bastion_host}'..."
|
34
|
+
Tunneler.log destination_host_connection.ssh(command)
|
35
|
+
end
|
36
|
+
|
37
|
+
def scp(local_file_path, destination_file_path)
|
38
|
+
tunnel = SshTunnel.new(@bastion_user, @bastion_host, {:keys => [@bastion_key]})
|
39
|
+
destination_host_connection = tunnel.remote(@destination_user, @destination_host, {:keys => [@destination_key]})
|
40
|
+
Tunneler.log "Uploading '#{local_file_path}' to '#{destination_file_path}' on '#{@destination_host}' via '#{@bastion_host}'..."
|
41
|
+
destination_host_connection.scp(local_file_path, destination_file_path)
|
42
|
+
end
|
43
|
+
|
44
|
+
def ssh
|
45
|
+
destination_key_filename = Pathname.new(@destination_key).basename
|
46
|
+
commands = [
|
47
|
+
"scp -i #{@bastion_key} #{@destination_key} #{@bastion_user}@#{@bastion_host}:",
|
48
|
+
"ssh -i #{@bastion_key} #{@strict_host_checking_options} -A -t -l #{@bastion_user} #{@bastion_host} -L #{DEFAULT_LOCAL_TUNNEL_PORT}:localhost:#{DEFAULT_LOCAL_TUNNEL_PORT} ssh #{@strict_host_checking_options} -A -t -i /home/#{@bastion_user}/#{destination_key_filename} -l #{@destination_user} #{@destination_host} -L #{DEFAULT_LOCAL_TUNNEL_PORT}:localhost:#{DEFAULT_LOCAL_TUNNEL_PORT}",
|
49
|
+
]
|
50
|
+
print "Opening ssh connection to '#{@destination_host}' via '#{@bastion_host}'"
|
51
|
+
open_terminal(commands.join(" ; "))
|
52
|
+
# remove the key as soon as authentication has occurred
|
53
|
+
# system "ssh -i #{@bastion_key} #{@strict_host_checking_options} -A -t -l #{@bastion_user} #{@bastion_host} -e 'rm /home/#{@bastion_user}/#{destination_private_key_filename}'"
|
54
|
+
end
|
55
|
+
|
56
|
+
def open_terminal(command)
|
57
|
+
os = RbConfig::CONFIG['host_os']
|
58
|
+
case os
|
59
|
+
when /darwin|mac os/
|
60
|
+
terminal = ENV['TERM_PROGRAM']
|
61
|
+
if terminal =~ /iTerm/
|
62
|
+
local_command = "osascript -e 'tell application \"System Events\" to keystroke \"t\" using command down' -e 'tell application \"iTerm\" to tell session -1 of current terminal to write text \"#{command}\"'"
|
63
|
+
elsif terminal =~ /Terminal/
|
64
|
+
local_command = "osascript -e 'tell application \"Terminal\" to activate' -e 'tell application \"System Events\" to tell process \"Terminal\" to keystroke \"t\" using command down' -e 'tell application \"Terminal\" to do script \"#{command}\" in selected tab of the front window'"
|
65
|
+
else
|
66
|
+
Tunneler.log "Terminal '#{terminal}' not supported", :cli
|
67
|
+
exit
|
68
|
+
end
|
69
|
+
when /linux/
|
70
|
+
local_command = "gnome-terminal -e '#{command}'"
|
71
|
+
else
|
72
|
+
Tunneler.log "SSH option not available in #{os.inspect}", :warn
|
73
|
+
return
|
74
|
+
end
|
75
|
+
system(local_command)
|
76
|
+
end
|
77
|
+
|
78
|
+
def setup_options
|
79
|
+
help = self.class.help + "\nParameters:"
|
80
|
+
sub_commands = self.class.sub_commands
|
81
|
+
options = Trollop::options(ARGV) do
|
82
|
+
banner help
|
83
|
+
opt :bastion_host, "Tunneler host", :type => :string, :required => true
|
84
|
+
opt :bastion_user, "Tunneler SSH user", :type => :string, :default => DEFAULT_SSH_USER
|
85
|
+
opt :bastion_key, "Tunneler SSH key path", :type => :string, :default => DEFAULT_SSH_KEY_PATH
|
86
|
+
opt :destination_host, "Destination host", :type => :string, :required => true
|
87
|
+
opt :destination_user, "Destination SSH user", :type => :string, :default => DEFAULT_SSH_USER
|
88
|
+
opt :destination_key, "Destination SSH key path", :type => :string, :default => DEFAULT_SSH_KEY_PATH
|
89
|
+
opt :strict_host_checking, "Enable SSH Strict Host Checking", :default => false
|
90
|
+
opt :debug, "Enable debug mode"
|
91
|
+
stop_on sub_commands
|
92
|
+
end
|
93
|
+
options.each { |name, value| instance_variable_set("@#{name}", value) }
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.help
|
97
|
+
"Tunneler CLI - ssh or scp through bastion host tunnel\n\n" +
|
98
|
+
"Available subcommands: #{self.sub_commands.join(', ')}\n\n" +
|
99
|
+
"Command syntax: tunneler <parameters> <subcommand>\n" +
|
100
|
+
"Examples: tunneler -b 120.1.2.3 -d 121.2.3.4 ssh\n" +
|
101
|
+
" tunneler -b 120.1.2.3 -d 121.2.3.4 scp local_file destination_file\n" +
|
102
|
+
" tunneler -b 120.1.2.3 -d 121.2.3.4 execute whoami\n"
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.sub_commands
|
106
|
+
["ssh", "scp", "execute"]
|
107
|
+
end
|
108
|
+
|
109
|
+
def process_sub_command
|
110
|
+
sub_command = ARGV.shift
|
111
|
+
unless self.class.sub_commands.include?(sub_command)
|
112
|
+
if sub_command
|
113
|
+
Tunneler.log "Invalid subcommand '#{sub_command}'", :cli
|
114
|
+
else
|
115
|
+
Tunneler.log "Please specify --help for command information", :cli
|
116
|
+
end
|
117
|
+
exit
|
118
|
+
end
|
119
|
+
sub_command
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Tunneler
|
2
|
+
|
3
|
+
DEFAULT_SSH_USER = ENV['USER']
|
4
|
+
|
5
|
+
DEFAULT_SSH_KEY_PATH = "#{ENV['HOME']}/.ssh/id_rsa"
|
6
|
+
|
7
|
+
DEFAULT_LOCAL_TUNNEL_PORT = 9997
|
8
|
+
|
9
|
+
class Config
|
10
|
+
|
11
|
+
include Singleton
|
12
|
+
|
13
|
+
attr_accessor :debug
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@debug = false
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Tunneler
|
2
|
+
class Logger
|
3
|
+
include Singleton
|
4
|
+
|
5
|
+
@@session = ""
|
6
|
+
|
7
|
+
LOG_FILE = "#{APPLICATION_ROOT}/log"
|
8
|
+
|
9
|
+
def self.create_log_file
|
10
|
+
unless File.exist?(LOG_FILE)
|
11
|
+
File.open(LOG_FILE, "w") {}
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.log(message, level)
|
16
|
+
message = message.to_s
|
17
|
+
self.log_to_file(message, level) unless level == :cli
|
18
|
+
self.log_to_session(message)
|
19
|
+
puts message unless level == :debug && !Tunneler.debug
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.log_to_file(message, level)
|
23
|
+
self.create_log_file
|
24
|
+
@@log ||= File.open(LOG_FILE, "a")
|
25
|
+
log_entry = [Time.now.utc.iso8601, level.upcase, message.strip.strip]
|
26
|
+
@@log.puts(log_entry.join("\t"))
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.log_to_session(message)
|
30
|
+
@@session << message + "\n"
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.session
|
34
|
+
@@session
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.truncate_session
|
38
|
+
session = @@session
|
39
|
+
@@session = ""
|
40
|
+
session
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/tunneler/ssh.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
module Tunneler
|
2
|
+
class Ssh
|
3
|
+
|
4
|
+
SSH_READY_TIMEOUT = 70
|
5
|
+
SSH_CONNECTIVITY_TIMEOUT = 15
|
6
|
+
ACCEPTABLE_CONNECTIVITY_ERRORS = [Errno::ECONNREFUSED]
|
7
|
+
|
8
|
+
SSH_COMMAND_TIMEOUT = 300
|
9
|
+
|
10
|
+
SCP_OPTIONS = { :recursive => true, :compression => "zlib" }
|
11
|
+
|
12
|
+
def initialize(user, host, options={})
|
13
|
+
@user = user
|
14
|
+
@host = host
|
15
|
+
@options = self.class.merge_options(options)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.default_options
|
19
|
+
{
|
20
|
+
:paranoid => false,
|
21
|
+
:keys => [DEFAULT_SSH_KEY_PATH],
|
22
|
+
:timeout => self::SSH_COMMAND_TIMEOUT,
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.merge_options(options={})
|
27
|
+
if options.empty?
|
28
|
+
Ssh.default_options
|
29
|
+
else
|
30
|
+
Ssh.default_options.merge(options)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def wait_for_ssh_access
|
35
|
+
Timer.timeout("#{@host}: SSH test",self.class::SSH_READY_TIMEOUT) do
|
36
|
+
until sshable? do
|
37
|
+
Tunneler.log "Waiting between connection attempts", :debug
|
38
|
+
sleep 5
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def sshable?
|
44
|
+
begin
|
45
|
+
return test_connectivity
|
46
|
+
rescue Exception => e
|
47
|
+
if ACCEPTABLE_CONNECTIVITY_ERRORS.include?(e.class)
|
48
|
+
Tunneler.log "Connectivity error: #{e.class} - #{e.message}", :debug
|
49
|
+
return false
|
50
|
+
else
|
51
|
+
raise e
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_connectivity
|
57
|
+
options_override = @options.clone
|
58
|
+
options_override[:timeout] = self.class::SSH_CONNECTIVITY_TIMEOUT
|
59
|
+
options_override[:verbose] = :debug if Tunneler.debug
|
60
|
+
if ssh("whoami", options_override).strip == @user
|
61
|
+
return true
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def ssh(command, options_override={})
|
66
|
+
if options_override.empty?
|
67
|
+
options = @options
|
68
|
+
else
|
69
|
+
options = options_override
|
70
|
+
end
|
71
|
+
output = ""
|
72
|
+
Net::SSH.start(@host, @user, @options) do |ssh|
|
73
|
+
output = ssh.exec!(command)
|
74
|
+
end
|
75
|
+
output
|
76
|
+
end
|
77
|
+
|
78
|
+
def scp(local_path, remote_path)
|
79
|
+
Net::SSH.start(@host, @user, @options) do |ssh|
|
80
|
+
ssh.scp.upload!(local_path, remote_path, SCP_OPTIONS)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Tunneler
|
2
|
+
class SshTunnel
|
3
|
+
|
4
|
+
attr_reader :options
|
5
|
+
|
6
|
+
def initialize(user, host, options={})
|
7
|
+
@user = user
|
8
|
+
@host = host
|
9
|
+
@options = Ssh.merge_options(options)
|
10
|
+
|
11
|
+
connect
|
12
|
+
end
|
13
|
+
|
14
|
+
def connect
|
15
|
+
@gateway = Gateway.connect(@host, @user, @options)
|
16
|
+
end
|
17
|
+
|
18
|
+
def remote(user, host, options={})
|
19
|
+
Remote.new(@gateway, user, host, Ssh.merge_options(options))
|
20
|
+
end
|
21
|
+
|
22
|
+
def terminate
|
23
|
+
@gateway.shutdown!
|
24
|
+
end
|
25
|
+
|
26
|
+
class Gateway < Net::SSH::Gateway
|
27
|
+
|
28
|
+
def initialize(host, user, options={})
|
29
|
+
super
|
30
|
+
@host = host
|
31
|
+
@user = user
|
32
|
+
@options = options
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.connect(host, user, options={})
|
36
|
+
gateway = self.new(host, user, options)
|
37
|
+
gateway.open("127.0.0.1", self.random_open_port)
|
38
|
+
gateway
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.random_open_port
|
42
|
+
socket = Socket.new(:INET, :STREAM, 0)
|
43
|
+
socket.bind(Addrinfo.tcp("127.0.0.1", 0))
|
44
|
+
socket.local_address.ip_port
|
45
|
+
end
|
46
|
+
|
47
|
+
def reconnect
|
48
|
+
shutdown!
|
49
|
+
self.class.connect(@host,@user,@options)
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
class Remote < Ssh
|
55
|
+
|
56
|
+
SSH_READY_TIMEOUT = 100
|
57
|
+
ACCEPTABLE_CONNECTIVITY_ERRORS = [Net::SSH::Disconnect, IOError]
|
58
|
+
|
59
|
+
def initialize(gateway, user, host, options={})
|
60
|
+
@gateway = gateway
|
61
|
+
super(user, host, options)
|
62
|
+
end
|
63
|
+
|
64
|
+
def sshable?
|
65
|
+
begin
|
66
|
+
gateway_reconnect
|
67
|
+
return test_connectivity
|
68
|
+
rescue Exception => e
|
69
|
+
if ACCEPTABLE_CONNECTIVITY_ERRORS.include?(e.class)
|
70
|
+
Tunneler.log "Connectivity error: #{e.class} - #{e.message}", :debug
|
71
|
+
return false
|
72
|
+
else
|
73
|
+
raise e
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def gateway_reconnect
|
79
|
+
@gateway = @gateway.reconnect
|
80
|
+
end
|
81
|
+
|
82
|
+
def ssh(command, options_override={})
|
83
|
+
if options_override.empty?
|
84
|
+
options = @options
|
85
|
+
else
|
86
|
+
options = options_override
|
87
|
+
end
|
88
|
+
output = ""
|
89
|
+
@gateway.ssh(@host, @user, @options) do |ssh|
|
90
|
+
output = ssh.exec!(command)
|
91
|
+
end
|
92
|
+
output
|
93
|
+
end
|
94
|
+
|
95
|
+
def scp(local_path, remote_path)
|
96
|
+
@gateway.ssh(@host, @user, @options) do |ssh|
|
97
|
+
ssh.scp.upload!(local_path, remote_path, SCP_OPTIONS)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
data/tunneler.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'tunneler/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "tunneler"
|
8
|
+
spec.version = Tunneler::VERSION
|
9
|
+
spec.authors = ["Nels Broberg"]
|
10
|
+
spec.email = ["zifridorio@hotmail.com"]
|
11
|
+
spec.description = %q{Command line tool and ruby gem for SSH tunneling}
|
12
|
+
spec.summary = %q{SSH to server via tunneler host}
|
13
|
+
spec.homepage = "https://github.com/nbroberg/tunneler"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "net-scp"
|
22
|
+
spec.add_dependency "net-ssh-gateway"
|
23
|
+
spec.add_dependency "trollop"
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
26
|
+
spec.add_development_dependency "rake"
|
27
|
+
spec.add_development_dependency "rspec"
|
28
|
+
spec.add_development_dependency "debugger"
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,174 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tunneler
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Nels Broberg
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-10-10 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: net-scp
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: net-ssh-gateway
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: trollop
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: bundler
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '1.3'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '1.3'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rake
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: rspec
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: debugger
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
description: Command line tool and ruby gem for SSH tunneling
|
127
|
+
email:
|
128
|
+
- zifridorio@hotmail.com
|
129
|
+
executables:
|
130
|
+
- tunneler
|
131
|
+
extensions: []
|
132
|
+
extra_rdoc_files: []
|
133
|
+
files:
|
134
|
+
- .gitignore
|
135
|
+
- Gemfile
|
136
|
+
- Gemfile.lock
|
137
|
+
- LICENSE
|
138
|
+
- README.md
|
139
|
+
- Rakefile
|
140
|
+
- bin/tunneler
|
141
|
+
- lib/tunneler.rb
|
142
|
+
- lib/tunneler/cli.rb
|
143
|
+
- lib/tunneler/config.rb
|
144
|
+
- lib/tunneler/logger.rb
|
145
|
+
- lib/tunneler/ssh.rb
|
146
|
+
- lib/tunneler/ssh_tunnel.rb
|
147
|
+
- lib/tunneler/version.rb
|
148
|
+
- tunneler.gemspec
|
149
|
+
homepage: https://github.com/nbroberg/tunneler
|
150
|
+
licenses:
|
151
|
+
- MIT
|
152
|
+
post_install_message:
|
153
|
+
rdoc_options: []
|
154
|
+
require_paths:
|
155
|
+
- lib
|
156
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
157
|
+
none: false
|
158
|
+
requirements:
|
159
|
+
- - ! '>='
|
160
|
+
- !ruby/object:Gem::Version
|
161
|
+
version: '0'
|
162
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
163
|
+
none: false
|
164
|
+
requirements:
|
165
|
+
- - ! '>='
|
166
|
+
- !ruby/object:Gem::Version
|
167
|
+
version: '0'
|
168
|
+
requirements: []
|
169
|
+
rubyforge_project:
|
170
|
+
rubygems_version: 1.8.23
|
171
|
+
signing_key:
|
172
|
+
specification_version: 3
|
173
|
+
summary: SSH to server via tunneler host
|
174
|
+
test_files: []
|