fenris 0.0.6 → 0.0.8
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/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
|
-
|