tunl 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/bin/tunl +91 -0
- data/lib/tunl.rb +204 -0
- metadata +63 -0
data/bin/tunl
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require File.dirname(__FILE__) + '/../lib/tunl'
|
4
|
+
require 'thor'
|
5
|
+
|
6
|
+
module Tunl
|
7
|
+
|
8
|
+
class CLI < Thor
|
9
|
+
|
10
|
+
desc "start <name>", "launch named tunnel"
|
11
|
+
method_options :check => :string, :port => :string, :verbose => :boolean
|
12
|
+
def start(name)
|
13
|
+
printf("\n%-3s %-8s %-20s\n\n", '~~>', "Start", "Tunnel: #{name}")
|
14
|
+
tunl = Tunl::Base.new
|
15
|
+
tunl.setup_security_group(name)
|
16
|
+
tunl.setup_key_pair(name)
|
17
|
+
tunl.setup_instance(name)
|
18
|
+
|
19
|
+
instance = tunl.find_instance(name)
|
20
|
+
port = options[:port] || 3000
|
21
|
+
if options[:verbose]
|
22
|
+
verbose = "-v"
|
23
|
+
else
|
24
|
+
verbose = ''
|
25
|
+
end
|
26
|
+
|
27
|
+
if uri = options[:check]
|
28
|
+
tunl.log("check", "#{instance[:dns_name]}#{uri}")
|
29
|
+
t = Thread.new do
|
30
|
+
loop do
|
31
|
+
`curl http://#{instance[:dns_name]}#{uri} > /dev/null 2>&1`
|
32
|
+
puts "."
|
33
|
+
sleep 60
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
tunl.log("tunnel", "#{instance[:dns_name]}:80 -> localhost:#{port}")
|
38
|
+
puts "\n\n"
|
39
|
+
`ssh -i #{tunl.key_pair_filepath(name)} #{verbose} -N -p 22 root@#{instance[:dns_name]} -R *:80:localhost:#{port}`
|
40
|
+
end
|
41
|
+
|
42
|
+
desc "ssh <name>", "ssh into the named instance"
|
43
|
+
def ssh(name)
|
44
|
+
tunl = Tunl::Base.new
|
45
|
+
instance = tunl.find_instance(name)
|
46
|
+
if instance.nil?
|
47
|
+
puts "Tunl #{name} does not exist"
|
48
|
+
exit(0)
|
49
|
+
end
|
50
|
+
exec "ssh -i #{tunl.key_pair_filepath(name)} root@#{instance[:dns_name]}"
|
51
|
+
end
|
52
|
+
|
53
|
+
desc "stop <name>", "terminate the named tunnel"
|
54
|
+
def stop(name)
|
55
|
+
tunl = Tunl::Base.new
|
56
|
+
if instance = tunl.find_instance(name)
|
57
|
+
printf("\n%-3s %-8s %-20s\n\n", '~~>', "Stop", "Tunnel: #{name}")
|
58
|
+
tunl.delete_instance(name)
|
59
|
+
tunl.delete_security_group(name)
|
60
|
+
tunl.delete_key_pair(name)
|
61
|
+
puts ""
|
62
|
+
else
|
63
|
+
puts "tunl #{name} not running"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
desc "list", "list all the tunnels"
|
68
|
+
def list
|
69
|
+
tunl = Tunl::Base.new
|
70
|
+
instances = tunl.find_all_instances
|
71
|
+
if instances.size==0
|
72
|
+
puts "no running tunnels"
|
73
|
+
else
|
74
|
+
$stdout.printf("\n%-3s %-12s\n\n", '', 'Tunnels')
|
75
|
+
$stdout.printf("%-3s %-12s %-12s %-12s\n\n", '', 'Name', 'Aws Id', 'Host')
|
76
|
+
instances.each do |item|
|
77
|
+
$stdout.printf("%-3s %-12s %-12s %-12s\n", '', item[:aws_groups].first.gsub('tunl_', ''), item[:aws_instance_id], item[:dns_name])
|
78
|
+
end
|
79
|
+
$stdout.puts("\n\n")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
no_tasks do
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
Tunl::CLI.start
|
data/lib/tunl.rb
ADDED
@@ -0,0 +1,204 @@
|
|
1
|
+
|
2
|
+
# ruby requirements
|
3
|
+
require 'pathname'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
|
7
|
+
require 'net/http'
|
8
|
+
# hack to disable SSL warnings issued by the right_http_connection gem
|
9
|
+
class Net::HTTP
|
10
|
+
alias_method :old_initialize, :initialize
|
11
|
+
def initialize(*args)
|
12
|
+
old_initialize(*args)
|
13
|
+
@ssl_context = OpenSSL::SSL::SSLContext.new
|
14
|
+
@ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
# gem requirements
|
20
|
+
require 'right_aws'
|
21
|
+
|
22
|
+
# hack to supress annoying log messages from right_aws gem
|
23
|
+
module Tunl
|
24
|
+
class SilentLogger
|
25
|
+
def self.info(msg)
|
26
|
+
end
|
27
|
+
def self.error(msg)
|
28
|
+
puts "RIGHT AWS ERROR : #{msg}"
|
29
|
+
end
|
30
|
+
def self.warn(msg)
|
31
|
+
puts "RIGHT AWS WARN: #{msg}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module Tunl
|
37
|
+
class Base
|
38
|
+
|
39
|
+
def root
|
40
|
+
@root ||= Pathname.new(File.expand_path("~/.tunl"))
|
41
|
+
end
|
42
|
+
|
43
|
+
def config
|
44
|
+
@config ||= YAML.load(File.read(root.join('tunl.yml')))
|
45
|
+
end
|
46
|
+
|
47
|
+
def ec2
|
48
|
+
@ec2 ||= RightAws::Ec2.new(config['aws_access_key'],config['aws_secret_key'], :logger => SilentLogger)
|
49
|
+
end
|
50
|
+
|
51
|
+
def key_pair_filepath(name)
|
52
|
+
root.join("tunl_key_pair_#{name}")
|
53
|
+
end
|
54
|
+
|
55
|
+
def create_key_pair(name)
|
56
|
+
log "create", "key pair tunl_#{name}"
|
57
|
+
filepath = key_pair_filepath(name)
|
58
|
+
key_pair = ec2.create_key_pair("tunl_#{name}")
|
59
|
+
File.open(filepath, 'w') { |f| f.write(key_pair[:aws_material]) }
|
60
|
+
FileUtils.chmod 0400, filepath
|
61
|
+
rescue RightAws::AwsError => e
|
62
|
+
if e.message =~ /already exists/
|
63
|
+
# WARNING : this could be dangerous.
|
64
|
+
delete_key_pair(name)
|
65
|
+
retry
|
66
|
+
else
|
67
|
+
raise e
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def delete_key_pair(name)
|
72
|
+
log "delete", "key Pair tunl_#{name}"
|
73
|
+
# delete key pair from amazon
|
74
|
+
ec2.delete_key_pair("tunl_#{name}")
|
75
|
+
# delete local file
|
76
|
+
filepath = key_pair_filepath(name)
|
77
|
+
if File.exists?(filepath)
|
78
|
+
FileUtils.rm_f(filepath)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def setup_key_pair(name)
|
83
|
+
if ! File.exists?(key_pair_filepath(name))
|
84
|
+
create_key_pair(name)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def create_security_group(name)
|
89
|
+
log "create", "security group tunl_#{name}"
|
90
|
+
ec2.create_security_group("tunl_#{name}", 'created by tunl gem')
|
91
|
+
rescue RightAws::AwsError => e
|
92
|
+
if e.message =~ /already exists/
|
93
|
+
return true
|
94
|
+
else
|
95
|
+
raise e
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def authorize_security_group(name)
|
100
|
+
[ 22, 80 ].each do |port|
|
101
|
+
begin
|
102
|
+
ec2.authorize_security_group_IP_ingress("tunl_#{name}", port, port)
|
103
|
+
rescue RightAws::AwsError => e
|
104
|
+
if e.message =~ /already been authorized/
|
105
|
+
# do nothing
|
106
|
+
else
|
107
|
+
raise e
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def delete_security_group(name)
|
114
|
+
log "delete", "security group tunl_#{name}"
|
115
|
+
ec2.delete_security_group("tunl_#{name}")
|
116
|
+
end
|
117
|
+
|
118
|
+
def find_security_group(name)
|
119
|
+
ec2.describe_security_groups.find { |item| item[:aws_group_name]=="tunl_#{name}" }
|
120
|
+
end
|
121
|
+
|
122
|
+
def setup_security_group(name)
|
123
|
+
# returns the names of all existing security groups prefixed with tunl_
|
124
|
+
group = find_security_group(name)
|
125
|
+
if group.nil?
|
126
|
+
create_security_group(name)
|
127
|
+
authorize_security_group(name)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def find_instance(name)
|
132
|
+
ec2.describe_instances.find { |item| item[:aws_groups].include?("tunl_#{name}") and (item[:aws_state].include?("pending") or item[:aws_state].include?("running")) }
|
133
|
+
end
|
134
|
+
|
135
|
+
def create_instance(name)
|
136
|
+
# launch instance with:
|
137
|
+
# os - ubuntu hardy 32 bit
|
138
|
+
# security_group: tunl_name
|
139
|
+
# key_pair: tunl_name
|
140
|
+
log "launch", "instance ..."
|
141
|
+
instances = ec2.launch_instances('ami-ed46a784',
|
142
|
+
:key_name => "tunl_#{name}",
|
143
|
+
:group_ids => "tunl_#{name}",
|
144
|
+
:instance_type => 'm1.small',
|
145
|
+
:availability_zone => 'us-east-1a',
|
146
|
+
:user_data => %Q{#!/bin/sh
|
147
|
+
echo "starting" > /tmp/user_data_status.txt
|
148
|
+
echo "GatewayPorts clientspecified" >> /etc/ssh/sshd_config
|
149
|
+
/etc/init.d/ssh restart
|
150
|
+
echo "finished" > /tmp/user_data_status.txt
|
151
|
+
})
|
152
|
+
log "", "#{instances.first[:aws_instance_id]}"
|
153
|
+
end
|
154
|
+
|
155
|
+
def wait_for_instance(name)
|
156
|
+
log "", "waiting for dns ..."
|
157
|
+
while true
|
158
|
+
break if ( find_instance(name) and find_instance(name)[:dns_name].size>0 )
|
159
|
+
sleep(2)
|
160
|
+
end
|
161
|
+
log "", "done: #{find_instance(name)[:dns_name]}"
|
162
|
+
require 'socket'
|
163
|
+
dns_name = find_instance(name)[:dns_name]
|
164
|
+
log "", "waiting for ssh"
|
165
|
+
loop do
|
166
|
+
begin
|
167
|
+
Timeout::timeout(4) do
|
168
|
+
TCPSocket.new(dns_name, 22)
|
169
|
+
log "", "done!"
|
170
|
+
log ""
|
171
|
+
return
|
172
|
+
end
|
173
|
+
rescue SocketError, Timeout::Error, Errno::ECONNREFUSED, Errno::EHOSTUNREACH
|
174
|
+
sleep(2)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def delete_instance(name)
|
180
|
+
instance = find_instance(name)
|
181
|
+
log "delete", "instance tunl_#{name} with id #{instance[:aws_instance_id]}"
|
182
|
+
ec2.terminate_instances(instance[:aws_instance_id])
|
183
|
+
end
|
184
|
+
|
185
|
+
def setup_instance(name)
|
186
|
+
instance = find_instance(name)
|
187
|
+
if instance.nil?
|
188
|
+
create_instance(name)
|
189
|
+
else
|
190
|
+
log "connect", "instance #{instance[:aws_instance_id]}"
|
191
|
+
end
|
192
|
+
wait_for_instance(name)
|
193
|
+
end
|
194
|
+
|
195
|
+
def find_all_instances
|
196
|
+
ec2.describe_instances.find_all { |item| item[:aws_groups].first.include?('tunl_') and ['running', 'pending'].include?(item[:aws_state]) }
|
197
|
+
end
|
198
|
+
|
199
|
+
def log(action,message='')
|
200
|
+
$stdout.printf("%-3s %-8s %-12s\n", '', action, message)
|
201
|
+
end
|
202
|
+
|
203
|
+
end
|
204
|
+
end
|
metadata
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tunl
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- David Crockett
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2009-09-30 00:00:00 -06:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: EC2 Proxy Service
|
22
|
+
email: davy@davcro.com
|
23
|
+
executables:
|
24
|
+
- tunl
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files: []
|
28
|
+
|
29
|
+
files:
|
30
|
+
- lib/tunl.rb
|
31
|
+
- bin/tunl
|
32
|
+
has_rdoc: true
|
33
|
+
homepage:
|
34
|
+
licenses: []
|
35
|
+
|
36
|
+
post_install_message:
|
37
|
+
rdoc_options: []
|
38
|
+
|
39
|
+
require_paths:
|
40
|
+
- lib
|
41
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
segments:
|
46
|
+
- 0
|
47
|
+
version: "0"
|
48
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
segments:
|
53
|
+
- 0
|
54
|
+
version: "0"
|
55
|
+
requirements: []
|
56
|
+
|
57
|
+
rubyforge_project:
|
58
|
+
rubygems_version: 1.3.6
|
59
|
+
signing_key:
|
60
|
+
specification_version: 3
|
61
|
+
summary: EC2 Proxy Service
|
62
|
+
test_files: []
|
63
|
+
|