fenris 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: []