fenris 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/bin/fenris ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
4
+ $LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
5
+
6
+ require 'fenris/em'
7
+ require 'fenris/command'
8
+ require 'fenris/client'
9
+
10
+ args = ARGV.dup
11
+ ARGV.clear
12
+ command = args.shift.strip rescue 'help'
13
+ #arg = args.shift.strip rescue nil
14
+
15
+ Fenris::Command.run command, *args
16
+
@@ -0,0 +1,150 @@
1
+ require 'restclient'
2
+ require 'json'
3
+ require 'openssl'
4
+
5
+ module Fenris
6
+ class Client
7
+ def debug(message)
8
+ puts "DEBUG: #{message}" if ENV['DEBUG']
9
+ end
10
+
11
+ def log(message)
12
+ puts "LOG: #{message}"
13
+ end
14
+
15
+ def initialize(url)
16
+ @url = url
17
+ end
18
+
19
+ def update(ip, port)
20
+ RestClient.put("#{@url}", { :ip => ip, :port => port }, :content_type => :json, :accept => :json)
21
+ end
22
+
23
+ def user
24
+ @user ||= JSON.parse RestClient.get("#{@url}", :content_type => :json, :accept => :json)
25
+ end
26
+
27
+ def consumers
28
+ @consumers ||= JSON.parse RestClient.get("#{@url}consumers", :content_type => :json, :accept => :json);
29
+ end
30
+
31
+ def providers
32
+ @providers ||= JSON.parse RestClient.get("#{@url}providers", :content_type => :json, :accept => :json);
33
+ end
34
+
35
+ def user_name
36
+ user["name"]
37
+ end
38
+
39
+ def route
40
+ "#{user["ip"]}:#{user["port"]}" if user["ip"]
41
+ end
42
+
43
+ def remove(name)
44
+ RestClient.delete("#{@url}consumers/#{name}");
45
+ end
46
+
47
+ def add(name)
48
+ RestClient.post("#{@url}consumers", { :name => name }, :content_type => :json, :accept => :json);
49
+ end
50
+
51
+ def bind(name, binding)
52
+ RestClient.put("#{@url}providers/#{name}", { :binding => binding }, :content_type => :json, :accept => :json);
53
+ end
54
+
55
+ def digest obj
56
+ OpenSSL::Digest::SHA1.new(obj.to_der).to_s
57
+ end
58
+
59
+ def generate_csr
60
+ subject = OpenSSL::X509::Name.parse "/DC=org/DC=fenris/CN=#{user_name}"
61
+ digest = OpenSSL::Digest::SHA1.new
62
+ req = OpenSSL::X509::Request.new
63
+ req.version = 0
64
+ req.subject = subject
65
+ req.public_key = key.public_key
66
+ req.sign(key, digest)
67
+ log "generating csr #{digest req} #{subject}"
68
+ req
69
+ end
70
+
71
+ def get_cn(cert)
72
+ cert.subject.to_a.detect { |a,b,c| a == "CN" }[1] rescue nil
73
+ end
74
+
75
+ def validate_peer(pem, peer_connection = nil, peer_name = nil)
76
+ consumer_cert = OpenSSL::X509::Certificate.new(pem)
77
+ cert_cn = get_cn(consumer_cert)
78
+ valid_peer_names = [ peer_name ] if peer_name
79
+ valid_peer_names ||= consumers.map { |c| c["name"] }
80
+ cn_ok = !!valid_peer_names.detect { |name| name == cert_cn }
81
+ cert_ok = !!consumer_cert.verify(broker.public_key)
82
+ log "Consumer Cert CN '#{cert_cn}' in allowed_list? #{cn_ok}"
83
+ log "Consumer Cert Signed By Broker? '#{cert_ok}'"
84
+ result = cn_ok and cert_ok
85
+ unless result
86
+ log "Certificate verification failed. connection closed [#{cn_ok}] [#{cert_ok}]"
87
+ peer_connection.close_connection if peer_connection
88
+ end
89
+ result
90
+ end
91
+
92
+ def cleanup
93
+ providers.each do |provider|
94
+ log "Deleting socket '#{provider["binding"]}'."
95
+ File.delete provider["binding"] if File.exists? provider["binding"]
96
+ end
97
+ [ cert_path, key_path ].each do |f|
98
+ if File.exists? f
99
+ log "Deleting file #{f}"
100
+ File.delete f
101
+ end
102
+ end
103
+ end
104
+
105
+ def save_keys
106
+ File.open(cert_path,"w") { |f| f.write cert.to_pem } unless File.exists? cert_path
107
+ File.open(key_path,"w") { |f| f.write key.to_pem } unless File.exists? key_path
108
+ end
109
+
110
+ def gen_cert
111
+ cert = OpenSSL::X509::Certificate.new(RestClient.post("#{@url}cert", :csr => generate_csr))
112
+ log "new cert received #{digest cert}"
113
+ cert
114
+ end
115
+
116
+ def gen_key
117
+ key = OpenSSL::PKey::RSA.new(2048)
118
+ log "new rsa key generated #{digest key}"
119
+ key
120
+ end
121
+
122
+ def get_broker
123
+ cert = OpenSSL::X509::Certificate.new(RestClient.get("#{@url}cert"))
124
+ log "got cert from broker #{digest cert} #{cert.subject}"
125
+ cert
126
+ end
127
+
128
+ def broker
129
+ @broker ||= OpenSSL::X509::Certificate.new(RestClient.get("#{@url}cert"))
130
+ end
131
+
132
+ def cert
133
+ @cert ||= OpenSSL::X509::Certificate.new(File.read(cert_path)) rescue nil
134
+ @cert ||= gen_cert
135
+ end
136
+
137
+ def key
138
+ @key ||= OpenSSL::PKey::RSA.new(File.read(key_path)) rescue nil
139
+ @key ||= gen_key
140
+ end
141
+
142
+ def cert_path
143
+ ".#{user_name}.cert"
144
+ end
145
+
146
+ def key_path
147
+ ".#{user_name}.key"
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,51 @@
1
+ module Fenris
2
+ module Command
3
+ def self.run(command, arg=nil, name=nil)
4
+ begin
5
+ client = Fenris::Client.new(ENV['FENRIS_URL']);
6
+ help = [ "Usage: fenris help\n",
7
+ " fenris info\n",
8
+ " fenris bind PROVIDER BINDING\n",
9
+ " fenris add CONSUMER\n",
10
+ " fenris remove CONSUMER\n",
11
+ " fenris serve BINDING\n",
12
+ " fenris connect [ USER [ BINDING ] ]" ]
13
+ case command
14
+ when "cert"
15
+ puts client.cert.to_text
16
+ when "bind"
17
+ client.bind(arg,name)
18
+ when "add"
19
+ client.add(arg)
20
+ when "remove"
21
+ client.remove(arg)
22
+ when "info"
23
+ puts "INFO:"
24
+ puts " #{client.user["name"]} #{client.route}"
25
+ unless client.consumers.empty?
26
+ puts "CLIENTS:"
27
+ client.consumers.each { |c| puts " #{c["name"]} (#{c["binding"]})" }
28
+ end
29
+ unless client.providers.empty?
30
+ puts "SERVICES:"
31
+ client.providers.each { |c| puts " #{c["binding"] || "unbound"} #{c["name"]} (#{c["provider"]}) #{c["ip"]} #{c["port"]}" }
32
+ end
33
+ when "serve"
34
+ from = 10001
35
+ to = arg
36
+ Fenris::Base.serve(client, from, to)
37
+ when "connect"
38
+ Fenris::Base.connect(client, arg, name)
39
+ else
40
+ puts command.inspect
41
+ puts help
42
+ end
43
+ exit
44
+ end
45
+ rescue SystemExit
46
+ rescue RestClient::ResourceNotFound
47
+ puts "Resource Not Found"
48
+ exit
49
+ end
50
+ end
51
+ end
data/lib/fenris/em.rb ADDED
@@ -0,0 +1,145 @@
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 ssl_verify_peer(pem)
27
+ @verify ||= @validator.call(pem)
28
+ end
29
+
30
+ def unbind
31
+ @unbound = true
32
+ puts "DEBUG: unbind #{@signature}"
33
+ EM::stop if @signature < 3 ## this is for attach($stdin)
34
+ @peer.close_connection_after_writing rescue nil
35
+ close_connection
36
+ end
37
+
38
+ def ssl_handshake_completed
39
+ @post_ssl.call
40
+ end
41
+
42
+ def proxy(peer)
43
+ peer.close if @unbound
44
+ @peer.each { |d| peer.push d}
45
+ @peer = peer
46
+ end
47
+
48
+ def receive_data data
49
+ @peer.push data
50
+ end
51
+ end
52
+
53
+ module Base
54
+ extend self
55
+
56
+ def producer_server(client, from, to)
57
+ return producer_server_stdio(client, from, to) if to == "--"
58
+ EventMachine::__send__ *mkbinding(:start_server, from), Fenris::Connection do |consumer|
59
+ client.log "New connection - begin ssl handshake"
60
+ consumer.validate_peer { |pem| client.validate_peer pem }
61
+ consumer.begin_ssl :key_file => client.key_path , :cert_file => client.cert_path do
62
+ client.log "SSL complete - open local connection"
63
+ EventMachine::__send__ *mkbinding(:connect, to), Fenris::Connection do |producer|
64
+ client.log "start proxying"
65
+ producer.proxy consumer; consumer.proxy producer
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ def producer_server_stdio(client, from, to)
72
+ EventMachine::__send__ *mkbinding(:connect, to), Fenris::Connection do |producer|
73
+ EventMachine::__send__ *mkbinding(:start_server, from), Fenris::Connection do |consumer|
74
+ client.log "stdio connection - begin ssl handshake"
75
+ consumer.validate_peer { |pem| client.validate_peer pem }
76
+ consumer.begin_ssl :key_file => client.key_path , :cert_file => client.cert_path do
77
+ client.log "SSL complete - start proxying"
78
+ producer.proxy consumer; consumer.proxy producer
79
+ end
80
+ end
81
+ end
82
+ end
83
+
84
+ def serve(client, listen_port, to)
85
+ at_exit { client.cleanup }
86
+
87
+ EventMachine::run do
88
+ client.save_keys
89
+ client.update "0.0.0.0", listen_port
90
+ from = "0.0.0.0:#{listen_port}"
91
+ client.log "Serving port #{to} on #{from}"
92
+ producer_server(client, from, to)
93
+ end
94
+ end
95
+
96
+ def mkbinding(action, binding)
97
+ if binding =~ /^:?(\d+)$/
98
+ [ action, "0.0.0.0", $1.to_i ]
99
+ elsif binding =~ /^(.+):(\d+)/
100
+ [ action, $1, $2.to_i ]
101
+ elsif binding == "--"
102
+ [ :attach, $stdin ]
103
+ else
104
+ [ action, binding ]
105
+ end
106
+ end
107
+
108
+ ## really want to unify all these :(
109
+ def consumer_connect(client, consumer, provider_name, provider)
110
+ EventMachine::__send__ *mkbinding(:start_server, consumer), Fenris::Connection do |consumer|
111
+ client.log "New connection: opening connection to the server"
112
+ EventMachine::__send__ *mkbinding(:connect, provider), Fenris::Connection do |provider|
113
+ client.log "Connection to the server made, starting ssl"
114
+ provider.validate_peer { |pem| client.validate_peer pem, consumer, provider_name }
115
+ provider.begin_ssl :key_file => client.key_path , :cert_file => client.cert_path do
116
+ client.log "SSL complete - start proxying"
117
+ provider.proxy consumer; consumer.proxy provider
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ def connect(client, overide_provider = nil, override_binding = nil)
124
+ at_exit { client.cleanup }
125
+
126
+ client.save_keys
127
+
128
+ abort "No providers" if client.providers.empty?
129
+
130
+ providers = client.providers.reject { |p| overide_provider && p["name"] != overide_provider }
131
+
132
+ abort "No provider named #{overide_provider}" if providers.empty?
133
+ abort "Can only pass a binding for a single provider" if override_binding && providers.length != 1
134
+
135
+ EventMachine::run do
136
+ providers.each do |p|
137
+ binding = override_binding || p["binding"]
138
+ consumer_connect(client, binding, p["name"], "#{p["ip"]}:#{p["port"]}")
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
144
+
145
+
@@ -0,0 +1,3 @@
1
+ module Fenris
2
+ VERSION = "0.0.1"
3
+ end
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fenris
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ prerelease: false
10
+ platform: ruby
11
+ authors:
12
+ - Orion Henry
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+ date: 2011-10-18 00:00:00.000000000 -07:00
17
+ default_executable:
18
+ dependencies:
19
+ - !ruby/object:Gem::Dependency
20
+ name: eventmachine
21
+ requirement: &2168813480 !ruby/object:Gem::Requirement
22
+ none: false
23
+ requirements:
24
+ - - ! '>='
25
+ - !ruby/object:Gem::Version
26
+ version: 0.12.10
27
+ segments:
28
+ - 0
29
+ - 12
30
+ - 10
31
+ type: :runtime
32
+ prerelease: false
33
+ version_requirements: *2168813480
34
+ - !ruby/object:Gem::Dependency
35
+ name: rest-client
36
+ requirement: &2168811760 !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ! '>='
40
+ - !ruby/object:Gem::Version
41
+ version: 1.6.7
42
+ segments:
43
+ - 1
44
+ - 6
45
+ - 7
46
+ type: :runtime
47
+ prerelease: false
48
+ version_requirements: *2168811760
49
+ - !ruby/object:Gem::Dependency
50
+ name: multi_json
51
+ requirement: &2168810220 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ! '>='
55
+ - !ruby/object:Gem::Version
56
+ version: 1.0.3
57
+ segments:
58
+ - 1
59
+ - 0
60
+ - 3
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: *2168810220
64
+ description: An authentication and service location service.
65
+ email: orion.henry@gmail.com
66
+ executables:
67
+ - fenris
68
+ extensions: []
69
+ extra_rdoc_files: []
70
+ files:
71
+ - bin/fenris
72
+ - lib/fenris/client.rb
73
+ - lib/fenris/command.rb
74
+ - lib/fenris/em.rb
75
+ - lib/fenris/version.rb
76
+ has_rdoc: true
77
+ homepage: http://github.com/orionz/fenris
78
+ licenses: []
79
+ post_install_message:
80
+ rdoc_options: []
81
+ require_paths:
82
+ - lib
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ! '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ segments:
90
+ - 0
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ none: false
93
+ requirements:
94
+ - - ! '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ segments:
98
+ - 0
99
+ requirements: []
100
+ rubyforge_project:
101
+ rubygems_version: 1.3.7
102
+ signing_key:
103
+ specification_version: 3
104
+ summary: An authentication and service location service.
105
+ test_files: []