zyre 0.3.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/Authentication.md +30 -0
- data/History.md +7 -0
- data/README.md +1 -1
- data/ext/zyre_ext/cert.c +504 -0
- data/ext/zyre_ext/event.c +17 -9
- data/ext/zyre_ext/extconf.rb +5 -0
- data/ext/zyre_ext/node.c +261 -22
- data/ext/zyre_ext/poller.c +6 -7
- data/ext/zyre_ext/zyre_ext.c +43 -1
- data/ext/zyre_ext/zyre_ext.h +19 -2
- data/lib/zyre.rb +38 -13
- data/lib/zyre/cert.rb +75 -0
- data/lib/zyre/event.rb +1 -0
- data/lib/zyre/event/stop.rb +5 -0
- data/lib/zyre/testing.rb +24 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/zyre/cert_spec.rb +218 -0
- data/spec/zyre/node_spec.rb +58 -0
- data/spec/zyre/testing_spec.rb +36 -0
- data/spec/zyre_spec.rb +12 -0
- metadata +21 -17
- metadata.gz.sig +0 -0
data/ext/zyre_ext/zyre_ext.h
CHANGED
@@ -10,14 +10,23 @@
|
|
10
10
|
#ifndef ZYRE_EXT_H_90322ABD
|
11
11
|
#define ZYRE_EXT_H_90322ABD
|
12
12
|
|
13
|
+
#include "extconf.h"
|
14
|
+
|
13
15
|
#include <ruby.h>
|
14
16
|
#include <ruby/intern.h>
|
15
17
|
#include <ruby/thread.h>
|
16
18
|
#include <ruby/encoding.h>
|
19
|
+
#include <ruby/version.h>
|
20
|
+
|
21
|
+
#ifdef HAVE_ZCERT_UNSET_META
|
22
|
+
# define CZMQ_BUILD_DRAFT_API 1
|
23
|
+
#endif
|
24
|
+
#ifdef HAVE_ZYRE_SET_BEACON_PEER_PORT
|
25
|
+
# define ZYRE_BUILD_DRAFT_API 1
|
26
|
+
#endif
|
17
27
|
|
18
28
|
#include "zyre.h"
|
19
29
|
#include "czmq.h"
|
20
|
-
#include "extconf.h"
|
21
30
|
|
22
31
|
#ifndef TRUE
|
23
32
|
# define TRUE 1
|
@@ -28,7 +37,8 @@
|
|
28
37
|
#endif
|
29
38
|
|
30
39
|
|
31
|
-
// For synthesized events
|
40
|
+
// For synthesized events, sizeof calculations, copied from the czmq and zyre
|
41
|
+
// source
|
32
42
|
struct _zyre_event_t {
|
33
43
|
char *type; // Event type as string
|
34
44
|
char *peer_uuid; // Sender UUID as string
|
@@ -40,6 +50,7 @@ struct _zyre_event_t {
|
|
40
50
|
};
|
41
51
|
|
42
52
|
|
53
|
+
|
43
54
|
/* --------------------------------------------------------------
|
44
55
|
* Declarations
|
45
56
|
* -------------------------------------------------------------- */
|
@@ -74,6 +85,9 @@ extern VALUE rzyre_mZyre;
|
|
74
85
|
extern VALUE rzyre_cZyreNode;
|
75
86
|
extern VALUE rzyre_cZyreEvent;
|
76
87
|
extern VALUE rzyre_cZyrePoller;
|
88
|
+
extern VALUE rzyre_cZyreCert;
|
89
|
+
|
90
|
+
extern zactor_t *auth_actor;
|
77
91
|
|
78
92
|
|
79
93
|
/* --------------------------------------------------------------
|
@@ -83,6 +97,7 @@ extern VALUE rzyre_cZyrePoller;
|
|
83
97
|
#define IsZyreNode( obj ) rb_obj_is_kind_of( (obj), rzyre_cZyreNode )
|
84
98
|
#define IsZyreEvent( obj ) rb_obj_is_kind_of( (obj), rzyre_cZyreEvent )
|
85
99
|
#define IsZyrePoller( obj ) rb_obj_is_kind_of( (obj), rzyre_cZyrePoller )
|
100
|
+
#define IsZyreCert( obj ) rb_obj_is_kind_of( (obj), rzyre_cZyreCert )
|
86
101
|
|
87
102
|
/* --------------------------------------------------------------
|
88
103
|
* Utility functions
|
@@ -98,8 +113,10 @@ extern void Init_zyre_ext _(( void ));
|
|
98
113
|
extern void rzyre_init_node _(( void ));
|
99
114
|
extern void rzyre_init_event _(( void ));
|
100
115
|
extern void rzyre_init_poller _(( void ));
|
116
|
+
extern void rzyre_init_cert _(( void ));
|
101
117
|
|
102
118
|
extern zyre_t * rzyre_get_node _(( VALUE ));
|
119
|
+
extern zcert_t * rzyre_get_cert _(( VALUE ));
|
103
120
|
|
104
121
|
#endif /* end of include guard: ZYRE_EXT_H_90322ABD */
|
105
122
|
|
data/lib/zyre.rb
CHANGED
@@ -1,35 +1,28 @@
|
|
1
1
|
# -*- ruby -*-
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require 'set'
|
4
5
|
require 'loggability'
|
5
6
|
|
6
7
|
require_relative 'zyre_ext'
|
7
8
|
|
8
9
|
|
9
|
-
|
10
|
+
#--
|
11
|
+
# See also: ext/zyre_ext/zyre_ext.c
|
10
12
|
module Zyre
|
11
13
|
extend Loggability
|
12
14
|
|
13
15
|
|
14
16
|
# Gem version (semver)
|
15
|
-
VERSION = '0.
|
17
|
+
VERSION = '0.4.0'
|
16
18
|
|
17
19
|
|
18
20
|
# Set up a logger for Zyre classes
|
19
21
|
log_as :zyre
|
20
22
|
|
21
23
|
|
22
|
-
|
23
|
-
|
24
|
-
### Specify a +timeout+ of -1 to wait indefinitely. The timeout is in floating-point
|
25
|
-
### seconds.
|
26
|
-
###
|
27
|
-
### Raises an Interrupt if the call is interrupted or the ZMQ context is destroyed.
|
28
|
-
def self::wait( *nodes, timeout: -1 )
|
29
|
-
nodes = nodes.flatten
|
30
|
-
return nil if nodes.empty?
|
31
|
-
return self.wait2( nodes, timeout )
|
32
|
-
end
|
24
|
+
@whitelisted_ips = Set.new
|
25
|
+
@blacklisted_ips = Set.new
|
33
26
|
|
34
27
|
|
35
28
|
### If the given +key+ is a Symbol, transform it into an RFC822-style header key. If
|
@@ -50,4 +43,36 @@ module Zyre
|
|
50
43
|
transform_values {|v| v.to_s.encode('us-ascii') }
|
51
44
|
end
|
52
45
|
|
46
|
+
|
47
|
+
### Allow (whitelist) a list of IP +addresses+. For NULL, all clients from
|
48
|
+
### these addresses will be accepted. For PLAIN and CURVE, they will be
|
49
|
+
### allowed to continue with authentication. You can call this method
|
50
|
+
### multiple times to whitelist more IP addresses. If you whitelist one
|
51
|
+
### or more addresses, any non-whitelisted addresses are treated as
|
52
|
+
### blacklisted:
|
53
|
+
def self::allow( *addresses )
|
54
|
+
@whitelisted_ips.merge( addresses )
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
### Deny (blacklist) a list of IP +addresses+. For all security mechanisms,
|
59
|
+
### this rejects the connection without any further authentication. Use
|
60
|
+
### either a whitelist, or a blacklist, not not both. If you define both
|
61
|
+
### a whitelist and a blacklist, only the whitelist takes effect:
|
62
|
+
def self::deny( *addresses )
|
63
|
+
@blacklisted_ips.merge( addresses )
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
### Returns +true+ if the underlying Czmq library was built with draft APIs.
|
68
|
+
def self::has_draft_czmq_apis?
|
69
|
+
return Zyre::BUILT_WITH_DRAFT_CZMQ_API ? true : false
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
### Returns +true+ if the underlying Zyre library was built with draft APIs.
|
74
|
+
def self::has_draft_apis?
|
75
|
+
return Zyre::BUILT_WITH_DRAFT_API ? true : false
|
76
|
+
end
|
77
|
+
|
53
78
|
end # module Zyre
|
data/lib/zyre/cert.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'loggability'
|
5
|
+
|
6
|
+
require 'zyre' unless defined?( Zyre )
|
7
|
+
|
8
|
+
|
9
|
+
#--
|
10
|
+
# See also: ext/zyre_ext/cert.c
|
11
|
+
class Zyre::Cert
|
12
|
+
extend Loggability
|
13
|
+
|
14
|
+
|
15
|
+
# The placeholder key that is set as the secret key for a public certificate.
|
16
|
+
EMPTY_KEY = "\x00" * 32
|
17
|
+
|
18
|
+
|
19
|
+
# Use the Zyre module's logger
|
20
|
+
log_to :zyre
|
21
|
+
|
22
|
+
|
23
|
+
# Set up some more Rubyish aliases
|
24
|
+
alias_method :private_key, :secret_key
|
25
|
+
alias_method :==, :eql?
|
26
|
+
|
27
|
+
|
28
|
+
### Fetch the value for the cert metadata with the given +name+.
|
29
|
+
def []( name )
|
30
|
+
return self.meta( name.to_s )
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
### Set the value for the cert metadata with the given +name+ to +value+.
|
35
|
+
def []=( name, value )
|
36
|
+
return self.set_meta( name.to_s, value.to_s )
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
### Return the metadata from the cert as a Hash.
|
41
|
+
def meta_hash
|
42
|
+
hash = self.meta_keys.each_with_object( {} ) do |key, h|
|
43
|
+
h[ key ] = self[ key ]
|
44
|
+
end
|
45
|
+
hash.freeze
|
46
|
+
return hash
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
### Delete the value for the cert metadata with the given +name+. Requires
|
51
|
+
### CZMQ to have been built with Draft APIs.
|
52
|
+
def delete( name )
|
53
|
+
name = name.to_s
|
54
|
+
|
55
|
+
deleted_val = self[ name ]
|
56
|
+
self.unset_meta( name )
|
57
|
+
|
58
|
+
return deleted_val
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
### Returns +true+ if the certificate has a secret key.
|
63
|
+
def have_secret_key?
|
64
|
+
return self.secret_key != EMPTY_KEY
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
### Apply the certificate to the specified +zyre_node+, i.e. use the
|
69
|
+
### cert for CURVE security. If the receiving certificate doesn't have a
|
70
|
+
### private key, an exception will be raised.
|
71
|
+
def apply( zyre_node )
|
72
|
+
return zyre_node.zcert = self
|
73
|
+
end
|
74
|
+
|
75
|
+
end # class Zyre::Cert
|
data/lib/zyre/event.rb
CHANGED
@@ -25,6 +25,7 @@ class Zyre::Event
|
|
25
25
|
autoload :Exit, 'zyre/event/exit'
|
26
26
|
autoload :Join, 'zyre/event/join'
|
27
27
|
autoload :Leave, 'zyre/event/leave'
|
28
|
+
autoload :Leader, 'zyre/event/leader'
|
28
29
|
autoload :Shout, 'zyre/event/shout'
|
29
30
|
autoload :Silent, 'zyre/event/silent'
|
30
31
|
autoload :Stop, 'zyre/event/stop'
|
data/lib/zyre/event/stop.rb
CHANGED
data/lib/zyre/testing.rb
CHANGED
@@ -221,6 +221,18 @@ module Zyre::Testing
|
|
221
221
|
end
|
222
222
|
|
223
223
|
|
224
|
+
### Generate a LEADER event.
|
225
|
+
def leader( **overrides )
|
226
|
+
uuid = overrides.delete( :peer_uuid ) || self.peer_uuid
|
227
|
+
config = {
|
228
|
+
peer_name: self.peer_name,
|
229
|
+
group: self.group
|
230
|
+
}.merge( overrides )
|
231
|
+
|
232
|
+
return Zyre::Event.synthesize( :leader, uuid, **config )
|
233
|
+
end
|
234
|
+
|
235
|
+
|
224
236
|
### Generate an EXIT event.
|
225
237
|
def exit( **overrides )
|
226
238
|
uuid = overrides.delete( :peer_uuid ) || self.peer_uuid
|
@@ -231,6 +243,17 @@ module Zyre::Testing
|
|
231
243
|
return Zyre::Event.synthesize( :exit, uuid, **config )
|
232
244
|
end
|
233
245
|
|
246
|
+
|
247
|
+
### Generate a STOP event.
|
248
|
+
def stop( **overrides )
|
249
|
+
uuid = overrides.delete( :peer_uuid ) || self.peer_uuid
|
250
|
+
config = {
|
251
|
+
peer_name: self.peer_name
|
252
|
+
}.merge( overrides )
|
253
|
+
|
254
|
+
return Zyre::Event.synthesize( :stop, uuid, **config )
|
255
|
+
end
|
256
|
+
|
234
257
|
end # class EventFactory
|
235
258
|
|
236
259
|
|
@@ -260,6 +283,7 @@ module Zyre::Testing
|
|
260
283
|
def started_node( name=nil )
|
261
284
|
node = Zyre::Node.new( name )
|
262
285
|
node.endpoint = 'inproc://node-test-%s' % [ SecureRandom.hex(16) ]
|
286
|
+
|
263
287
|
yield( node ) if block_given?
|
264
288
|
|
265
289
|
if @gossip_endpoint
|
data/spec/spec_helper.rb
CHANGED
@@ -55,6 +55,10 @@ RSpec.configure do |config|
|
|
55
55
|
Zyre::Testing.check_fdmax
|
56
56
|
end
|
57
57
|
|
58
|
+
config.filter_run_excluding( :draft_api ) unless Zyre.has_draft_apis?
|
59
|
+
config.filter_run_excluding( :czmq_draft_api ) unless Zyre.has_draft_czmq_apis?
|
60
|
+
config.filter_run_excluding( :no_czmq_draft_api ) if Zyre.has_draft_czmq_apis?
|
61
|
+
|
58
62
|
config.include( Zyre::Testing )
|
59
63
|
config.include( Loggability::SpecHelpers )
|
60
64
|
end
|
@@ -0,0 +1,218 @@
|
|
1
|
+
#!/usr/bin/env rspec -cfd
|
2
|
+
|
3
|
+
require_relative '../spec_helper'
|
4
|
+
|
5
|
+
require 'tempfile'
|
6
|
+
|
7
|
+
require 'zyre/cert'
|
8
|
+
|
9
|
+
|
10
|
+
RSpec.describe( Zyre::Cert ) do
|
11
|
+
|
12
|
+
let( :cert_file ) { Tempfile.new('zyre_cert_test') }
|
13
|
+
|
14
|
+
|
15
|
+
it "can be created in-memory" do
|
16
|
+
expect( described_class.new ).to be_a( described_class )
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
it "can be created from a keypair" do
|
21
|
+
cert = described_class.new
|
22
|
+
|
23
|
+
result = described_class.from( cert.public_key, cert.private_key )
|
24
|
+
|
25
|
+
expect( result ).to be_a( described_class )
|
26
|
+
expect( result ).to eq( cert )
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
it "can be saved to and loaded from a file" do
|
31
|
+
cert = described_class.new
|
32
|
+
cert.save( cert_file.path )
|
33
|
+
|
34
|
+
result = described_class.load( cert_file.path )
|
35
|
+
|
36
|
+
expect( result ).to be_a( described_class )
|
37
|
+
expect( result ).to eq( cert )
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
it "can be saved as a public cert to and loaded from a file" do
|
42
|
+
cert = described_class.new
|
43
|
+
cert.save_public( cert_file.path )
|
44
|
+
|
45
|
+
result = described_class.load( cert_file.path )
|
46
|
+
|
47
|
+
expect( result ).to be_a( described_class )
|
48
|
+
expect( result.public_key ).to eq( cert.public_key )
|
49
|
+
expect( result.secret_key ).to eq( Zyre::Cert::EMPTY_KEY )
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
it "can be saved as a secret cert to and loaded from a file" do
|
54
|
+
cert = described_class.new
|
55
|
+
cert.save_secret( cert_file.path )
|
56
|
+
|
57
|
+
result = described_class.load( cert_file.path )
|
58
|
+
|
59
|
+
expect( result ).to be_a( described_class )
|
60
|
+
expect( result ).to eq( cert )
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
it "knows what its public key is" do
|
65
|
+
cert = described_class.new
|
66
|
+
|
67
|
+
key = cert.public_key
|
68
|
+
|
69
|
+
expect( key.encoding ).to eq( Encoding::ASCII_8BIT )
|
70
|
+
expect( key.bytesize ).to eq( 32 )
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
it "knows what its secret key is" do
|
75
|
+
cert = described_class.new
|
76
|
+
|
77
|
+
key = cert.secret_key
|
78
|
+
|
79
|
+
expect( key.encoding ).to eq( Encoding::ASCII_8BIT )
|
80
|
+
expect( key.bytesize ).to eq( 32 )
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
it "knows what its Z85-armored public key is" do
|
85
|
+
cert = described_class.new
|
86
|
+
|
87
|
+
key = cert.public_txt
|
88
|
+
|
89
|
+
expect( key.encoding ).to eq( Encoding::US_ASCII )
|
90
|
+
expect( key.length ).to eq( 40 )
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
it "knows what its Z85-armored secret key is" do
|
95
|
+
cert = described_class.new
|
96
|
+
|
97
|
+
key = cert.secret_txt
|
98
|
+
|
99
|
+
expect( key.encoding ).to eq( Encoding::US_ASCII )
|
100
|
+
expect( key.length ).to eq( 40 )
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
it "can set metadata on the cert" do
|
105
|
+
cert = described_class.new
|
106
|
+
|
107
|
+
cert[ :username ] = 'timmy'
|
108
|
+
|
109
|
+
expect( cert['username'] ).to eq( 'timmy' )
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
it "stringifies non-string metadata on the cert" do
|
114
|
+
cert = described_class.new
|
115
|
+
|
116
|
+
cert[ :euid ] = 12
|
117
|
+
|
118
|
+
expect( cert[:euid] ).to eq( '12' )
|
119
|
+
end
|
120
|
+
|
121
|
+
|
122
|
+
it "fetches non-existant values as nil" do
|
123
|
+
cert = described_class.new
|
124
|
+
|
125
|
+
expect( cert[:nonexistant_key] ).to be_nil
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
it "silently ignores overwrites of a metadata value if not built with Drafts", :no_czmq_draft_api do
|
130
|
+
cert = described_class.new
|
131
|
+
|
132
|
+
cert.set_meta( 'foo', 'bar' )
|
133
|
+
cert.set_meta( 'foo', 'baz' )
|
134
|
+
|
135
|
+
expect( cert.meta('foo') ).to eq( 'bar' )
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
it "can overwrite a metadata value if built with Drafts", :czmq_draft_api do
|
140
|
+
cert = described_class.new
|
141
|
+
|
142
|
+
cert.set_meta( 'foo', 'bar' )
|
143
|
+
cert.set_meta( 'foo', 'baz' )
|
144
|
+
|
145
|
+
expect( cert.meta('foo') ).to eq( 'baz' )
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
it "knows what metadata has been set on the cert" do
|
150
|
+
cert = described_class.new
|
151
|
+
|
152
|
+
cert[ :euid ] = 0
|
153
|
+
cert[ :username ] = 'jrandom'
|
154
|
+
cert[ 'firstname' ] = 'James'
|
155
|
+
cert[ :lastname ] = 'Random'
|
156
|
+
cert[ 'key 2' ] = 'cf67c750-c704-4ef7-ab83-ecb2cd2e326c'
|
157
|
+
|
158
|
+
expect( cert.meta_keys ).
|
159
|
+
to contain_exactly( 'euid', 'username', 'firstname', 'lastname', 'key 2' )
|
160
|
+
end
|
161
|
+
|
162
|
+
|
163
|
+
it "can delete one of its metadata key-value pairs", :czmq_draft_api do
|
164
|
+
cert = described_class.new
|
165
|
+
|
166
|
+
cert[ :euid ] = 0
|
167
|
+
cert[ :username ] = 'jrandom'
|
168
|
+
cert[ 'firstname' ] = 'James'
|
169
|
+
cert[ :lastname ] = 'Random'
|
170
|
+
cert[ 'key 2' ] = 'cf67c750-c704-4ef7-ab83-ecb2cd2e326c'
|
171
|
+
|
172
|
+
cert.delete( 'lastname' )
|
173
|
+
|
174
|
+
expect( cert.meta_keys ).
|
175
|
+
to contain_exactly( 'euid', 'username', 'firstname', 'key 2' )
|
176
|
+
end
|
177
|
+
|
178
|
+
|
179
|
+
it "can return all of its metadata as a Hash" do
|
180
|
+
cert = described_class.new
|
181
|
+
|
182
|
+
cert[ :euid ] = 0
|
183
|
+
cert[ :username ] = 'jrandom'
|
184
|
+
cert[ 'firstname' ] = 'James'
|
185
|
+
cert[ :lastname ] = 'Random'
|
186
|
+
cert[ 'key 2' ] = 'cf67c750-c704-4ef7-ab83-ecb2cd2e326c'
|
187
|
+
|
188
|
+
expect( cert.meta_hash ).to eq({
|
189
|
+
'euid' => '0',
|
190
|
+
'username' => 'jrandom',
|
191
|
+
'firstname' => 'James',
|
192
|
+
'lastname' => 'Random',
|
193
|
+
'key 2' => 'cf67c750-c704-4ef7-ab83-ecb2cd2e326c',
|
194
|
+
})
|
195
|
+
end
|
196
|
+
|
197
|
+
|
198
|
+
it "can be applied to a Zyre node", :draft_apis do
|
199
|
+
node = instance_double( Zyre::Node )
|
200
|
+
cert = Zyre::Cert.new
|
201
|
+
|
202
|
+
expect( node ).to receive( :zcert= ).with( cert )
|
203
|
+
cert.apply( node )
|
204
|
+
end
|
205
|
+
|
206
|
+
|
207
|
+
it "can be duplicated" do
|
208
|
+
cert = described_class.new
|
209
|
+
cert[ :node_id ] = '05343500-f908-4903-9441-e648eb1754ec'
|
210
|
+
cert[ :node_order ] = 1
|
211
|
+
|
212
|
+
other = cert.dup
|
213
|
+
|
214
|
+
expect( other.object_id ).not_to eq( cert.object_id )
|
215
|
+
end
|
216
|
+
|
217
|
+
|
218
|
+
end
|