fluent-plugin-secure-forward 0.0.3 → 0.0.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 616e3ee6ce26495d3cae028b95f875452e65b281
4
- data.tar.gz: c2c1e3423af3a2659389b3ed3a534876b56ad9d3
3
+ metadata.gz: b51f31fdd96e025c02cc760ff62119649c41822b
4
+ data.tar.gz: 8e6173fe782e3fe9f1cb5f919c8f6eac6f43c455
5
5
  SHA512:
6
- metadata.gz: d33ca023780967c1d5e543dd30da3beea56c9cddc53ad43b346f19513fa72a00fb35c48797336910b6455de39b14e3e32212f4250073c28ede2d1fddbbfcc077
7
- data.tar.gz: 0353931f0f0308dc51719e24628cf96c9c01f53baba40ee087b5dd0f09f42c5bc7492475fe1434e15cc4706fbc0335f3728ca6f614b5a100fe98536bc332fb71
6
+ metadata.gz: f90bd6309c2b69c62133efe58fd7732c816c1256086ed681198dcc66d949095d3d3bc86fb78fc4e76ad7392ca1fe591d301bab6488a493aa81750eba266d76d1
7
+ data.tar.gz: ab9cef4259d0071e81fefbe389ffa4840a80bcc5f621f7b1e82bc79781b1278728c124807df925bec0ebfc4fc3e0405d5a6f98cae8331b864247fd6830d9b022
data/README.md CHANGED
@@ -126,7 +126,7 @@ Minimal configurations like this:
126
126
  </server>
127
127
  </match>
128
128
 
129
- At this version (v0.0.x), only one `<server>` section can be specified.
129
+ When specified 2 or more `<server>`, this plugin uses these nodes in simple round-robin order.
130
130
 
131
131
  If server requires username/password, set `username` and `password` in `<server>` section:
132
132
 
@@ -135,10 +135,15 @@ If server requires username/password, set `username` and `password` in `<server>
135
135
  shared_key secret_string
136
136
  self_hostname client.fqdn.local
137
137
  <server>
138
- host server.fqdn.local
138
+ host first.fqdn.local
139
139
  username repeatedly
140
140
  password sushi
141
141
  </server>
142
+ <server>
143
+ host second.fqdn.local
144
+ username sasatatsu
145
+ password karaage
146
+ </server>
142
147
  </match>
143
148
 
144
149
  ## Senario (developer document)
data/Rakefile CHANGED
@@ -1,2 +1,11 @@
1
1
  #!/usr/bin/env rake
2
2
  require "bundler/gem_tasks"
3
+
4
+ require 'rake/testtask'
5
+ Rake::TestTask.new(:test) do |test|
6
+ test.libs << 'lib' << 'test'
7
+ test.pattern = 'test/**/test_*.rb'
8
+ test.verbose = true
9
+ end
10
+
11
+ task :default => :test
data/example/client.conf CHANGED
@@ -9,5 +9,11 @@
9
9
  <server>
10
10
  host localhost
11
11
  </server>
12
+ <server>
13
+ host localhost
14
+ </server>
15
+ <server>
16
+ host localhost
17
+ </server>
12
18
  flush_interval 1s
13
19
  </match>
@@ -1,7 +1,7 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  Gem::Specification.new do |gem|
3
3
  gem.name = "fluent-plugin-secure-forward"
4
- gem.version = "0.0.3"
4
+ gem.version = "0.0.4"
5
5
  gem.authors = ["TAGOMORI Satoshi"]
6
6
  gem.email = ["tagomoris@gmail.com"]
7
7
  gem.summary = %q{Fluentd input/output plugin to forward over SSL with authentications}
@@ -16,4 +16,5 @@ Gem::Specification.new do |gem|
16
16
  gem.add_runtime_dependency "fluentd"
17
17
  gem.add_runtime_dependency "fluent-mixin-config-placeholders"
18
18
  gem.add_runtime_dependency "resolve-hostname"
19
+ gem.add_development_dependency "rake"
19
20
  end
@@ -2,6 +2,13 @@
2
2
 
3
3
  require 'fluent/mixin/config_placeholders'
4
4
 
5
+ module Fluent
6
+ class SecureForwardInput < Input
7
+ end
8
+ end
9
+
10
+ require_relative 'input_session'
11
+
5
12
  module Fluent
6
13
  class SecureForwardInput < Input
7
14
  DEFAULT_SECURE_LISTEN_PORT = 24284
@@ -57,7 +64,6 @@ module Fluent
57
64
 
58
65
  def initialize
59
66
  super
60
- require 'resolv'
61
67
  require 'socket'
62
68
  require 'openssl'
63
69
  require 'digest'
@@ -214,210 +220,5 @@ module Fluent
214
220
  Fluent::Engine.emit(tag, time, record)
215
221
  end
216
222
  end
217
-
218
- class Session # Fluent::SecureForwardInput::Session
219
- attr_accessor :receiver
220
- attr_accessor :state, :thread, :node, :socket, :unpacker, :auth_salt
221
-
222
- def initialize(receiver, socket)
223
- @receiver = receiver
224
-
225
- @state = :helo
226
-
227
- @socket = socket
228
- @socket.sync = true
229
-
230
- @ipaddress = nil
231
- @node = nil
232
- @unpacker = MessagePack::Unpacker.new
233
- @thread = Thread.new(&method(:start))
234
- end
235
-
236
- def established?
237
- @state == :established
238
- end
239
-
240
- def generate_salt
241
- OpenSSL::Random.random_bytes(16)
242
- end
243
-
244
- def check_node(hostname, ipaddress, port, proto)
245
- node = nil
246
- family = Socket.const_get(proto)
247
- @receiver.nodes.each do |n|
248
- proto, port, host, ipaddr, family_num, socktype_num, proto_num = Socket.getaddrinfo(n[:host], port, family).first
249
- if ipaddr == ipaddress
250
- node = n
251
- break
252
- end
253
- end
254
- node
255
- end
256
-
257
- ## not implemented yet
258
- # def check_hostname_reverse_lookup(ipaddress)
259
- # rev_name = Resolv.getname(ipaddress)
260
- # proto, port, host, ipaddr, family_num, socktype_num, proto_num = Socket.getaddrinfo(rev_name, DUMMY_PORT)
261
- # unless ipaddr == ipaddress
262
- # return false
263
- # end
264
- # true
265
- # end
266
-
267
- def generate_helo
268
- $log.debug "generating helo"
269
- # ['HELO', options(hash)]
270
- [ 'HELO', {'auth' => (@receiver.authentication ? @auth_key_salt : ''), 'keepalive' => @receiver.allow_keepalive } ]
271
- end
272
-
273
- def check_ping(message)
274
- $log.debug "checking ping"
275
- # ['PING', self_hostname, shared_key\_salt, sha512\_hex(shared_key\_salt + self_hostname + shared_key),
276
- # username || '', sha512\_hex(auth\_salt + username + password) || '']
277
- unless message.size == 6 && message[0] == 'PING'
278
- return false, 'invalid ping message'
279
- end
280
- ping, hostname, shared_key_salt, shared_key_hexdigest, username, password_digest = message
281
-
282
- shared_key = if @node && @node[:shared_key]
283
- @node[:shared_key]
284
- else
285
- @receiver.shared_key
286
- end
287
- serverside = Digest::SHA512.new.update(shared_key_salt).update(hostname).update(shared_key).hexdigest
288
- if shared_key_hexdigest != serverside
289
- $log.warn "Shared key mismatch from '#{hostname}'"
290
- return false, 'shared_key mismatch'
291
- end
292
-
293
- if @receiver.authentication
294
- users = @receiver.select_authenticate_users(@node, username)
295
- success = false
296
- users.each do |user|
297
- passhash = Digest::SHA512.new.update(@auth_key_salt).update(username).update(user[:password]).hexdigest
298
- success ||= (passhash == password_digest)
299
- end
300
- unless success
301
- $log.warn "Authentication failed from client '#{hostname}', username '#{username}'"
302
- return false, 'username/password mismatch'
303
- end
304
- end
305
-
306
- return true, shared_key_salt
307
- end
308
-
309
- def generate_pong(auth_result, reason_or_salt)
310
- $log.debug "generating pong"
311
- # ['PONG', bool(authentication result), 'reason if authentication failed',
312
- # self_hostname, sha512\_hex(salt + self_hostname + sharedkey)]
313
- if not auth_result
314
- return ['PONG', false, reason_or_salt, '', '']
315
- end
316
-
317
- shared_key = if @node && @node[:shared_key]
318
- @node[:shared_key]
319
- else
320
- @receiver.shared_key
321
- end
322
- shared_key_hex = Digest::SHA512.new.update(reason_or_salt).update(@receiver.self_hostname).update(shared_key).hexdigest
323
- [ 'PONG', true, '', @receiver.self_hostname, shared_key_hex ]
324
- end
325
-
326
- def on_read(data)
327
- $log.debug "on_read"
328
- if self.established?
329
- @receiver.on_message(data)
330
- end
331
-
332
- case @state
333
- when :pingpong
334
- success, reason_or_salt = self.check_ping(data)
335
- if not success
336
- send_data generate_pong(false, reason_or_salt)
337
- self.shutdown
338
- return
339
- end
340
- send_data generate_pong(true, reason_or_salt)
341
-
342
- $log.debug "connection established"
343
- @state = :established
344
- end
345
- end
346
-
347
- def send_data(data)
348
- # not nonblock because write data (response) needs sequence
349
- @socket.write data.to_msgpack
350
- end
351
-
352
- def start
353
- $log.debug "starting server"
354
-
355
- $log.trace "accepting ssl session"
356
- begin
357
- @socket.accept
358
- rescue OpenSSL::SSL::SSLError => e
359
- $log.debug "failed to establish ssl session"
360
- self.shutdown
361
- return
362
- end
363
-
364
- proto, port, host, ipaddr = @socket.io.addr
365
- @node = check_node(host, ipaddr, port, proto)
366
- if @node.nil? && (! @receiver.allow_anonymous_source)
367
- $log.warn "Connection required from unknown host '#{host}' (#{ipaddr}), disconnecting..."
368
- self.shutdown
369
- return
370
- end
371
-
372
- @auth_key_salt = generate_salt
373
-
374
- buf = ''
375
- read_length = @receiver.read_length
376
- read_interval = @receiver.read_interval
377
- socket_interval = @receiver.socket_interval
378
-
379
- send_data generate_helo()
380
- @state = :pingpong
381
-
382
- loop do
383
- begin
384
- while @socket.read_nonblock(read_length, buf)
385
- if buf == ''
386
- sleep read_interval
387
- next
388
- end
389
- @unpacker.feed_each(buf, &method(:on_read))
390
- buf = ''
391
- end
392
- rescue OpenSSL::SSL::SSLError => e
393
- # to wait i/o restart
394
- sleep socket_interval
395
- rescue EOFError => e
396
- $log.debug "Connection closed from '#{host}'(#{ipaddr})"
397
- break
398
- end
399
- end
400
- rescue => e
401
- $log.warn e
402
- ensure
403
- self.shutdown
404
- end
405
-
406
- def shutdown
407
- @state = :closed
408
- if @thread == Thread.current
409
- @socket.close
410
- @thread.kill
411
- else
412
- if @thread
413
- @thread.kill
414
- @thread.join
415
- end
416
- @socket.close
417
- end
418
- rescue => e
419
- $log.debug "#{e.class}:#{e.message}"
420
- end
421
- end
422
223
  end
423
224
  end
@@ -0,0 +1,210 @@
1
+ # require 'msgpack'
2
+ # require 'socket'
3
+ # require 'openssl'
4
+ # require 'digest'
5
+ ### require 'resolv'
6
+
7
+ class Fluent::SecureForwardInput::Session
8
+ attr_accessor :receiver
9
+ attr_accessor :state, :thread, :node, :socket, :unpacker, :auth_salt
10
+
11
+ def initialize(receiver, socket)
12
+ @receiver = receiver
13
+
14
+ @state = :helo
15
+
16
+ @socket = socket
17
+ @socket.sync = true
18
+
19
+ @ipaddress = nil
20
+ @node = nil
21
+ @unpacker = MessagePack::Unpacker.new
22
+ @thread = Thread.new(&method(:start))
23
+ end
24
+
25
+ def established?
26
+ @state == :established
27
+ end
28
+
29
+ def generate_salt
30
+ OpenSSL::Random.random_bytes(16)
31
+ end
32
+
33
+ def check_node(hostname, ipaddress, port, proto)
34
+ node = nil
35
+ family = Socket.const_get(proto)
36
+ @receiver.nodes.each do |n|
37
+ proto, port, host, ipaddr, family_num, socktype_num, proto_num = Socket.getaddrinfo(n[:host], port, family).first
38
+ if ipaddr == ipaddress
39
+ node = n
40
+ break
41
+ end
42
+ end
43
+ node
44
+ end
45
+
46
+ ## not implemented yet
47
+ # def check_hostname_reverse_lookup(ipaddress)
48
+ # rev_name = Resolv.getname(ipaddress)
49
+ # proto, port, host, ipaddr, family_num, socktype_num, proto_num = Socket.getaddrinfo(rev_name, DUMMY_PORT)
50
+ # unless ipaddr == ipaddress
51
+ # return false
52
+ # end
53
+ # true
54
+ # end
55
+
56
+ def generate_helo
57
+ $log.debug "generating helo"
58
+ # ['HELO', options(hash)]
59
+ [ 'HELO', {'auth' => (@receiver.authentication ? @auth_key_salt : ''), 'keepalive' => @receiver.allow_keepalive } ]
60
+ end
61
+
62
+ def check_ping(message)
63
+ $log.debug "checking ping"
64
+ # ['PING', self_hostname, shared_key\_salt, sha512\_hex(shared_key\_salt + self_hostname + shared_key),
65
+ # username || '', sha512\_hex(auth\_salt + username + password) || '']
66
+ unless message.size == 6 && message[0] == 'PING'
67
+ return false, 'invalid ping message'
68
+ end
69
+ ping, hostname, shared_key_salt, shared_key_hexdigest, username, password_digest = message
70
+
71
+ shared_key = if @node && @node[:shared_key]
72
+ @node[:shared_key]
73
+ else
74
+ @receiver.shared_key
75
+ end
76
+ serverside = Digest::SHA512.new.update(shared_key_salt).update(hostname).update(shared_key).hexdigest
77
+ if shared_key_hexdigest != serverside
78
+ $log.warn "Shared key mismatch from '#{hostname}'"
79
+ return false, 'shared_key mismatch'
80
+ end
81
+
82
+ if @receiver.authentication
83
+ users = @receiver.select_authenticate_users(@node, username)
84
+ success = false
85
+ users.each do |user|
86
+ passhash = Digest::SHA512.new.update(@auth_key_salt).update(username).update(user[:password]).hexdigest
87
+ success ||= (passhash == password_digest)
88
+ end
89
+ unless success
90
+ $log.warn "Authentication failed from client '#{hostname}', username '#{username}'"
91
+ return false, 'username/password mismatch'
92
+ end
93
+ end
94
+
95
+ return true, shared_key_salt
96
+ end
97
+
98
+ def generate_pong(auth_result, reason_or_salt)
99
+ $log.debug "generating pong"
100
+ # ['PONG', bool(authentication result), 'reason if authentication failed',
101
+ # self_hostname, sha512\_hex(salt + self_hostname + sharedkey)]
102
+ if not auth_result
103
+ return ['PONG', false, reason_or_salt, '', '']
104
+ end
105
+
106
+ shared_key = if @node && @node[:shared_key]
107
+ @node[:shared_key]
108
+ else
109
+ @receiver.shared_key
110
+ end
111
+ shared_key_hex = Digest::SHA512.new.update(reason_or_salt).update(@receiver.self_hostname).update(shared_key).hexdigest
112
+ [ 'PONG', true, '', @receiver.self_hostname, shared_key_hex ]
113
+ end
114
+
115
+ def on_read(data)
116
+ $log.debug "on_read"
117
+ if self.established?
118
+ @receiver.on_message(data)
119
+ end
120
+
121
+ case @state
122
+ when :pingpong
123
+ success, reason_or_salt = self.check_ping(data)
124
+ if not success
125
+ send_data generate_pong(false, reason_or_salt)
126
+ self.shutdown
127
+ return
128
+ end
129
+ send_data generate_pong(true, reason_or_salt)
130
+
131
+ $log.debug "connection established"
132
+ @state = :established
133
+ end
134
+ end
135
+
136
+ def send_data(data)
137
+ # not nonblock because write data (response) needs sequence
138
+ @socket.write data.to_msgpack
139
+ end
140
+
141
+ def start
142
+ $log.debug "starting server"
143
+
144
+ $log.trace "accepting ssl session"
145
+ begin
146
+ @socket.accept
147
+ rescue OpenSSL::SSL::SSLError => e
148
+ $log.debug "failed to establish ssl session"
149
+ self.shutdown
150
+ return
151
+ end
152
+
153
+ proto, port, host, ipaddr = @socket.io.addr
154
+ @node = check_node(host, ipaddr, port, proto)
155
+ if @node.nil? && (! @receiver.allow_anonymous_source)
156
+ $log.warn "Connection required from unknown host '#{host}' (#{ipaddr}), disconnecting..."
157
+ self.shutdown
158
+ return
159
+ end
160
+
161
+ @auth_key_salt = generate_salt
162
+
163
+ buf = ''
164
+ read_length = @receiver.read_length
165
+ read_interval = @receiver.read_interval
166
+ socket_interval = @receiver.socket_interval
167
+
168
+ send_data generate_helo()
169
+ @state = :pingpong
170
+
171
+ loop do
172
+ begin
173
+ while @socket.read_nonblock(read_length, buf)
174
+ if buf == ''
175
+ sleep read_interval
176
+ next
177
+ end
178
+ @unpacker.feed_each(buf, &method(:on_read))
179
+ buf = ''
180
+ end
181
+ rescue OpenSSL::SSL::SSLError => e
182
+ # to wait i/o restart
183
+ sleep socket_interval
184
+ rescue EOFError => e
185
+ $log.debug "Connection closed from '#{host}'(#{ipaddr})"
186
+ break
187
+ end
188
+ end
189
+ rescue => e
190
+ $log.warn "unexpected error in in_secure_forward", :error_class => e.class, :error => e
191
+ ensure
192
+ self.shutdown
193
+ end
194
+
195
+ def shutdown
196
+ @state = :closed
197
+ if @thread == Thread.current
198
+ @socket.close
199
+ @thread.kill
200
+ else
201
+ if @thread
202
+ @thread.kill
203
+ @thread.join
204
+ end
205
+ @socket.close
206
+ end
207
+ rescue => e
208
+ $log.debug "#{e.class}:#{e.message}"
209
+ end
210
+ end
@@ -0,0 +1,38 @@
1
+ module Fluent::SecureForwardOutput::OpenSSLUtil
2
+ def self.verify_result_name(code)
3
+ case code
4
+ when OpenSSL::X509::V_OK then 'V_OK'
5
+ when OpenSSL::X509::V_ERR_AKID_SKID_MISMATCH then 'V_ERR_AKID_SKID_MISMATCH'
6
+ when OpenSSL::X509::V_ERR_APPLICATION_VERIFICATION then 'V_ERR_APPLICATION_VERIFICATION'
7
+ when OpenSSL::X509::V_ERR_CERT_CHAIN_TOO_LONG then 'V_ERR_CERT_CHAIN_TOO_LONG'
8
+ when OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED then 'V_ERR_CERT_HAS_EXPIRED'
9
+ when OpenSSL::X509::V_ERR_CERT_NOT_YET_VALID then 'V_ERR_CERT_NOT_YET_VALID'
10
+ when OpenSSL::X509::V_ERR_CERT_REJECTED then 'V_ERR_CERT_REJECTED'
11
+ when OpenSSL::X509::V_ERR_CERT_REVOKED then 'V_ERR_CERT_REVOKED'
12
+ when OpenSSL::X509::V_ERR_CERT_SIGNATURE_FAILURE then 'V_ERR_CERT_SIGNATURE_FAILURE'
13
+ when OpenSSL::X509::V_ERR_CERT_UNTRUSTED then 'V_ERR_CERT_UNTRUSTED'
14
+ when OpenSSL::X509::V_ERR_CRL_HAS_EXPIRED then 'V_ERR_CRL_HAS_EXPIRED'
15
+ when OpenSSL::X509::V_ERR_CRL_NOT_YET_VALID then 'V_ERR_CRL_NOT_YET_VALID'
16
+ when OpenSSL::X509::V_ERR_CRL_SIGNATURE_FAILURE then 'V_ERR_CRL_SIGNATURE_FAILURE'
17
+ when OpenSSL::X509::V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT then 'V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT'
18
+ when OpenSSL::X509::V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD then 'V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD'
19
+ when OpenSSL::X509::V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD then 'V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD'
20
+ when OpenSSL::X509::V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD then 'V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD'
21
+ when OpenSSL::X509::V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD then 'V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD'
22
+ when OpenSSL::X509::V_ERR_INVALID_CA then 'V_ERR_INVALID_CA'
23
+ when OpenSSL::X509::V_ERR_INVALID_PURPOSE then 'V_ERR_INVALID_PURPOSE'
24
+ when OpenSSL::X509::V_ERR_KEYUSAGE_NO_CERTSIGN then 'V_ERR_KEYUSAGE_NO_CERTSIGN'
25
+ when OpenSSL::X509::V_ERR_OUT_OF_MEM then 'V_ERR_OUT_OF_MEM'
26
+ when OpenSSL::X509::V_ERR_PATH_LENGTH_EXCEEDED then 'V_ERR_PATH_LENGTH_EXCEEDED'
27
+ when OpenSSL::X509::V_ERR_SELF_SIGNED_CERT_IN_CHAIN then 'V_ERR_SELF_SIGNED_CERT_IN_CHAIN'
28
+ when OpenSSL::X509::V_ERR_SUBJECT_ISSUER_MISMATCH then 'V_ERR_SUBJECT_ISSUER_MISMATCH'
29
+ when OpenSSL::X509::V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY then 'V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY'
30
+ when OpenSSL::X509::V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE then 'V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY'
31
+ when OpenSSL::X509::V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE then 'V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE'
32
+ when OpenSSL::X509::V_ERR_UNABLE_TO_GET_CRL then 'V_ERR_UNABLE_TO_GET_CRL'
33
+ when OpenSSL::X509::V_ERR_UNABLE_TO_GET_ISSUER_CERT then 'V_ERR_UNABLE_TO_GET_ISSUER_CERT'
34
+ when OpenSSL::X509::V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY then 'V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY'
35
+ when OpenSSL::X509::V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE then 'V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE'
36
+ end
37
+ end
38
+ end
@@ -2,6 +2,13 @@
2
2
 
3
3
  require 'fluent/mixin/config_placeholders'
4
4
 
5
+ module Fluent
6
+ class SecureForwardOutput < ObjectBufferedOutput
7
+ end
8
+ end
9
+
10
+ require_relative 'output_node'
11
+
5
12
  module Fluent
6
13
  class SecureForwardOutput < ObjectBufferedOutput
7
14
  DEFAULT_SECURE_CONNECT_PORT = 24284
@@ -74,9 +81,8 @@ module Fluent
74
81
  raise Fluent::ConfigError, "unknown config tag name #{element.name}"
75
82
  end
76
83
  end
77
- if @nodes.size > 1
78
- raise Fluent::ConfigError, "Two or more servers are not supported yet."
79
- end
84
+ @next_node = 0
85
+ @mutex = Mutex.new
80
86
 
81
87
  @hostname_resolver = Resolve::Hostname.new(:system_resolver => true)
82
88
 
@@ -84,8 +90,21 @@ module Fluent
84
90
  end
85
91
 
86
92
  def select_node
87
- #TODO: roundrobin? random?
88
- @nodes.select(&:established?).first
93
+ tries = 0
94
+ nodes = @nodes.size
95
+ @mutex.synchronize {
96
+ n = nil
97
+ while tries <= nodes
98
+ n = @nodes[@next_node]
99
+ @next_node += 1
100
+ @next_node = 0 if @next_node >= nodes
101
+
102
+ return n if n && n.established?
103
+
104
+ tries += 1
105
+ end
106
+ nil
107
+ }
89
108
  end
90
109
 
91
110
  def start
@@ -168,266 +187,5 @@ module Fluent
168
187
  # writeRawBody(packed_es)
169
188
  es.write_to(ssl)
170
189
  end
171
-
172
- class Node # Fluent::SecureForwardOutput::Node
173
- attr_accessor :host, :port, :hostlabel, :shared_key, :username, :password
174
- attr_accessor :authentication, :keepalive
175
- attr_accessor :socket, :sslsession, :unpacker, :shared_key_salt, :state
176
-
177
- def initialize(sender, shared_key, conf)
178
- @sender = sender
179
- @shared_key = shared_key
180
-
181
- @host = conf['host']
182
- @port = (conf['port'] || DEFAULT_SECURE_CONNECT_PORT).to_i
183
- @hostlabel = conf['hostlabel'] || conf['host']
184
- @username = conf['username'] || ''
185
- @password = conf['password'] || ''
186
-
187
- @authentication = nil
188
- @keepalive = nil
189
-
190
- @socket = nil
191
- @sslsession = nil
192
- @unpacker = MessagePack::Unpacker.new
193
-
194
- @shared_key_salt = generate_salt
195
- @state = :helo
196
- @thread = nil
197
- end
198
-
199
- def dup
200
- Node.new(
201
- @sender,
202
- @shared_key,
203
- {'host' => @host, 'port' => @port, 'hostlabel' => @hostlabel, 'username' => @username, 'password' => @password}
204
- )
205
- end
206
-
207
- def start
208
- @thread = Thread.new(&method(:connect))
209
- end
210
-
211
- def shutdown
212
- $log.debug "shutting down node #{@host}"
213
- @state = :closed
214
-
215
- if @thread == Thread.current
216
- @sslsession.close if @sslsession
217
- @socket.close if @socket
218
- @thread.kill
219
- else
220
- if @thread
221
- @thread.kill
222
- @thread.join
223
- end
224
- @sslsession.close if @sslsession
225
- @socket.close if @socket
226
- end
227
- rescue => e
228
- $log.debug "error on node shutdown #{e.class}:#{e.message}"
229
- end
230
-
231
- def verify_result_name(code)
232
- case code
233
- when OpenSSL::X509::V_OK then 'V_OK'
234
- when OpenSSL::X509::V_ERR_AKID_SKID_MISMATCH then 'V_ERR_AKID_SKID_MISMATCH'
235
- when OpenSSL::X509::V_ERR_APPLICATION_VERIFICATION then 'V_ERR_APPLICATION_VERIFICATION'
236
- when OpenSSL::X509::V_ERR_CERT_CHAIN_TOO_LONG then 'V_ERR_CERT_CHAIN_TOO_LONG'
237
- when OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED then 'V_ERR_CERT_HAS_EXPIRED'
238
- when OpenSSL::X509::V_ERR_CERT_NOT_YET_VALID then 'V_ERR_CERT_NOT_YET_VALID'
239
- when OpenSSL::X509::V_ERR_CERT_REJECTED then 'V_ERR_CERT_REJECTED'
240
- when OpenSSL::X509::V_ERR_CERT_REVOKED then 'V_ERR_CERT_REVOKED'
241
- when OpenSSL::X509::V_ERR_CERT_SIGNATURE_FAILURE then 'V_ERR_CERT_SIGNATURE_FAILURE'
242
- when OpenSSL::X509::V_ERR_CERT_UNTRUSTED then 'V_ERR_CERT_UNTRUSTED'
243
- when OpenSSL::X509::V_ERR_CRL_HAS_EXPIRED then 'V_ERR_CRL_HAS_EXPIRED'
244
- when OpenSSL::X509::V_ERR_CRL_NOT_YET_VALID then 'V_ERR_CRL_NOT_YET_VALID'
245
- when OpenSSL::X509::V_ERR_CRL_SIGNATURE_FAILURE then 'V_ERR_CRL_SIGNATURE_FAILURE'
246
- when OpenSSL::X509::V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT then 'V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT'
247
- when OpenSSL::X509::V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD then 'V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD'
248
- when OpenSSL::X509::V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD then 'V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD'
249
- when OpenSSL::X509::V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD then 'V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD'
250
- when OpenSSL::X509::V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD then 'V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD'
251
- when OpenSSL::X509::V_ERR_INVALID_CA then 'V_ERR_INVALID_CA'
252
- when OpenSSL::X509::V_ERR_INVALID_PURPOSE then 'V_ERR_INVALID_PURPOSE'
253
- when OpenSSL::X509::V_ERR_KEYUSAGE_NO_CERTSIGN then 'V_ERR_KEYUSAGE_NO_CERTSIGN'
254
- when OpenSSL::X509::V_ERR_OUT_OF_MEM then 'V_ERR_OUT_OF_MEM'
255
- when OpenSSL::X509::V_ERR_PATH_LENGTH_EXCEEDED then 'V_ERR_PATH_LENGTH_EXCEEDED'
256
- when OpenSSL::X509::V_ERR_SELF_SIGNED_CERT_IN_CHAIN then 'V_ERR_SELF_SIGNED_CERT_IN_CHAIN'
257
- when OpenSSL::X509::V_ERR_SUBJECT_ISSUER_MISMATCH then 'V_ERR_SUBJECT_ISSUER_MISMATCH'
258
- when OpenSSL::X509::V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY then 'V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY'
259
- when OpenSSL::X509::V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE then 'V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY'
260
- when OpenSSL::X509::V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE then 'V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE'
261
- when OpenSSL::X509::V_ERR_UNABLE_TO_GET_CRL then 'V_ERR_UNABLE_TO_GET_CRL'
262
- when OpenSSL::X509::V_ERR_UNABLE_TO_GET_ISSUER_CERT then 'V_ERR_UNABLE_TO_GET_ISSUER_CERT'
263
- when OpenSSL::X509::V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY then 'V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY'
264
- when OpenSSL::X509::V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE then 'V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE'
265
- end
266
- end
267
-
268
- def established?
269
- @state == :established
270
- end
271
-
272
- def generate_salt
273
- OpenSSL::Random.random_bytes(16)
274
- end
275
-
276
- def check_helo(message)
277
- $log.debug "checking helo"
278
- # ['HELO', options(hash)]
279
- unless message.size == 2 && message[0] == 'HELO'
280
- return false
281
- end
282
- opts = message[1]
283
- @authentication = opts['auth']
284
- @keepalive = opts['keepalive']
285
- true
286
- end
287
-
288
- def generate_ping
289
- $log.debug "generating ping"
290
- # ['PING', self_hostname, sharedkey\_salt, sha512\_hex(sharedkey\_salt + self_hostname + shared_key),
291
- # username || '', sha512\_hex(auth\_salt + username + password) || '']
292
- shared_key_hexdigest = Digest::SHA512.new.update(@shared_key_salt).update(@sender.self_hostname).update(@shared_key).hexdigest
293
- ping = ['PING', @sender.self_hostname, @shared_key_salt, shared_key_hexdigest]
294
- if @authentication != ''
295
- password_hexdigest = Digest::SHA512.new.update(@authentication).update(@username).update(@password).hexdigest
296
- ping.push(@username, password_hexdigest)
297
- else
298
- ping.push('','')
299
- end
300
- ping
301
- end
302
-
303
- def check_pong(message)
304
- $log.debug "checking pong"
305
- # ['PONG', bool(authentication result), 'reason if authentication failed',
306
- # self_hostname, sha512\_hex(salt + self_hostname + sharedkey)]
307
- unless message.size == 5 && message[0] == 'PONG'
308
- return false, 'invalid format for PONG message'
309
- end
310
- pong, auth_result, reason, hostname, shared_key_hexdigest = message
311
-
312
- unless auth_result
313
- return false, 'authentication failed: ' + reason
314
- end
315
-
316
- clientside = Digest::SHA512.new.update(@shared_key_salt).update(hostname).update(@shared_key).hexdigest
317
- unless shared_key_hexdigest == clientside
318
- return false, 'shared key mismatch'
319
- end
320
-
321
- return true, nil
322
- end
323
-
324
- def send_data(data)
325
- @sslsession.write data.to_msgpack
326
- end
327
-
328
- def on_read(data)
329
- $log.debug "on_read"
330
- if self.established?
331
- #TODO: ACK
332
- $log.warn "unknown packets arrived..."
333
- return
334
- end
335
-
336
- case @state
337
- when :helo
338
- # TODO: log debug
339
- unless check_helo(data)
340
- $log.warn "received invalid helo message from #{@host}"
341
- self.shutdown
342
- return
343
- end
344
- send_data generate_ping()
345
- @state = :pingpong
346
- when :pingpong
347
- success, reason = check_pong(data)
348
- unless success
349
- $log.warn "connection refused to #{@host}:" + reason
350
- self.shutdown
351
- return
352
- end
353
- $log.info "connection established to #{@host}"
354
- @state = :established
355
- end
356
- end
357
-
358
- def connect
359
- $log.debug "starting client"
360
-
361
- addr = @sender.hostname_resolver.getaddress(@host)
362
- $log.debug "create tcp socket to node", :host => @host, :address => addr, :port => @port
363
- sock = TCPSocket.new(addr, @port)
364
-
365
- $log.trace "changing socket options"
366
- opt = [1, @sender.send_timeout.to_i].pack('I!I!') # { int l_onoff; int l_linger; }
367
- sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, opt)
368
-
369
- opt = [@sender.send_timeout.to_i, 0].pack('L!L!') # struct timeval
370
- sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, opt)
371
-
372
- # TODO: SSLContext constructer parameter (SSL/TLS protocol version)
373
- $log.trace "initializing SSL contexts"
374
- context = OpenSSL::SSL::SSLContext.new
375
- # TODO: context.ca_file = (ca_file_path)
376
- # TODO: context.ciphers = (SSL Shared key chiper protocols)
377
-
378
- $log.debug "trying to connect ssl session", :host => @host, :ipaddr => addr, :port => @port
379
- sslsession = OpenSSL::SSL::SSLSocket.new(sock, context)
380
- # TODO: check connection failure
381
- sslsession.connect
382
- $log.debug "ssl session connected", :host => @host, :port => @port
383
-
384
- begin
385
- unless @sender.allow_self_signed_certificate
386
- $log.debug "checking peer's certificate", :subject => sslsession.peer_cert.subject
387
- sslsession.post_connection_check(@hostlabel)
388
- verify = sslsession.verify_result
389
- if verify != OpenSSL::X509::V_OK
390
- err_name = verify_result_name(verify)
391
- $log.warn "failed to verify certification while connecting host #{@host} as #{@hostlabel} (but not raised, why?)"
392
- $log.warn "verify_result: #{err_name}"
393
- raise RuntimeError, "failed to verify certification while connecting host #{@host} as #{@hostlabel}"
394
- end
395
- end
396
- rescue OpenSSL::SSL::SSLError => e
397
- $log.warn "failed to verify certification while connecting ssl session", :host => @host, :hostlabel => @hostlabel
398
- self.shutdown
399
- raise
400
- end
401
-
402
- $log.debug "ssl sessison connected", :host => @host, :port => @port
403
- @socket = sock
404
- @sslsession = sslsession
405
-
406
- buf = ''
407
- read_length = @sender.read_length
408
- read_interval = @sender.read_interval
409
- socket_interval = @sender.socket_interval
410
-
411
- loop do
412
- begin
413
- while @sslsession.read_nonblock(read_length, buf)
414
- if buf == ''
415
- sleep read_interval
416
- next
417
- end
418
- @unpacker.feed_each(buf, &method(:on_read))
419
- buf = ''
420
- end
421
- rescue OpenSSL::SSL::SSLError
422
- # to wait i/o restart
423
- sleep socket_interval
424
- rescue EOFError
425
- $log.warn "disconnected from #{@host}"
426
- break
427
- end
428
- end
429
- self.shutdown
430
- end
431
- end
432
190
  end
433
191
  end
@@ -0,0 +1,231 @@
1
+ # require 'msgpack'
2
+ # require 'socket'
3
+ # require 'openssl'
4
+ # require 'digest'
5
+ # require 'resolve/hostname'
6
+
7
+ require_relative 'openssl_util'
8
+
9
+ class Fluent::SecureForwardOutput::Node
10
+ attr_accessor :host, :port, :hostlabel, :shared_key, :username, :password
11
+ attr_accessor :authentication, :keepalive
12
+ attr_accessor :socket, :sslsession, :unpacker, :shared_key_salt, :state
13
+
14
+ def initialize(sender, shared_key, conf)
15
+ @sender = sender
16
+ @shared_key = shared_key
17
+
18
+ @host = conf['host']
19
+ @port = (conf['port'] || Fluent::SecureForwardOutput::DEFAULT_SECURE_CONNECT_PORT).to_i
20
+ @hostlabel = conf['hostlabel'] || conf['host']
21
+ @username = conf['username'] || ''
22
+ @password = conf['password'] || ''
23
+
24
+ @authentication = nil
25
+ @keepalive = nil
26
+
27
+ @socket = nil
28
+ @sslsession = nil
29
+ @unpacker = MessagePack::Unpacker.new
30
+
31
+ @shared_key_salt = generate_salt
32
+ @state = :helo
33
+ @thread = nil
34
+ end
35
+
36
+ def dup
37
+ self.class.new(
38
+ @sender,
39
+ @shared_key,
40
+ {'host' => @host, 'port' => @port, 'hostlabel' => @hostlabel, 'username' => @username, 'password' => @password}
41
+ )
42
+ end
43
+
44
+ def start
45
+ @thread = Thread.new(&method(:connect))
46
+ end
47
+
48
+ def shutdown
49
+ $log.debug "shutting down node #{@host}"
50
+ @state = :closed
51
+
52
+ if @thread == Thread.current
53
+ @sslsession.close if @sslsession
54
+ @socket.close if @socket
55
+ @thread.kill
56
+ else
57
+ if @thread
58
+ @thread.kill
59
+ @thread.join
60
+ end
61
+ @sslsession.close if @sslsession
62
+ @socket.close if @socket
63
+ end
64
+ rescue => e
65
+ $log.debug "error on node shutdown #{e.class}:#{e.message}"
66
+ end
67
+
68
+ def established?
69
+ @state == :established
70
+ end
71
+
72
+ def generate_salt
73
+ OpenSSL::Random.random_bytes(16)
74
+ end
75
+
76
+ def check_helo(message)
77
+ $log.debug "checking helo"
78
+ # ['HELO', options(hash)]
79
+ unless message.size == 2 && message[0] == 'HELO'
80
+ return false
81
+ end
82
+ opts = message[1]
83
+ @authentication = opts['auth']
84
+ @keepalive = opts['keepalive']
85
+ true
86
+ end
87
+
88
+ def generate_ping
89
+ $log.debug "generating ping"
90
+ # ['PING', self_hostname, sharedkey\_salt, sha512\_hex(sharedkey\_salt + self_hostname + shared_key),
91
+ # username || '', sha512\_hex(auth\_salt + username + password) || '']
92
+ shared_key_hexdigest = Digest::SHA512.new.update(@shared_key_salt).update(@sender.self_hostname).update(@shared_key).hexdigest
93
+ ping = ['PING', @sender.self_hostname, @shared_key_salt, shared_key_hexdigest]
94
+ if @authentication != ''
95
+ password_hexdigest = Digest::SHA512.new.update(@authentication).update(@username).update(@password).hexdigest
96
+ ping.push(@username, password_hexdigest)
97
+ else
98
+ ping.push('','')
99
+ end
100
+ ping
101
+ end
102
+
103
+ def check_pong(message)
104
+ $log.debug "checking pong"
105
+ # ['PONG', bool(authentication result), 'reason if authentication failed',
106
+ # self_hostname, sha512\_hex(salt + self_hostname + sharedkey)]
107
+ unless message.size == 5 && message[0] == 'PONG'
108
+ return false, 'invalid format for PONG message'
109
+ end
110
+ pong, auth_result, reason, hostname, shared_key_hexdigest = message
111
+
112
+ unless auth_result
113
+ return false, 'authentication failed: ' + reason
114
+ end
115
+
116
+ clientside = Digest::SHA512.new.update(@shared_key_salt).update(hostname).update(@shared_key).hexdigest
117
+ unless shared_key_hexdigest == clientside
118
+ return false, 'shared key mismatch'
119
+ end
120
+
121
+ return true, nil
122
+ end
123
+
124
+ def send_data(data)
125
+ @sslsession.write data.to_msgpack
126
+ end
127
+
128
+ def on_read(data)
129
+ $log.debug "on_read"
130
+ if self.established?
131
+ #TODO: ACK
132
+ $log.warn "unknown packets arrived..."
133
+ return
134
+ end
135
+
136
+ case @state
137
+ when :helo
138
+ # TODO: log debug
139
+ unless check_helo(data)
140
+ $log.warn "received invalid helo message from #{@host}"
141
+ self.shutdown
142
+ return
143
+ end
144
+ send_data generate_ping()
145
+ @state = :pingpong
146
+ when :pingpong
147
+ success, reason = check_pong(data)
148
+ unless success
149
+ $log.warn "connection refused to #{@host}:" + reason
150
+ self.shutdown
151
+ return
152
+ end
153
+ $log.info "connection established to #{@host}"
154
+ @state = :established
155
+ end
156
+ end
157
+
158
+ def connect
159
+ $log.debug "starting client"
160
+
161
+ addr = @sender.hostname_resolver.getaddress(@host)
162
+ $log.debug "create tcp socket to node", :host => @host, :address => addr, :port => @port
163
+ sock = TCPSocket.new(addr, @port)
164
+
165
+ $log.trace "changing socket options"
166
+ opt = [1, @sender.send_timeout.to_i].pack('I!I!') # { int l_onoff; int l_linger; }
167
+ sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, opt)
168
+
169
+ opt = [@sender.send_timeout.to_i, 0].pack('L!L!') # struct timeval
170
+ sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, opt)
171
+
172
+ # TODO: SSLContext constructer parameter (SSL/TLS protocol version)
173
+ $log.trace "initializing SSL contexts"
174
+ context = OpenSSL::SSL::SSLContext.new
175
+ # TODO: context.ca_file = (ca_file_path)
176
+ # TODO: context.ciphers = (SSL Shared key chiper protocols)
177
+
178
+ $log.debug "trying to connect ssl session", :host => @host, :ipaddr => addr, :port => @port
179
+ sslsession = OpenSSL::SSL::SSLSocket.new(sock, context)
180
+ # TODO: check connection failure
181
+ sslsession.connect
182
+ $log.debug "ssl session connected", :host => @host, :port => @port
183
+
184
+ begin
185
+ unless @sender.allow_self_signed_certificate
186
+ $log.debug "checking peer's certificate", :subject => sslsession.peer_cert.subject
187
+ sslsession.post_connection_check(@hostlabel)
188
+ verify = sslsession.verify_result
189
+ if verify != OpenSSL::X509::V_OK
190
+ err_name = Fluent::SecureForwardOutput::OpenSSLUtil.verify_result_name(verify)
191
+ $log.warn "failed to verify certification while connecting host #{@host} as #{@hostlabel} (but not raised, why?)"
192
+ $log.warn "verify_result: #{err_name}"
193
+ raise RuntimeError, "failed to verify certification while connecting host #{@host} as #{@hostlabel}"
194
+ end
195
+ end
196
+ rescue OpenSSL::SSL::SSLError => e
197
+ $log.warn "failed to verify certification while connecting ssl session", :host => @host, :hostlabel => @hostlabel
198
+ self.shutdown
199
+ raise
200
+ end
201
+
202
+ $log.debug "ssl sessison connected", :host => @host, :port => @port
203
+ @socket = sock
204
+ @sslsession = sslsession
205
+
206
+ buf = ''
207
+ read_length = @sender.read_length
208
+ read_interval = @sender.read_interval
209
+ socket_interval = @sender.socket_interval
210
+
211
+ loop do
212
+ begin
213
+ while @sslsession.read_nonblock(read_length, buf)
214
+ if buf == ''
215
+ sleep read_interval
216
+ next
217
+ end
218
+ @unpacker.feed_each(buf, &method(:on_read))
219
+ buf = ''
220
+ end
221
+ rescue OpenSSL::SSL::SSLError
222
+ # to wait i/o restart
223
+ sleep socket_interval
224
+ rescue EOFError
225
+ $log.warn "disconnected from #{@host}"
226
+ break
227
+ end
228
+ end
229
+ self.shutdown
230
+ end
231
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,29 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'test/unit'
11
+
12
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
13
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
14
+ require 'fluent/test'
15
+ unless ENV.has_key?('VERBOSE')
16
+ nulllogger = Object.new
17
+ nulllogger.instance_eval {|obj|
18
+ def method_missing(method, *args)
19
+ # pass
20
+ end
21
+ }
22
+ $log = nulllogger
23
+ end
24
+
25
+ require 'fluent/plugin/in_secure_forward'
26
+ require 'fluent/plugin/out_secure_forward'
27
+
28
+ class Test::Unit::TestCase
29
+ end
@@ -0,0 +1,10 @@
1
+ require 'helper'
2
+
3
+ class SecureForwardInputTest < Test::Unit::TestCase
4
+ CONFIG = %[
5
+ ]
6
+
7
+ def create_driver(conf=CONFIG,tag='test')
8
+ Fluent::Test::InputTestDriver.new(Fluent::SecureForwardInput, tag).configure(conf)
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ require 'helper'
2
+
3
+ class SecureForwardOutputTest < Test::Unit::TestCase
4
+ CONFIG = %[
5
+ ]
6
+
7
+ def create_driver(conf=CONFIG,tag='test')
8
+ Fluent::Test::OutputTestDriver.new(Fluent::SecureForwardOutput, tag).configure(conf)
9
+ end
10
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluent-plugin-secure-forward
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - TAGOMORI Satoshi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-07-23 00:00:00.000000000 Z
11
+ date: 2013-07-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fluentd
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - '>='
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
55
69
  description: This version is HIGHLY EXPERIMENTAL.
56
70
  email:
57
71
  - tagomoris@gmail.com
@@ -74,7 +88,11 @@ files:
74
88
  - example/server.conf
75
89
  - fluent-plugin-secure-forward.gemspec
76
90
  - lib/fluent/plugin/in_secure_forward.rb
91
+ - lib/fluent/plugin/input_session.rb
92
+ - lib/fluent/plugin/openssl_util.rb
77
93
  - lib/fluent/plugin/out_secure_forward.rb
94
+ - lib/fluent/plugin/output_node.rb
95
+ - test/helper.rb
78
96
  - test/plugin/test_in_secure_forward.rb
79
97
  - test/plugin/test_out_secure_forward.rb
80
98
  homepage: https://github.com/tagomoris/fluent-plugin-secure-forward
@@ -101,6 +119,7 @@ signing_key:
101
119
  specification_version: 4
102
120
  summary: Fluentd input/output plugin to forward over SSL with authentications
103
121
  test_files:
122
+ - test/helper.rb
104
123
  - test/plugin/test_in_secure_forward.rb
105
124
  - test/plugin/test_out_secure_forward.rb
106
125
  has_rdoc: