fenris 0.0.6 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/fenris +5 -5
- data/bin/xx +156 -0
- data/lib/fenris/client.rb +207 -100
- data/lib/fenris/command.rb +169 -78
- data/lib/fenris/command.rb.old +90 -0
- data/lib/fenris/connection.rb +124 -0
- data/lib/fenris/version.rb +1 -1
- metadata +12 -10
- data/lib/fenris/em.rb +0 -164
data/bin/fenris
CHANGED
@@ -3,14 +3,14 @@
|
|
3
3
|
lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
|
4
4
|
$LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
|
5
5
|
|
6
|
-
require 'fenris/em'
|
7
6
|
require 'fenris/command'
|
8
7
|
require 'fenris/client'
|
8
|
+
require 'fenris/connection'
|
9
9
|
|
10
|
-
args = ARGV.dup
|
11
|
-
ARGV.clear
|
12
|
-
command = args.shift.strip rescue 'help'
|
10
|
+
#args = ARGV.dup
|
11
|
+
#ARGV.clear
|
12
|
+
#command = args.shift.strip rescue 'help'
|
13
13
|
#arg = args.shift.strip rescue nil
|
14
14
|
|
15
|
-
Fenris::Command.run
|
15
|
+
Fenris::Command.run ARGV
|
16
16
|
|
data/bin/xx
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'socket'
|
5
|
+
require 'ostruct'
|
6
|
+
require 'uri'
|
7
|
+
|
8
|
+
module Fenris
|
9
|
+
class Command
|
10
|
+
def self.run(args)
|
11
|
+
url = URI.parse(ENV['FENRIS_URL'] || 'https://broker.fenris.cc/')
|
12
|
+
args, options, help = parse(args)
|
13
|
+
fenris = Fenris::Client.new url, options
|
14
|
+
command = args.shift
|
15
|
+
arg = args.first
|
16
|
+
|
17
|
+
case [ command, args.length ]
|
18
|
+
when [ "info", 0 ]
|
19
|
+
fenris.update_user_config
|
20
|
+
printf "INFO:\n"
|
21
|
+
printf " %s\n", fenris.user_name
|
22
|
+
unless fenris.users.empty?
|
23
|
+
puts "SUBACCOUNTS:"
|
24
|
+
fenris.users.each { |c| printf " %-20s\n", c["name"] }
|
25
|
+
end
|
26
|
+
unless fenris.consumers.empty?
|
27
|
+
puts "CLIENTS:"
|
28
|
+
fenris.consumers.each { |c| printf " %-20s\n", c["name"] }
|
29
|
+
end
|
30
|
+
unless fenris.providers.empty?
|
31
|
+
puts "SERVICES:"
|
32
|
+
fenris.providers.each { |c| printf " %-20s %s\n", c["name"], c["location"] }
|
33
|
+
end
|
34
|
+
when [ "useradd", 1 ]
|
35
|
+
Fenris::Client.new(url, options).useradd(arg)
|
36
|
+
puts "user added"
|
37
|
+
when [ "userdel", 1 ]
|
38
|
+
Fenris::Client.new(url, options).userdel(arg)
|
39
|
+
puts "ok"
|
40
|
+
when [ "sync", 0 ]
|
41
|
+
fenris.update_config
|
42
|
+
when [ "rekey", 0 ]
|
43
|
+
Fenris::Client.new(url, options).rekey
|
44
|
+
puts "rekey complete"
|
45
|
+
when [ "add", 1 ]
|
46
|
+
Fenris::Client.new(url, options).add(arg)
|
47
|
+
puts "ok"
|
48
|
+
when [ "remove", 1 ]
|
49
|
+
Fenris::Client.new(url, options).remove(arg)
|
50
|
+
puts "ok"
|
51
|
+
when [ "provide", 1 ]
|
52
|
+
Fenris::Client.new(url, options).provide(arg)
|
53
|
+
when [ "consume", 0 ]
|
54
|
+
Fenris::Client.new(url, options).consume
|
55
|
+
when [ "consume", 1 ]
|
56
|
+
Fenris::Client.new(url, options).consume(arg)
|
57
|
+
when [ "consume", 2 ]
|
58
|
+
Fenris::Client.new(url, options).consume(args[0] => args[1])
|
59
|
+
else
|
60
|
+
case commands[0]
|
61
|
+
when "exec"
|
62
|
+
options.quiet = true
|
63
|
+
Fenris::Client.new(url, options).exec(*args)
|
64
|
+
else
|
65
|
+
puts help
|
66
|
+
exit
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.parse(args)
|
73
|
+
options = OpenStruct.new
|
74
|
+
options.port = 10001
|
75
|
+
options.host = Socket.gethostname
|
76
|
+
options.user = ENV['FENRIS_USER']
|
77
|
+
options.password = ENV['FENRIS_AUTHKEY']
|
78
|
+
options.config = ENV['FENRIS_CONFIG'] || "#{ENV["HOME"]}/.fenris"
|
79
|
+
options.debug = !! ENV['FENRIS_DEBUG']
|
80
|
+
options.autosync = true
|
81
|
+
|
82
|
+
opts = OptionParser.new do |opts|
|
83
|
+
opts.banner = "Usage: fenris [options] COMMAND"
|
84
|
+
opts.separator ""
|
85
|
+
opts.separator "Commands:"
|
86
|
+
opts.separator ""
|
87
|
+
opts.separator " fenris info # info about current user and connections"
|
88
|
+
opts.separator " fenris useradd NAME # create a new subaccount"
|
89
|
+
opts.separator " fenris userdel NAME # delete a subaccount"
|
90
|
+
opts.separator " fenris add USER # allow USER to consume service"
|
91
|
+
opts.separator " fenris remove USER # disallow USER to consume service"
|
92
|
+
opts.separator " fenris provide BINDING # provide a local service"
|
93
|
+
opts.separator " fenris consume [ USER [ BINDING ] ] # consume one (or all) remote services"
|
94
|
+
opts.separator " fenris exec COMMAND # consume services and run COMMAND. Terminates when command exits."
|
95
|
+
opts.separator " fenris sync # sync config with broker"
|
96
|
+
opts.separator " fenris rekey # generate a new authkey for the user"
|
97
|
+
opts.separator " fenris bind USER BINDING # bind a remote service to a local binding"
|
98
|
+
opts.separator ""
|
99
|
+
opts.separator "Bindings can take the form of:"
|
100
|
+
opts.separator ""
|
101
|
+
opts.separator " PORT, :PORT, HOST:PORT, /PATH/TO/UNIX_SOCKET, or -- for STDIN"
|
102
|
+
opts.separator ""
|
103
|
+
opts.separator "Options:"
|
104
|
+
|
105
|
+
opts.on("-u", "--user USER", "Connect as USER. Default is $FENRIS_USER.") do |user|
|
106
|
+
options.user = user
|
107
|
+
end
|
108
|
+
|
109
|
+
opts.on("-c", "--config DIR", "Store config information in DIR. Default is $HOME/.fenris or $FENRIS_CONFIG.") do |dir|
|
110
|
+
options.config = dir
|
111
|
+
end
|
112
|
+
|
113
|
+
opts.on("-h", "--hostname HOST", "Hostname given to consumers on 'provide'. Default is `hostname`.") do |dir|
|
114
|
+
options.config = dir
|
115
|
+
end
|
116
|
+
|
117
|
+
opts.on("-p", "--port PORT", "") do |dir|
|
118
|
+
options.config = dir
|
119
|
+
end
|
120
|
+
|
121
|
+
opts.on("-q", "--quiet", "Do not print log messages.") do
|
122
|
+
options.quiet = true
|
123
|
+
end
|
124
|
+
|
125
|
+
opts.on("-d", "--debug", "Print debugging messages. Default is $FENRIS_DEBUG.") do
|
126
|
+
options.debug = true
|
127
|
+
end
|
128
|
+
|
129
|
+
opts.on("-a", "--[no-]autosync", "Automatically sync updates from the broker. Default is true.") do |auto|
|
130
|
+
options.autosync = auto
|
131
|
+
end
|
132
|
+
|
133
|
+
opts.on("-h", "--help", "Show this message") do
|
134
|
+
puts opts
|
135
|
+
exit
|
136
|
+
end
|
137
|
+
|
138
|
+
opts.on("--version", "Show version") do
|
139
|
+
puts Fenris::VERSION
|
140
|
+
exit
|
141
|
+
end
|
142
|
+
opts.separator ""
|
143
|
+
end
|
144
|
+
|
145
|
+
begin
|
146
|
+
commands = opts.parse!(args)
|
147
|
+
rescue OptionParser::InvalidOption
|
148
|
+
puts opts
|
149
|
+
exit
|
150
|
+
end
|
151
|
+
[ commands, options, opts.to_s ]
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
Fenris::Command.run(ARGV)
|
data/lib/fenris/client.rb
CHANGED
@@ -3,64 +3,191 @@ require 'json'
|
|
3
3
|
require 'openssl'
|
4
4
|
require 'uri'
|
5
5
|
require 'eventmachine'
|
6
|
+
require 'ostruct'
|
6
7
|
|
7
8
|
module Fenris
|
9
|
+
|
10
|
+
class NoSuchProvider < RuntimeError; end
|
11
|
+
|
8
12
|
class Client
|
9
|
-
def initialize(url)
|
13
|
+
def initialize(url, options = OpenStruct.new)
|
10
14
|
@url = url
|
11
15
|
@url = URI.parse(url) unless url.is_a? URI
|
12
|
-
|
16
|
+
options = OpenStruct.new options if options.is_a? Hash
|
17
|
+
@options = options
|
18
|
+
@verbose = false
|
19
|
+
end
|
20
|
+
|
21
|
+
def options
|
22
|
+
@options
|
23
|
+
end
|
24
|
+
|
25
|
+
def url
|
26
|
+
@url.user = options.user
|
27
|
+
@url.password = read_config_file("#{user_name}.authkey")
|
28
|
+
@url.password ||= options.password
|
29
|
+
@url.password ||= get_authkey_from_stdin
|
30
|
+
@url
|
31
|
+
end
|
32
|
+
|
33
|
+
def get_authkey_from_stdin
|
34
|
+
system "stty -echo"
|
35
|
+
print "Authkey: "
|
36
|
+
password = gets.chomp
|
37
|
+
system "stty echo"
|
38
|
+
password
|
39
|
+
end
|
40
|
+
|
41
|
+
def config_dir
|
42
|
+
options.config
|
43
|
+
end
|
44
|
+
|
45
|
+
def verify_private_key
|
46
|
+
if key = read_config_file("#{user_name}.key")
|
47
|
+
log "using existing key #{digest key}" if @verbose
|
48
|
+
else
|
49
|
+
key = OpenSSL::PKey::RSA.new(2048)
|
50
|
+
write key
|
51
|
+
log "new rsa key generated #{digest key}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def verify_cert(cn)
|
56
|
+
if cert = read_cert(cn)
|
57
|
+
log "existing cert #{digest cert} :: #{cert.not_after} :: #{cn}" if @verbose
|
58
|
+
else
|
59
|
+
cert = OpenSSL::X509::Certificate.new(RestClient.post("#{url}cert", :csr => generate_csr(cn)))
|
60
|
+
write cert
|
61
|
+
log "new cert received #{digest cert} :: #{cert.not_after} :: #{cn}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def read_cert cn
|
66
|
+
if cert = read_config_file("#{cn}.crt")
|
67
|
+
if cert.not_after > Time.now
|
68
|
+
cert
|
69
|
+
else
|
70
|
+
log "cert expired #{digest cert} :: #{cert.not_after} :: #{cn}"
|
71
|
+
nil
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def my_cert
|
77
|
+
read_config_file "#{user_name}.crt"
|
78
|
+
end
|
79
|
+
|
80
|
+
def my_key
|
81
|
+
read_config_file "#{user_name}.key"
|
82
|
+
end
|
83
|
+
|
84
|
+
def cert(name)
|
85
|
+
read_config_file "#{user_name}:#{name}.crt"
|
86
|
+
end
|
87
|
+
|
88
|
+
def cert_path(name)
|
89
|
+
"#{config_dir}/#{user_name}:#{name}.crt"
|
90
|
+
end
|
91
|
+
|
92
|
+
def my_cert_path
|
93
|
+
"#{config_dir}/#{user_name}.crt"
|
94
|
+
end
|
95
|
+
|
96
|
+
def my_key_path
|
97
|
+
"#{config_dir}/#{user_name}.key"
|
13
98
|
end
|
14
99
|
|
15
|
-
def
|
16
|
-
|
100
|
+
def write_cert(cert)
|
101
|
+
write_config_file "#{get_cn(cert)}.crt", cert
|
102
|
+
end
|
103
|
+
|
104
|
+
def write_config_file name, data
|
105
|
+
File.umask 0077
|
106
|
+
Dir.mkdir config_dir unless Dir.exists? config_dir
|
107
|
+
File.open("#{config_dir}/#{name}","w") { |f| f.write(data) }
|
108
|
+
end
|
109
|
+
|
110
|
+
def read_config_file name
|
111
|
+
path = "#{config_dir}/#{name}"
|
112
|
+
if not File.exists? path
|
113
|
+
nil
|
114
|
+
elsif name =~ /[.]json$/
|
115
|
+
JSON.parse File.read(path)
|
116
|
+
elsif name =~ /[.]crt$/
|
117
|
+
OpenSSL::X509::Certificate.new File.read(path)
|
118
|
+
elsif name =~ /[.]key$/
|
119
|
+
OpenSSL::PKey::RSA.new File.read(path)
|
120
|
+
else
|
121
|
+
File.read path
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def write object
|
126
|
+
if object.is_a? OpenSSL::X509::Certificate
|
127
|
+
write_config_file "#{get_cn(object)}.crt", object.to_pem
|
128
|
+
elsif object.is_a? OpenSSL::PKey::RSA
|
129
|
+
write_config_file "#{user_name}.key", object.to_pem
|
130
|
+
else
|
131
|
+
write_config_file "#{user_name}.json", object.to_json
|
132
|
+
end
|
17
133
|
end
|
18
134
|
|
19
135
|
def debug(message)
|
20
|
-
puts "DEBUG: #{message}" if
|
136
|
+
puts "DEBUG: #{message}" if options.debug and not options.quiet
|
21
137
|
end
|
22
138
|
|
23
139
|
def log(message)
|
24
|
-
puts "LOG: #{message}" if not
|
140
|
+
puts "LOG: #{message}" if not options.quiet
|
25
141
|
end
|
26
142
|
|
27
|
-
def
|
28
|
-
RestClient.put("#{
|
143
|
+
def post_location
|
144
|
+
RestClient.put("#{url}", { :location => location }, :content_type => :json, :accept => :json)
|
29
145
|
end
|
30
|
-
|
146
|
+
|
31
147
|
def user
|
32
|
-
|
148
|
+
@user ||= read_config_file "config.json"
|
33
149
|
end
|
34
150
|
|
35
151
|
def ssl?
|
36
152
|
@url.scheme == "https"
|
37
153
|
end
|
38
154
|
|
39
|
-
def
|
40
|
-
@
|
41
|
-
|
155
|
+
def update_user_config
|
156
|
+
@user = JSON.parse RestClient.get("#{url}", :content_type => :json, :accept => :json)
|
157
|
+
write_config_file "#{user_name}.authkey", @url.password
|
158
|
+
write user
|
42
159
|
end
|
43
160
|
|
44
|
-
def
|
45
|
-
|
161
|
+
def update_config(verbose = true)
|
162
|
+
@verbose = verbose
|
163
|
+
log "updating config in #{config_dir}" if @verbose
|
164
|
+
update_user_config
|
165
|
+
log "have update user config" if @verbose
|
166
|
+
write_config_file "root.crt", RestClient.get("#{url}cert") ## TODO find a way to get this out of the connection info
|
167
|
+
verify_private_key
|
168
|
+
verify_cert user_name
|
169
|
+
providers.each do |p|
|
170
|
+
verify_cert "#{user_name}:#{p["name"]}"
|
171
|
+
end
|
172
|
+
@verbose = false
|
46
173
|
end
|
47
174
|
|
48
|
-
def
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
debug response.status
|
58
|
-
@async_connection = nil
|
59
|
-
end
|
60
|
-
blk.call if blk
|
175
|
+
def provider_map
|
176
|
+
Hash[ * providers.map { |p| [ p["name"], p["binding"] ] }.flatten ]
|
177
|
+
end
|
178
|
+
|
179
|
+
def location_of(provider)
|
180
|
+
if p = providers.detect { |p| p["name"] == provider }
|
181
|
+
p["locations"].first
|
182
|
+
else
|
183
|
+
raise NoSuchProvider, provider
|
61
184
|
end
|
62
185
|
end
|
63
186
|
|
187
|
+
def location
|
188
|
+
"#{options.host}:#{options.port}"
|
189
|
+
end
|
190
|
+
|
64
191
|
def consumers
|
65
192
|
user["consumers"]
|
66
193
|
end
|
@@ -70,27 +197,31 @@ module Fenris
|
|
70
197
|
end
|
71
198
|
|
72
199
|
def user_name
|
73
|
-
user
|
74
|
-
end
|
75
|
-
|
76
|
-
def route
|
77
|
-
user["location"]
|
200
|
+
options.user
|
78
201
|
end
|
79
202
|
|
80
203
|
def remove(name)
|
81
|
-
|
204
|
+
update_user_config
|
205
|
+
RestClient.delete("#{url}consumers/#{name}");
|
82
206
|
end
|
83
207
|
|
84
208
|
def add(name)
|
85
|
-
|
209
|
+
update_user_config
|
210
|
+
RestClient.post("#{url}consumers", { :name => name }, :content_type => :json, :accept => :json);
|
86
211
|
end
|
87
212
|
|
88
213
|
def useradd(name)
|
89
|
-
|
214
|
+
update_user_config
|
215
|
+
newuser = JSON.parse RestClient.post("#{url}users", { :name => name }, :content_type => :json, :accept => :json);
|
216
|
+
write_config_file "#{newuser["name"]}.authkey", newuser["authkey"]
|
217
|
+
newuser
|
90
218
|
end
|
91
219
|
|
92
220
|
def rekey
|
93
|
-
|
221
|
+
update_user_config
|
222
|
+
newkey = RestClient.post("#{url}authkeys", { }, :content_type => :json, :accept => :json);
|
223
|
+
write_config_file "#{user_name}.authkey", newkey
|
224
|
+
newkey
|
94
225
|
end
|
95
226
|
|
96
227
|
def users
|
@@ -98,24 +229,21 @@ module Fenris
|
|
98
229
|
end
|
99
230
|
|
100
231
|
def userdel(name)
|
101
|
-
|
232
|
+
update_user_config
|
233
|
+
RestClient.delete("#{url}users/#{name}", :content_type => :json, :accept => :json);
|
102
234
|
end
|
103
235
|
|
104
236
|
def bind(name, binding)
|
105
|
-
|
237
|
+
update_user_config
|
238
|
+
RestClient.put("#{url}providers/#{name}", { :binding => binding }, :content_type => :json, :accept => :json);
|
106
239
|
end
|
107
240
|
|
108
241
|
def digest obj
|
109
242
|
OpenSSL::Digest::SHA1.new(obj.to_der).to_s
|
110
243
|
end
|
111
244
|
|
112
|
-
def generate_csr(
|
113
|
-
|
114
|
-
subject = OpenSSL::X509::Name.parse "/DC=org/DC=fenris/CN=#{user_name}"
|
115
|
-
else
|
116
|
-
subject = OpenSSL::X509::Name.parse "/DC=org/DC=fenris/CN=#{user_name}:#{provider}"
|
117
|
-
end
|
118
|
-
log "CSR: #{subject}"
|
245
|
+
def generate_csr(cn)
|
246
|
+
subject = OpenSSL::X509::Name.parse "/DC=org/DC=fenris/CN=#{cn}"
|
119
247
|
digest = OpenSSL::Digest::SHA1.new
|
120
248
|
req = OpenSSL::X509::Request.new
|
121
249
|
req.version = 0
|
@@ -138,7 +266,7 @@ module Fenris
|
|
138
266
|
consumer_cn,provider_cn = cert_cn.split ":"
|
139
267
|
provider_cn_ok = !!user_name
|
140
268
|
consumer_cn_ok = !!valid_peer_names.detect { |name| name == consumer_cn }
|
141
|
-
cert_ok = !!consumer_cert.verify(
|
269
|
+
cert_ok = !!consumer_cert.verify(root.public_key)
|
142
270
|
log "Consumer Cert CN '#{cert_cn}' displays correct provider? #{provider_cn_ok}"
|
143
271
|
log "Consumer Cert CN '#{cert_cn}' in allowed_list? #{consumer_cn_ok}"
|
144
272
|
log "Consumer Cert Signed By Broker? '#{cert_ok}'"
|
@@ -150,71 +278,50 @@ module Fenris
|
|
150
278
|
result
|
151
279
|
end
|
152
280
|
|
153
|
-
def
|
154
|
-
|
155
|
-
log "Deleting socket '#{provider["binding"]}'."
|
156
|
-
File.delete provider["binding"] if File.exists? provider["binding"]
|
157
|
-
end
|
158
|
-
## TODO - gawd! ugly!
|
159
|
-
[ *providers.map { |c| c["name"] }.map { |provider| cert_path(provider) }, cert_path, key_path ].each do |f|
|
160
|
-
if File.exists? f
|
161
|
-
log "Deleting file #{f}"
|
162
|
-
File.delete f
|
163
|
-
end
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
|
-
def save_keys
|
168
|
-
providers.map { |c| c["name"] }.each do |provider|
|
169
|
-
File.open(cert_path(provider),"w") { |f| f.write cert(provider).to_pem } unless File.exists? cert_path(provider)
|
170
|
-
end
|
171
|
-
File.open(cert_path,"w") { |f| f.write cert.to_pem } unless File.exists? cert_path
|
172
|
-
File.open(key_path,"w") { |f| f.write key.to_pem } unless File.exists? key_path
|
281
|
+
def root
|
282
|
+
read_config_file "root.crt"
|
173
283
|
end
|
174
284
|
|
175
|
-
def
|
176
|
-
|
177
|
-
log "new cert received #{digest cert}"
|
178
|
-
cert
|
285
|
+
def key
|
286
|
+
read_config_file "#{user_name}.key"
|
179
287
|
end
|
180
288
|
|
181
|
-
|
182
|
-
key = OpenSSL::PKey::RSA.new(2048)
|
183
|
-
log "new rsa key generated #{digest key}"
|
184
|
-
key
|
185
|
-
end
|
289
|
+
UPDATE_INTERVAL = 10
|
186
290
|
|
187
|
-
def
|
188
|
-
|
189
|
-
|
190
|
-
|
291
|
+
def provide(local_binding)
|
292
|
+
update_config
|
293
|
+
EventMachine::run do
|
294
|
+
EventMachine::PeriodicTimer.new(UPDATE_INTERVAL) { Thread.new { update_config(false) } } if options.autosync
|
295
|
+
post_location
|
296
|
+
ProviderServer.begin(self, location, local_binding)
|
297
|
+
end
|
191
298
|
end
|
192
299
|
|
193
|
-
def
|
194
|
-
|
195
|
-
end
|
300
|
+
def consume(arg = nil, &blk)
|
301
|
+
update_config
|
196
302
|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
@cert[provider] ||= gen_cert(provider)
|
201
|
-
end
|
303
|
+
map = arg if arg.is_a? Hash
|
304
|
+
map = { arg => provider_map[arg] } if arg.is_a? String
|
305
|
+
map ||= provider_map
|
202
306
|
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
if provider == :self
|
210
|
-
".#{user_name}.cert"
|
211
|
-
else
|
212
|
-
".#{user_name}:#{provider}.cert"
|
307
|
+
EventMachine::run do
|
308
|
+
EventMachine::PeriodicTimer.new(UPDATE_INTERVAL) { update_config(false) } if options.autosync
|
309
|
+
map.each do |provider, binding|
|
310
|
+
ConsumerLocal.begin(self, location_of(provider), provider, binding)
|
311
|
+
end
|
312
|
+
blk.call if blk
|
213
313
|
end
|
214
314
|
end
|
215
315
|
|
216
|
-
def
|
217
|
-
|
316
|
+
def exec(*args)
|
317
|
+
command = args.join(' ')
|
318
|
+
consume do
|
319
|
+
Thread.new do
|
320
|
+
system command
|
321
|
+
EventMachine::stop
|
322
|
+
end
|
323
|
+
end
|
218
324
|
end
|
219
325
|
end
|
220
326
|
end
|
327
|
+
|
data/lib/fenris/command.rb
CHANGED
@@ -1,87 +1,178 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'socket'
|
3
|
+
require 'ostruct'
|
4
|
+
require 'uri'
|
5
|
+
|
1
6
|
module Fenris
|
2
|
-
|
3
|
-
def self.run(
|
4
|
-
arg, name = args
|
7
|
+
class Command
|
8
|
+
def self.run(args)
|
5
9
|
begin
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
10
|
+
run_command(args)
|
11
|
+
rescue RestClient::Conflict
|
12
|
+
puts "Duplicate"
|
13
|
+
rescue RestClient::Unauthorized
|
14
|
+
puts "Unauthorized"
|
15
|
+
rescue RestClient::ResourceNotFound
|
16
|
+
puts "Resource Not Found"
|
17
|
+
rescue Fenris::NoSuchProvider => e
|
18
|
+
puts "No such provider '#{e.message}'"
|
19
|
+
exit
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.run_command(args)
|
24
|
+
url = URI.parse(ENV['FENRIS_URL'] || 'https://broker.fenris.cc/')
|
25
|
+
args, options, help = parse(args)
|
26
|
+
fenris = Fenris::Client.new url, options
|
27
|
+
command = args.shift
|
28
|
+
arg = args.first
|
29
|
+
|
30
|
+
case [ command, args.length ]
|
31
|
+
when [ "info", 0 ]
|
32
|
+
fenris.update_user_config
|
33
|
+
printf "INFO:\n"
|
34
|
+
printf " %s\n", fenris.user_name
|
35
|
+
unless fenris.users.empty?
|
36
|
+
puts "SUBACCOUNTS:"
|
37
|
+
fenris.users.each { |c| printf " %-20s\n", c["name"] }
|
38
|
+
end
|
39
|
+
unless fenris.consumers.empty?
|
40
|
+
puts "CLIENTS:"
|
41
|
+
fenris.consumers.each { |c| printf " %-20s\n", c["name"] }
|
42
|
+
end
|
43
|
+
unless fenris.providers.empty?
|
44
|
+
puts "SERVICES:"
|
45
|
+
fenris.providers.each { |c| printf " %-20s [%s] %s\n", c["name"], c["binding"], c["location"] }
|
46
|
+
end
|
47
|
+
when [ "useradd", 1 ]
|
48
|
+
fenris.useradd(arg)
|
49
|
+
puts "user added"
|
50
|
+
when [ "userdel", 1 ]
|
51
|
+
fenris.userdel(arg)
|
52
|
+
puts "ok"
|
53
|
+
when [ "bind", 2 ]
|
54
|
+
fenris.bind(args[0], args[1])
|
55
|
+
when [ "sync", 0 ]
|
56
|
+
fenris.update_config
|
57
|
+
when [ "rekey", 0 ]
|
58
|
+
fenris.rekey
|
59
|
+
puts "rekey complete"
|
60
|
+
when [ "add", 1 ]
|
61
|
+
fenris.add(arg)
|
62
|
+
puts "ok"
|
63
|
+
when [ "remove", 1 ]
|
64
|
+
fenris.remove(arg)
|
65
|
+
puts "ok"
|
66
|
+
when [ "provide", 1 ]
|
67
|
+
fenris.provide(arg)
|
68
|
+
when [ "consume", 0 ]
|
69
|
+
fenris.consume
|
70
|
+
when [ "consume", 1 ]
|
71
|
+
fenris.consume(arg)
|
72
|
+
when [ "consume", 2 ]
|
73
|
+
fenris.consume(args[0] => args[1])
|
74
|
+
else
|
22
75
|
case command
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
76
|
+
when "exec"
|
77
|
+
options.quiet = true
|
78
|
+
Fenris::Client.new(url, options).exec(*args)
|
79
|
+
else
|
80
|
+
puts help
|
81
|
+
exit
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.parse(args)
|
87
|
+
extra = []
|
88
|
+
options = OpenStruct.new
|
89
|
+
options.port = 10001
|
90
|
+
options.host = Socket.gethostname
|
91
|
+
options.user = ENV['FENRIS_USER']
|
92
|
+
options.password = ENV['FENRIS_AUTHKEY']
|
93
|
+
options.config = ENV['FENRIS_CONFIG'] || "#{ENV["HOME"]}/.fenris"
|
94
|
+
options.debug = !! ENV['FENRIS_DEBUG']
|
95
|
+
options.autosync = true
|
96
|
+
|
97
|
+
if i = args.index("exec")
|
98
|
+
extra = args[i..(args.length)]
|
99
|
+
if i > 0
|
100
|
+
args = args[0..(i-1)]
|
101
|
+
else
|
102
|
+
args = []
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
opts = OptionParser.new do |opts|
|
107
|
+
opts.banner = "Usage: fenris [options] COMMAND"
|
108
|
+
opts.separator ""
|
109
|
+
opts.separator "Commands:"
|
110
|
+
opts.separator ""
|
111
|
+
opts.separator " fenris info # info about current user and connections"
|
112
|
+
opts.separator " fenris useradd NAME # create a new subaccount"
|
113
|
+
opts.separator " fenris userdel NAME # delete a subaccount"
|
114
|
+
opts.separator " fenris add USER # allow USER to consume service"
|
115
|
+
opts.separator " fenris remove USER # disallow USER to consume service"
|
116
|
+
opts.separator " fenris provide BINDING # provide a local service"
|
117
|
+
opts.separator " fenris consume [ USER [ BINDING ] ] # consume one (or all) remote services"
|
118
|
+
opts.separator " fenris exec COMMAND # consume services and run COMMAND. Terminates when command exits."
|
119
|
+
opts.separator " fenris sync # sync config with broker"
|
120
|
+
opts.separator " fenris rekey # generate a new authkey for the user"
|
121
|
+
opts.separator " fenris bind USER BINDING # bind a remote service to a local binding"
|
122
|
+
opts.separator ""
|
123
|
+
opts.separator "Bindings can take the form of:"
|
124
|
+
opts.separator ""
|
125
|
+
opts.separator " PORT, :PORT, HOST:PORT, /PATH/TO/UNIX_SOCKET, or -- for STDIN"
|
126
|
+
opts.separator ""
|
127
|
+
opts.separator "Options:"
|
128
|
+
|
129
|
+
opts.on("-u", "--user USER", "Connect as USER. Default is $FENRIS_USER.") do |user|
|
130
|
+
options.user = user
|
74
131
|
end
|
132
|
+
|
133
|
+
opts.on("-c", "--config DIR", "Store config information in DIR. Default is $HOME/.fenris or $FENRIS_CONFIG.") do |dir|
|
134
|
+
options.config = dir
|
135
|
+
end
|
136
|
+
|
137
|
+
opts.on("-h", "--hostname HOST", "Hostname given to consumers on 'provide'. Default is `hostname`.") do |dir|
|
138
|
+
options.config = dir
|
139
|
+
end
|
140
|
+
|
141
|
+
opts.on("-p", "--port PORT", "") do |dir|
|
142
|
+
options.config = dir
|
143
|
+
end
|
144
|
+
|
145
|
+
opts.on("-q", "--quiet", "Do not print log messages.") do
|
146
|
+
options.quiet = true
|
147
|
+
end
|
148
|
+
|
149
|
+
opts.on("-d", "--debug", "Print debugging messages. Default is $FENRIS_DEBUG.") do
|
150
|
+
options.debug = true
|
151
|
+
end
|
152
|
+
|
153
|
+
opts.on("-a", "--[no-]autosync", "Automatically sync updates from the broker. Default is true.") do |auto|
|
154
|
+
options.autosync = auto
|
155
|
+
end
|
156
|
+
|
157
|
+
opts.on("-h", "--help", "Show this message") do
|
158
|
+
puts opts
|
159
|
+
exit
|
160
|
+
end
|
161
|
+
|
162
|
+
opts.on("--version", "Show version") do
|
163
|
+
puts Fenris::VERSION
|
164
|
+
exit
|
165
|
+
end
|
166
|
+
opts.separator ""
|
167
|
+
end
|
168
|
+
|
169
|
+
begin
|
170
|
+
commands = opts.parse!(args)
|
171
|
+
rescue OptionParser::InvalidOption
|
172
|
+
puts opts
|
75
173
|
exit
|
76
174
|
end
|
77
|
-
|
78
|
-
rescue RestClient::Conflict
|
79
|
-
puts "Duplicate"
|
80
|
-
rescue RestClient::Unauthorized
|
81
|
-
puts "Unauthorized"
|
82
|
-
rescue RestClient::ResourceNotFound
|
83
|
-
puts "Resource Not Found"
|
84
|
-
exit
|
175
|
+
[ commands + extra, options, opts.to_s ]
|
85
176
|
end
|
86
177
|
end
|
87
178
|
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module Fenris
|
2
|
+
module Command
|
3
|
+
def self.run(command, *args)
|
4
|
+
arg, name = args
|
5
|
+
begin
|
6
|
+
url = URI.parse(ENV['FENRIS_URL'] || 'https://broker.fenris.cc/')
|
7
|
+
url.user ||= ENV['FENRIS_USER']
|
8
|
+
url.password ||= ENV['FENRIS_AUTHKEY']
|
9
|
+
client = Fenris::Client.new url
|
10
|
+
help = [ "Usage: fenris help\n",
|
11
|
+
" fenris info\n",
|
12
|
+
" fenris bind PROVIDER BINDING\n",
|
13
|
+
" fenris useradd NAME\n",
|
14
|
+
" fenris userdel NAME\n",
|
15
|
+
" fenris update\n",
|
16
|
+
" fenris rekey\n",
|
17
|
+
" fenris add CONSUMER\n",
|
18
|
+
" fenris remove CONSUMER\n",
|
19
|
+
" fenris exec COMMAND\n",
|
20
|
+
" fenris provide BINDING\n",
|
21
|
+
" fenris consume [ USER [ BINDING ] ]" ]
|
22
|
+
case command
|
23
|
+
when "update"
|
24
|
+
client.update_config
|
25
|
+
when "userdel"
|
26
|
+
client.update_user_config
|
27
|
+
client.userdel(arg)
|
28
|
+
puts "ok"
|
29
|
+
when "useradd"
|
30
|
+
client.update_user_config
|
31
|
+
new_user = client.useradd(arg)
|
32
|
+
puts "New user created '#{new_user["name"]}'"
|
33
|
+
when "rekey"
|
34
|
+
client.update_user_config
|
35
|
+
newkey = client.rekey
|
36
|
+
puts "Rekey Complete"
|
37
|
+
when "cert"
|
38
|
+
client.update_user_config
|
39
|
+
puts client.cert.to_text
|
40
|
+
when "bind"
|
41
|
+
client.update_user_config
|
42
|
+
client.bind(arg,name)
|
43
|
+
when "add"
|
44
|
+
client.update_user_config
|
45
|
+
client.add(arg)
|
46
|
+
when "remove"
|
47
|
+
client.update_user_config
|
48
|
+
client.remove(arg)
|
49
|
+
when "info"
|
50
|
+
client.update_user_config
|
51
|
+
printf "INFO:\n"
|
52
|
+
printf " %s\n", client.user["name"]
|
53
|
+
unless client.users.empty?
|
54
|
+
puts "SUBACCOUNTS:"
|
55
|
+
client.users.each { |c| printf " %-20s\n", c["name"] }
|
56
|
+
end
|
57
|
+
unless client.consumers.empty?
|
58
|
+
puts "CLIENTS:"
|
59
|
+
client.consumers.each { |c| printf " %-20s\n", c["name"] }
|
60
|
+
end
|
61
|
+
unless client.providers.empty?
|
62
|
+
puts "SERVICES:"
|
63
|
+
client.providers.each { |c| printf " %-20s %s\n", c["name"], c["location"] }
|
64
|
+
end
|
65
|
+
when "provide"
|
66
|
+
external = "#{Socket.gethostname}:#{10001}"
|
67
|
+
internal = arg
|
68
|
+
client.provide(external, internal)
|
69
|
+
when "consume"
|
70
|
+
client.consume(arg, name)
|
71
|
+
when "exec"
|
72
|
+
client.quiet = true
|
73
|
+
client.exec(*args)
|
74
|
+
else
|
75
|
+
puts command.inspect
|
76
|
+
puts help
|
77
|
+
end
|
78
|
+
exit
|
79
|
+
end
|
80
|
+
rescue SystemExit
|
81
|
+
rescue RestClient::Conflict
|
82
|
+
puts "Duplicate"
|
83
|
+
rescue RestClient::Unauthorized
|
84
|
+
puts "Unauthorized"
|
85
|
+
rescue RestClient::ResourceNotFound
|
86
|
+
puts "Resource Not Found"
|
87
|
+
exit
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
|
3
|
+
module Fenris
|
4
|
+
class Connection < EventMachine::Connection
|
5
|
+
def self.mkbinding(action, binding)
|
6
|
+
if binding =~ /^:?(\d+)$/
|
7
|
+
[ action, "0.0.0.0", $1.to_i ]
|
8
|
+
elsif binding =~ /^(.+):(\d+)/
|
9
|
+
[ action, $1, $2.to_i ]
|
10
|
+
elsif binding == "--"
|
11
|
+
[ :attach, $stdin ]
|
12
|
+
else
|
13
|
+
[ action, binding ]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(client = nil, options = {})
|
18
|
+
@client = client
|
19
|
+
## TODO -- peer_to_be vs @peer is a poor name choice - refactor
|
20
|
+
## sometimes I call the proxying connection on the local box 'peer', sometimes I call the other fenris process 'peer' - confusing
|
21
|
+
@binding = options[:binding]
|
22
|
+
@peer_to_be = options[:peer]
|
23
|
+
@peer_name = options[:peer_name]
|
24
|
+
@peer_binding = options[:peer_binding]
|
25
|
+
@peer = []
|
26
|
+
end
|
27
|
+
|
28
|
+
def log msg
|
29
|
+
@client.log msg if @client
|
30
|
+
end
|
31
|
+
|
32
|
+
def push(data)
|
33
|
+
# this is the black magic to switch @peer between a connection and an array
|
34
|
+
send_data data
|
35
|
+
end
|
36
|
+
|
37
|
+
def ssl_verify_peer(pem)
|
38
|
+
@verify ||= @client.validate_peer pem, @peer_to_be, @peer_name
|
39
|
+
end
|
40
|
+
|
41
|
+
def unbind
|
42
|
+
log "Connection closed"
|
43
|
+
@unbound = true
|
44
|
+
## TODO clean up buffer vs peer vs peer_to_be
|
45
|
+
(@peer_to_be || @peer).close_connection_after_writing rescue nil
|
46
|
+
close_connection
|
47
|
+
EventMachine::stop if @binding[0] == :attach
|
48
|
+
end
|
49
|
+
|
50
|
+
def ssl_handshake_completed
|
51
|
+
@post_ssl.call
|
52
|
+
end
|
53
|
+
|
54
|
+
def proxy(peer, leader = true)
|
55
|
+
peer.close if @unbound
|
56
|
+
@peer.each { |d| peer.push d}
|
57
|
+
@peer = peer
|
58
|
+
@peer_to_be = nil
|
59
|
+
if leader
|
60
|
+
@peer.proxy(self, false)
|
61
|
+
EventMachine::enable_proxy(self, @peer)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def proxy_target_unbound
|
66
|
+
unbind
|
67
|
+
end
|
68
|
+
|
69
|
+
def receive_data data
|
70
|
+
@peer.push data
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class ProviderServer < Connection
|
75
|
+
def self.begin(client, provider_binding, local_binding)
|
76
|
+
client.log "Serving port #{local_binding} on #{provider_binding}"
|
77
|
+
server_em = mkbinding(:start_server, provider_binding)
|
78
|
+
local_em = mkbinding(:connect, local_binding)
|
79
|
+
EventMachine::__send__ *server_em, self, client, :peer_binding => local_em, :binding => server_em
|
80
|
+
end
|
81
|
+
|
82
|
+
def post_init
|
83
|
+
log "New connection - begin ssl handshake"
|
84
|
+
start_tls :private_key_file => @client.my_key_path, :cert_chain_file => @client.my_cert_path, :verify_peer => true
|
85
|
+
end
|
86
|
+
|
87
|
+
def ssl_handshake_completed
|
88
|
+
log "SSL complete - open local connection"
|
89
|
+
EventMachine::__send__ *@peer_binding, ProviderLocal, @client, :peer => self, :binding => @peer_binding
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class ProviderLocal < Connection
|
94
|
+
def post_init
|
95
|
+
log "start proxying"
|
96
|
+
@peer_to_be.proxy self
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
class ConsumerLocal < Connection
|
101
|
+
def self.begin(client, provider_binding, provider_name, consumer_binding)
|
102
|
+
local_em = mkbinding(:start_server, consumer_binding)
|
103
|
+
provider_em = mkbinding(:connect, provider_binding)
|
104
|
+
client.log consumer_binding.inspect
|
105
|
+
EventMachine::__send__ *local_em, self, client, :peer_name => provider_name, :peer_binding => provider_em, :binding => local_em
|
106
|
+
end
|
107
|
+
|
108
|
+
def post_init
|
109
|
+
EventMachine::__send__ *@peer_binding, ConsumerProvider, @client, :peer_name => @peer_name, :peer => self, :binding => @peer_binding
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class ConsumerProvider < Connection
|
114
|
+
def post_init
|
115
|
+
log "Connection to the server made, starting ssl"
|
116
|
+
start_tls :private_key_file => @client.my_key_path, :cert_chain_file => @client.cert_path(@peer_name), :verify_peer => true
|
117
|
+
end
|
118
|
+
|
119
|
+
def ssl_handshake_completed
|
120
|
+
log "SSL complete - start proxying"
|
121
|
+
@peer_to_be.proxy self
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
data/lib/fenris/version.rb
CHANGED
metadata
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fenris
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.8
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 8
|
9
9
|
prerelease: false
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
@@ -13,12 +13,12 @@ authors:
|
|
13
13
|
autorequire:
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
|
-
date: 2011-
|
16
|
+
date: 2011-11-01 00:00:00.000000000 -07:00
|
17
17
|
default_executable:
|
18
18
|
dependencies:
|
19
19
|
- !ruby/object:Gem::Dependency
|
20
20
|
name: eventmachine
|
21
|
-
requirement: &
|
21
|
+
requirement: &2154437240 !ruby/object:Gem::Requirement
|
22
22
|
none: false
|
23
23
|
requirements:
|
24
24
|
- - ! '>='
|
@@ -30,10 +30,10 @@ dependencies:
|
|
30
30
|
- 10
|
31
31
|
type: :runtime
|
32
32
|
prerelease: false
|
33
|
-
version_requirements: *
|
33
|
+
version_requirements: *2154437240
|
34
34
|
- !ruby/object:Gem::Dependency
|
35
35
|
name: rest-client
|
36
|
-
requirement: &
|
36
|
+
requirement: &2154436420 !ruby/object:Gem::Requirement
|
37
37
|
none: false
|
38
38
|
requirements:
|
39
39
|
- - ! '>='
|
@@ -45,10 +45,10 @@ dependencies:
|
|
45
45
|
- 7
|
46
46
|
type: :runtime
|
47
47
|
prerelease: false
|
48
|
-
version_requirements: *
|
48
|
+
version_requirements: *2154436420
|
49
49
|
- !ruby/object:Gem::Dependency
|
50
50
|
name: multi_json
|
51
|
-
requirement: &
|
51
|
+
requirement: &2154435660 !ruby/object:Gem::Requirement
|
52
52
|
none: false
|
53
53
|
requirements:
|
54
54
|
- - ! '>='
|
@@ -60,7 +60,7 @@ dependencies:
|
|
60
60
|
- 3
|
61
61
|
type: :runtime
|
62
62
|
prerelease: false
|
63
|
-
version_requirements: *
|
63
|
+
version_requirements: *2154435660
|
64
64
|
description: An authentication and service location service.
|
65
65
|
email: orion.henry@gmail.com
|
66
66
|
executables:
|
@@ -69,9 +69,11 @@ extensions: []
|
|
69
69
|
extra_rdoc_files: []
|
70
70
|
files:
|
71
71
|
- bin/fenris
|
72
|
+
- bin/xx
|
72
73
|
- lib/fenris/client.rb
|
73
74
|
- lib/fenris/command.rb
|
74
|
-
- lib/fenris/
|
75
|
+
- lib/fenris/command.rb.old
|
76
|
+
- lib/fenris/connection.rb
|
75
77
|
- lib/fenris/version.rb
|
76
78
|
has_rdoc: true
|
77
79
|
homepage: http://github.com/orionz/fenris
|
data/lib/fenris/em.rb
DELETED
@@ -1,164 +0,0 @@
|
|
1
|
-
require 'eventmachine'
|
2
|
-
|
3
|
-
module Fenris
|
4
|
-
module Connection
|
5
|
-
def initialize
|
6
|
-
@peer = []
|
7
|
-
end
|
8
|
-
|
9
|
-
def push(data)
|
10
|
-
# this is the black magic to switch @peer between a connection and an array
|
11
|
-
send_data data
|
12
|
-
end
|
13
|
-
|
14
|
-
def begin_ssl(opts, &blk)
|
15
|
-
@post_ssl = blk
|
16
|
-
start_tls :private_key_file => opts[:key_file], :cert_chain_file => opts[:cert_file], :verify_peer => true
|
17
|
-
end
|
18
|
-
|
19
|
-
def post_init
|
20
|
-
end
|
21
|
-
|
22
|
-
def validate_peer(&blk)
|
23
|
-
@validator = blk;
|
24
|
-
end
|
25
|
-
|
26
|
-
def on_unbind(&blk)
|
27
|
-
@on_unbind = blk
|
28
|
-
end
|
29
|
-
|
30
|
-
def ssl_verify_peer(pem)
|
31
|
-
@verify ||= @validator.call(pem)
|
32
|
-
end
|
33
|
-
|
34
|
-
def unbind
|
35
|
-
@on_unbind.call(self) if @on_unbind
|
36
|
-
@unbound = true
|
37
|
-
EM::stop if @signature < 3 ## this is for attach($stdin)
|
38
|
-
@peer.close_connection_after_writing rescue nil
|
39
|
-
close_connection
|
40
|
-
end
|
41
|
-
|
42
|
-
def ssl_handshake_completed
|
43
|
-
@post_ssl.call
|
44
|
-
end
|
45
|
-
|
46
|
-
def proxy(peer)
|
47
|
-
peer.close if @unbound
|
48
|
-
@peer.each { |d| peer.push d}
|
49
|
-
@peer = peer
|
50
|
-
end
|
51
|
-
|
52
|
-
def receive_data data
|
53
|
-
@peer.push data
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
module Base
|
58
|
-
extend self
|
59
|
-
|
60
|
-
UPDATE_INTERVAL = 10
|
61
|
-
|
62
|
-
def listen(client, external, internal)
|
63
|
-
return listen_stdio(client, external, internal) if internal == "--"
|
64
|
-
EventMachine::__send__ *mkbinding(:start_server, external), Fenris::Connection do |consumer|
|
65
|
-
client.log "New connection - begin ssl handshake"
|
66
|
-
consumer.validate_peer { |pem| client.validate_peer pem }
|
67
|
-
consumer.begin_ssl :key_file => client.key_path , :cert_file => client.cert_path do
|
68
|
-
client.log "SSL complete - open local connection"
|
69
|
-
EventMachine::__send__ *mkbinding(:connect, internal), Fenris::Connection do |provider|
|
70
|
-
client.log "start proxying"
|
71
|
-
provider.proxy consumer; consumer.proxy provider
|
72
|
-
provider.on_unbind { client.log "Connection closed" }
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def listen_stdio(client, external, internal)
|
79
|
-
EventMachine::__send__ *mkbinding(:connect, internal), Fenris::Connection do |provider|
|
80
|
-
EventMachine::__send__ *mkbinding(:start_server, external), Fenris::Connection do |consumer|
|
81
|
-
client.log "stdio connection - begin ssl handshake"
|
82
|
-
consumer.validate_peer { |pem| client.validate_peer pem }
|
83
|
-
consumer.begin_ssl :key_file => client.key_path , :cert_file => client.cert_path do
|
84
|
-
client.log "SSL complete - start proxying"
|
85
|
-
provider.proxy consumer; consumer.proxy provider
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
def provide(client, external, internal)
|
92
|
-
at_exit { client.cleanup }
|
93
|
-
|
94
|
-
EventMachine::run do
|
95
|
-
client.save_keys
|
96
|
-
EventMachine::PeriodicTimer.new(UPDATE_INTERVAL) { client.async_update } unless ENV['FENRIS_NO_TIMER']
|
97
|
-
client.update external
|
98
|
-
client.log "Serving port #{internal} on #{external}"
|
99
|
-
listen client, external, internal
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
def mkbinding(action, binding)
|
104
|
-
if binding =~ /^:?(\d+)$/
|
105
|
-
[ action, "0.0.0.0", $1.to_i ]
|
106
|
-
elsif binding =~ /^(.+):(\d+)/
|
107
|
-
[ action, $1, $2.to_i ]
|
108
|
-
elsif binding == "--"
|
109
|
-
[ :attach, $stdin ]
|
110
|
-
else
|
111
|
-
[ action, binding ]
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
## really want to unify all these :(
|
116
|
-
def consumer_connect(client, consumer, provider_name, provider)
|
117
|
-
client.log "COMSUMER CONNECT #{consumer.inspect} #{provider_name.inspect} #{provider.inspect}"
|
118
|
-
EventMachine::__send__ *mkbinding(:start_server, consumer), Fenris::Connection do |consumer|
|
119
|
-
client.log "New connection: opening connection to the server"
|
120
|
-
EventMachine::__send__ *mkbinding(:connect, provider), Fenris::Connection do |provider|
|
121
|
-
client.log "Connection to the server made, starting ssl"
|
122
|
-
provider.validate_peer { |pem| client.validate_peer pem, consumer, provider_name }
|
123
|
-
provider.begin_ssl :key_file => client.key_path , :cert_file => client.cert_path(provider_name) do
|
124
|
-
client.log "SSL complete - start proxying"
|
125
|
-
provider.proxy consumer; consumer.proxy provider
|
126
|
-
end
|
127
|
-
end
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
def consume(client, overide_provider = nil, override_binding = nil)
|
132
|
-
at_exit { client.cleanup }
|
133
|
-
|
134
|
-
client.save_keys
|
135
|
-
|
136
|
-
abort "No providers" if client.providers.empty?
|
137
|
-
|
138
|
-
providers = client.providers.reject { |p| overide_provider && p["name"] != overide_provider }
|
139
|
-
|
140
|
-
abort "No provider named #{overide_provider}" if providers.empty?
|
141
|
-
abort "Can only pass a binding for a single provider" if override_binding && providers.length != 1
|
142
|
-
|
143
|
-
EventMachine::run do
|
144
|
-
EventMachine::PeriodicTimer.new(UPDATE_INTERVAL) { client.async_update } unless ENV['FENRIS_NO_TIMER']
|
145
|
-
providers.each do |p|
|
146
|
-
binding = override_binding || p["binding"]
|
147
|
-
consumer_connect(client, binding, p["name"], p["location"])
|
148
|
-
end
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
def exec(client, *args)
|
153
|
-
command = args.join(' ')
|
154
|
-
EventMachine::run do
|
155
|
-
consume(client)
|
156
|
-
Thread.new do
|
157
|
-
system command
|
158
|
-
EM::stop
|
159
|
-
end
|
160
|
-
end
|
161
|
-
end
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|