zyre 0.3.1 → 0.4.0
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.
- 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
|