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 +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: []
|