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 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 command, *args
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)
@@ -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
- @quiet = false
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 quiet= val
16
- @quiet = val
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 ENV['DEBUG'] and !@quiet
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 @quiet
140
+ puts "LOG: #{message}" if not options.quiet
25
141
  end
26
142
 
27
- def update(location)
28
- RestClient.put("#{@url}", { :location => location }, :content_type => :json, :accept => :json)
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
- @user ||= JSON.parse RestClient.get("#{@url}", :content_type => :json, :accept => :json)
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 async_connection
40
- @async_connection = nil if @async_connection && @async_connection.error?
41
- @async_connection ||= EM::Protocols::HttpClient2.connect :host => @url.host, :port => @url.port, :ssl => ssl?
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 auth_string
45
- "Basic " + ["#{@url.user}:#{@url.password}"].pack('m').strip.gsub(/\n/,'')
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 async_update(&blk)
49
- request = async_connection.get(:uri => "/", :authorization => auth_string )
50
- request.callback do |response|
51
- if response.status == 200
52
- # TODO - output if something has changed
53
- @broker ||= OpenSSL::X509::Certificate.new(async_connection.get_peer_cert) if ssl?
54
- @user = JSON.parse response.content
55
- else
56
- log "Error updating user info"
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["name"]
74
- end
75
-
76
- def route
77
- user["location"]
200
+ options.user
78
201
  end
79
202
 
80
203
  def remove(name)
81
- RestClient.delete("#{@url}consumers/#{name}");
204
+ update_user_config
205
+ RestClient.delete("#{url}consumers/#{name}");
82
206
  end
83
207
 
84
208
  def add(name)
85
- RestClient.post("#{@url}consumers", { :name => name }, :content_type => :json, :accept => :json);
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
- JSON.parse RestClient.post("#{@url}users", { :name => name }, :content_type => :json, :accept => :json);
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
- RestClient.post("#{@url}authkeys", { }, :content_type => :json, :accept => :json);
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
- RestClient.delete("#{@url}users/#{name}", :content_type => :json, :accept => :json);
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
- RestClient.put("#{@url}providers/#{name}", { :binding => binding }, :content_type => :json, :accept => :json);
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(provider)
113
- if provider == :self
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(broker.public_key)
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 cleanup
154
- providers.each do |provider|
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 gen_cert(provider)
176
- cert = OpenSSL::X509::Certificate.new(RestClient.post("#{@url}cert", :csr => generate_csr(provider)))
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
- def gen_key
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 get_broker
188
- cert = OpenSSL::X509::Certificate.new(RestClient.get("#{@url}cert"))
189
- log "got cert from broker #{digest cert} #{cert.subject}"
190
- cert
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 broker
194
- @broker ||= OpenSSL::X509::Certificate.new(RestClient.get("#{@url}cert"))
195
- end
300
+ def consume(arg = nil, &blk)
301
+ update_config
196
302
 
197
- def cert(provider = :self)
198
- @cert ||= {}
199
- @cert[provider] ||= OpenSSL::X509::Certificate.new(File.read(cert_path(provider))) rescue nil
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
- def key
204
- @key ||= OpenSSL::PKey::RSA.new(File.read(key_path)) rescue nil
205
- @key ||= gen_key
206
- end
207
-
208
- def cert_path(provider = :self)
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 key_path
217
- ".#{user_name}.key"
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
+
@@ -1,87 +1,178 @@
1
+ require 'optparse'
2
+ require 'socket'
3
+ require 'ostruct'
4
+ require 'uri'
5
+
1
6
  module Fenris
2
- module Command
3
- def self.run(command, *args)
4
- arg, name = args
7
+ class Command
8
+ def self.run(args)
5
9
  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 users\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 ] ]" ]
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
- when "users"
24
- client.users.each do |u|
25
- puts u["name"]
26
- end
27
- when "userdel"
28
- client.userdel(arg)
29
- puts "ok"
30
- when "useradd"
31
- new_user = client.useradd(arg)
32
- puts "New user created"
33
- puts "export FENRIS_USER='#{new_user["name"]}'"
34
- puts "export FENRIS_AUTHKEY='#{new_user["authkey"]}'"
35
- when "rekey"
36
- newkey = client.rekey
37
- puts "New Key Assigned:"
38
- puts "export FENRIS_AUTHKEY='#{newkey}'"
39
- when "cert"
40
- puts client.cert.to_text
41
- when "bind"
42
- client.bind(arg,name)
43
- when "add"
44
- client.add(arg)
45
- when "remove"
46
- client.remove(arg)
47
- when "info"
48
- printf "INFO:\n"
49
- printf " %s\n", client.user["name"]
50
- unless client.users.empty?
51
- puts "SUBACCOUNTS:"
52
- client.users.each { |c| printf " %-20s\n", c["name"] }
53
- end
54
- unless client.consumers.empty?
55
- puts "CLIENTS:"
56
- client.consumers.each { |c| printf " %-20s\n", c["name"] }
57
- end
58
- unless client.providers.empty?
59
- puts "SERVICES:"
60
- client.providers.each { |c| printf " %-20s %s\n", c["name"], c["location"] }
61
- end
62
- when "provide"
63
- external = "#{Socket.gethostname}:#{10001}"
64
- internal = arg
65
- Fenris::Base.provide(client, external, internal)
66
- when "consume"
67
- Fenris::Base.consume(client, arg, name)
68
- when "exec"
69
- client.quiet = true
70
- Fenris::Base.exec(client, *args)
71
- else
72
- puts command.inspect
73
- puts help
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
- rescue SystemExit
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
@@ -1,3 +1,3 @@
1
1
  module Fenris
2
- VERSION = "0.0.6"
2
+ VERSION = "0.0.8"
3
3
  end
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.6
4
+ version: 0.0.8
5
5
  segments:
6
6
  - 0
7
7
  - 0
8
- - 6
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-10-26 00:00:00.000000000 -07:00
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: &2153842880 !ruby/object:Gem::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: *2153842880
33
+ version_requirements: *2154437240
34
34
  - !ruby/object:Gem::Dependency
35
35
  name: rest-client
36
- requirement: &2153842060 !ruby/object:Gem::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: *2153842060
48
+ version_requirements: *2154436420
49
49
  - !ruby/object:Gem::Dependency
50
50
  name: multi_json
51
- requirement: &2153841300 !ruby/object:Gem::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: *2153841300
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/em.rb
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
@@ -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
-