tbm 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +5 -0
- data/Rakefile +42 -0
- data/UNLICENSE +24 -0
- data/bin/tbm +5 -0
- data/lib/tbm.rb +4 -0
- data/lib/tunnel/cli.rb +69 -0
- data/lib/tunnel/config.rb +93 -0
- data/lib/tunnel/meta.rb +5 -0
- data/lib/tunnel/target.rb +24 -0
- metadata +83 -0
data/README.md
ADDED
@@ -0,0 +1,5 @@
|
|
1
|
+
# Tunnels
|
2
|
+
|
3
|
+
A simple Ruby script to manage named SSH tunnels because I need these regularly and it's a bit of a pain to manage this in some other way.
|
4
|
+
|
5
|
+
At the moment, this will be a command that you invoke, and leave running. At some point, it might make sense to make this two processes -- one that stays alive while there are active tunnels, and a command-line that interacts with that process. For now, that's more work than I intend to bite off -- more of a v2 feature.
|
data/Rakefile
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'rake/clean'
|
2
|
+
require 'rake/packagetask'
|
3
|
+
require 'rspec/core/rake_task'
|
4
|
+
require 'rspec'
|
5
|
+
require 'rubygems'
|
6
|
+
require 'rubygems/package_task'
|
7
|
+
require 'tbm'
|
8
|
+
|
9
|
+
CLEAN.include( 'coverage', 'pkg' )
|
10
|
+
|
11
|
+
desc '"spec" (run RSpec)'
|
12
|
+
task :default => :spec
|
13
|
+
|
14
|
+
desc "Run RSpec on spec/*"
|
15
|
+
RSpec::Core::RakeTask.new
|
16
|
+
|
17
|
+
desc "Generate code coverage"
|
18
|
+
RSpec::Core::RakeTask.new(:coverage) do |t|
|
19
|
+
t.rcov = true
|
20
|
+
t.rcov_opts = ['--exclude', 'spec,/gems/,/rubygems/', '--text-report']
|
21
|
+
end
|
22
|
+
|
23
|
+
spec = Gem::Specification.new do |spec|
|
24
|
+
spec.name = 'tbm'
|
25
|
+
spec.version = Tunnel::VERSION
|
26
|
+
spec.date = Tunnel::DATE
|
27
|
+
spec.summary = 'Named SSH tunnels, like bookmarks.'
|
28
|
+
spec.description = 'The "Tunnel Boring Machine" is meant to bore ssh tunnels through the internet to your desired destination simply and repeatedly, as often as you need them. This is a tool for someone who needs SSH tunnels frequently.'
|
29
|
+
spec.author = 'Geoffrey Wiseman'
|
30
|
+
spec.email = 'geoffrey.wiseman@codiform.com'
|
31
|
+
spec.homepage = 'http://github.com/geoffreywiseman/tunnel-boring-machine'
|
32
|
+
spec.executables << 'tbm'
|
33
|
+
|
34
|
+
spec.files = Dir['{lib,spec}/**/*.rb', 'bin/*', 'Rakefile', 'README.md', 'UNLICENSE']
|
35
|
+
|
36
|
+
spec.add_dependency( 'net/ssh', '>= 2.6.2' )
|
37
|
+
end
|
38
|
+
|
39
|
+
Gem::PackageTask.new( spec ) do |pkg|
|
40
|
+
pkg.need_tar_gz = true
|
41
|
+
pkg.need_zip = true
|
42
|
+
end
|
data/UNLICENSE
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
This is free and unencumbered software released into the public domain.
|
2
|
+
|
3
|
+
Anyone is free to copy, modify, publish, use, compile, sell, or
|
4
|
+
distribute this software, either in source code form or as a compiled
|
5
|
+
binary, for any purpose, commercial or non-commercial, and by any
|
6
|
+
means.
|
7
|
+
|
8
|
+
In jurisdictions that recognize copyright laws, the author or authors
|
9
|
+
of this software dedicate any and all copyright interest in the
|
10
|
+
software to the public domain. We make this dedication for the benefit
|
11
|
+
of the public at large and to the detriment of our heirs and
|
12
|
+
successors. We intend this dedication to be an overt act of
|
13
|
+
relinquishment in perpetuity of all present and future rights to this
|
14
|
+
software under copyright law.
|
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 NONINFRINGEMENT.
|
19
|
+
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
20
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
21
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
23
|
+
|
24
|
+
For more information, please refer to <http://unlicense.org/>
|
data/bin/tbm
ADDED
data/lib/tbm.rb
ADDED
data/lib/tunnel/cli.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'net/ssh'
|
2
|
+
|
3
|
+
module Tunnel
|
4
|
+
class CommandLineInterface
|
5
|
+
def initialize( config )
|
6
|
+
@config = config
|
7
|
+
@target = nil
|
8
|
+
@cancelled = false
|
9
|
+
end
|
10
|
+
|
11
|
+
def parse
|
12
|
+
if ARGV.size != 1
|
13
|
+
print_targets "SYNTAX: tbm <target>\n\nWhere target is one of:"
|
14
|
+
else
|
15
|
+
target_name = ARGV[0]
|
16
|
+
@target = @config.get_target( target_name )
|
17
|
+
print_targets( "Cannot find target: #{target_name}\n\nThese are the targets currently defined:" ) if @target.nil?
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def print_targets( message )
|
22
|
+
puts message
|
23
|
+
@config.each_target { |target| puts "\t#{target.name}" }
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
def valid?
|
28
|
+
!@target.nil?
|
29
|
+
end
|
30
|
+
|
31
|
+
def bore
|
32
|
+
puts "Starting #{APP_NAME} v#{VERSION}"
|
33
|
+
puts
|
34
|
+
|
35
|
+
trap("INT") { @cancelled = true }
|
36
|
+
Net::SSH.start( @target.host, @target.username ) do |session|
|
37
|
+
forward_ports( session )
|
38
|
+
end
|
39
|
+
|
40
|
+
puts "Shutting down the machine."
|
41
|
+
end
|
42
|
+
|
43
|
+
def forward_ports( session )
|
44
|
+
begin
|
45
|
+
puts "Opened connection to #{@target.username}@#{@target.host}:"
|
46
|
+
@target.each_forward do |fwd|
|
47
|
+
remote_server, port = fwd
|
48
|
+
session.forward.local( port, remote_server, port )
|
49
|
+
puts "\tforwarded port #{port} to #{remote_server}:#{port}"
|
50
|
+
end
|
51
|
+
puts "\twaiting for Ctrl-C..."
|
52
|
+
session.loop(0.1) { not @cancelled }
|
53
|
+
puts "\n\tCtrl-C pressed. Exiting."
|
54
|
+
rescue Errno::EACCES
|
55
|
+
@cancelled = true
|
56
|
+
puts "\tCould not open all ports; you may need to sudo if port < 1000."
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.parse_and_run
|
61
|
+
config = Config.new
|
62
|
+
if config.valid?
|
63
|
+
cli = CommandLineInterface.new( config )
|
64
|
+
cli.parse
|
65
|
+
cli.bore if cli.valid?
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'etc'
|
2
|
+
|
3
|
+
module Tunnel
|
4
|
+
class Config
|
5
|
+
CONFIG_FILE = File.expand_path( '~/.tunnels' )
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@valid = nil
|
9
|
+
@targets = []
|
10
|
+
if File.file? CONFIG_FILE
|
11
|
+
config_data = YAML.load_file( CONFIG_FILE )
|
12
|
+
parse config_data if config_data.is_a? Hash
|
13
|
+
else
|
14
|
+
puts "Configure your tunnels in ~/.tunnels in YAML form."
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def valid?
|
19
|
+
@valid
|
20
|
+
end
|
21
|
+
|
22
|
+
def get_target( name )
|
23
|
+
@targets.find { |target| target.has_name?(name) }
|
24
|
+
end
|
25
|
+
|
26
|
+
def each_target( &block )
|
27
|
+
@targets.each { |target| yield target }
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def parse( targets )
|
33
|
+
targets.each_key { |name| parse_target name, targets[name] }
|
34
|
+
@valid = true if @valid.nil? && !@targets.empty?
|
35
|
+
end
|
36
|
+
|
37
|
+
def parse_target( name, config )
|
38
|
+
if config.is_a? Hash
|
39
|
+
target = create_target( name, config )
|
40
|
+
unless target.nil?
|
41
|
+
@targets << target
|
42
|
+
parse_forward( target, config['forward'] )
|
43
|
+
end
|
44
|
+
else
|
45
|
+
puts "Cannot parse target '#{name}' (#{config.class})"
|
46
|
+
@valid = false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def create_target( name, config )
|
51
|
+
if config.has_key? 'host' then
|
52
|
+
if config.has_key? 'username' then
|
53
|
+
username = config['username']
|
54
|
+
else
|
55
|
+
username = Etc.getlogin
|
56
|
+
end
|
57
|
+
Target.new name, config['host'], username
|
58
|
+
else
|
59
|
+
puts "Cannot parse target '#{name}': no host found."
|
60
|
+
@valid = false
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def parse_forward( target, config )
|
65
|
+
case config
|
66
|
+
when nil
|
67
|
+
puts "Target #{target.name} has no forwards defined."
|
68
|
+
@valid = false
|
69
|
+
when Fixnum
|
70
|
+
target.forward_port( config )
|
71
|
+
when Array
|
72
|
+
config.each { |port| target.forward_port(port) }
|
73
|
+
when Hash
|
74
|
+
config.each_key do |server|
|
75
|
+
server_config = config[ server ]
|
76
|
+
case server_config
|
77
|
+
when Fixnum
|
78
|
+
target.forward_port( server_config, server )
|
79
|
+
when Array
|
80
|
+
server_config.each { |port| target.forward_port( port, server ) }
|
81
|
+
else
|
82
|
+
puts "Not sure how to handle forward from #{target.host} to #{server}: #{server_config.class}"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
else
|
86
|
+
puts "Not sure how to handle forward for '#{target.host}': #{config.class}"
|
87
|
+
@valid = false
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
data/lib/tunnel/meta.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
module Tunnel
|
2
|
+
class Target
|
3
|
+
attr_reader :name, :host, :username
|
4
|
+
|
5
|
+
def initialize( name, host, username )
|
6
|
+
@name = name
|
7
|
+
@host = host
|
8
|
+
@username = username
|
9
|
+
@forwards = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def forward_port( port, server='localhost' )
|
13
|
+
@forwards << [ server, port ]
|
14
|
+
end
|
15
|
+
|
16
|
+
def has_name?( name )
|
17
|
+
@name==name
|
18
|
+
end
|
19
|
+
|
20
|
+
def each_forward( &block )
|
21
|
+
@forwards.each { |fwd| yield fwd }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tbm
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Geoffrey Wiseman
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2012-11-23 00:00:00 -05:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
type: :runtime
|
22
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
segments:
|
27
|
+
- 2
|
28
|
+
- 6
|
29
|
+
- 2
|
30
|
+
version: 2.6.2
|
31
|
+
name: net/ssh
|
32
|
+
requirement: *id001
|
33
|
+
prerelease: false
|
34
|
+
description: The "Tunnel Boring Machine" is meant to bore ssh tunnels through the internet to your desired destination simply and repeatedly, as often as you need them. This is a tool for someone who needs SSH tunnels frequently.
|
35
|
+
email: geoffrey.wiseman@codiform.com
|
36
|
+
executables:
|
37
|
+
- tbm
|
38
|
+
extensions: []
|
39
|
+
|
40
|
+
extra_rdoc_files: []
|
41
|
+
|
42
|
+
files:
|
43
|
+
- lib/tbm.rb
|
44
|
+
- lib/tunnel/cli.rb
|
45
|
+
- lib/tunnel/config.rb
|
46
|
+
- lib/tunnel/meta.rb
|
47
|
+
- lib/tunnel/target.rb
|
48
|
+
- bin/tbm
|
49
|
+
- Rakefile
|
50
|
+
- README.md
|
51
|
+
- UNLICENSE
|
52
|
+
has_rdoc: true
|
53
|
+
homepage: http://github.com/geoffreywiseman/tunnel-boring-machine
|
54
|
+
licenses: []
|
55
|
+
|
56
|
+
post_install_message:
|
57
|
+
rdoc_options: []
|
58
|
+
|
59
|
+
require_paths:
|
60
|
+
- lib
|
61
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
segments:
|
66
|
+
- 0
|
67
|
+
version: "0"
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
segments:
|
73
|
+
- 0
|
74
|
+
version: "0"
|
75
|
+
requirements: []
|
76
|
+
|
77
|
+
rubyforge_project:
|
78
|
+
rubygems_version: 1.3.6
|
79
|
+
signing_key:
|
80
|
+
specification_version: 3
|
81
|
+
summary: Named SSH tunnels, like bookmarks.
|
82
|
+
test_files: []
|
83
|
+
|