fenris 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/fenris +16 -0
- data/lib/fenris/client.rb +150 -0
- data/lib/fenris/command.rb +51 -0
- data/lib/fenris/em.rb +145 -0
- data/lib/fenris/version.rb +3 -0
- metadata +105 -0
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
|
+
|
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: []
|