fluentd 0.14.2 → 0.14.5
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
- data/ChangeLog +77 -1131
- data/Rakefile +15 -0
- data/appveyor.yml +2 -2
- data/example/in_forward.conf +3 -0
- data/example/in_forward_client.conf +37 -0
- data/example/in_forward_shared_key.conf +15 -0
- data/example/in_forward_users.conf +24 -0
- data/example/multi_filters.conf +61 -0
- data/example/out_forward.conf +13 -13
- data/example/out_forward_client.conf +109 -0
- data/example/out_forward_shared_key.conf +36 -0
- data/example/out_forward_users.conf +65 -0
- data/example/{out_buffered_null.conf → out_null.conf} +10 -6
- data/example/secondary_file.conf +41 -0
- data/fluentd.gemspec +19 -16
- data/lib/fluent/agent.rb +5 -0
- data/lib/fluent/command/debug.rb +4 -4
- data/lib/fluent/compat/handle_tag_and_time_mixin.rb +2 -0
- data/lib/fluent/compat/output.rb +5 -2
- data/lib/fluent/config/configure_proxy.rb +26 -5
- data/lib/fluent/config/error.rb +3 -0
- data/lib/fluent/config/section.rb +15 -0
- data/lib/fluent/event_router.rb +77 -4
- data/lib/fluent/plugin/base.rb +12 -3
- data/lib/fluent/plugin/buffer.rb +5 -1
- data/lib/fluent/plugin/filter.rb +48 -6
- data/lib/fluent/plugin/filter_record_transformer.rb +3 -1
- data/lib/fluent/plugin/filter_stdout.rb +0 -4
- data/lib/fluent/plugin/in_debug_agent.rb +5 -5
- data/lib/fluent/plugin/in_dummy.rb +9 -1
- data/lib/fluent/plugin/in_forward.rb +330 -62
- data/lib/fluent/plugin/in_monitor_agent.rb +31 -77
- data/lib/fluent/plugin/in_tail.rb +76 -92
- data/lib/fluent/plugin/multi_output.rb +4 -0
- data/lib/fluent/plugin/out_forward.rb +328 -222
- data/lib/fluent/plugin/out_null.rb +37 -0
- data/lib/fluent/plugin/out_secondary_file.rb +128 -0
- data/lib/fluent/plugin/out_stdout.rb +38 -2
- data/lib/fluent/plugin/output.rb +29 -6
- data/lib/fluent/plugin/storage_local.rb +16 -0
- data/lib/fluent/plugin_helper/timer.rb +6 -1
- data/lib/fluent/root_agent.rb +4 -1
- data/lib/fluent/supervisor.rb +62 -9
- data/lib/fluent/test/base.rb +3 -0
- data/lib/fluent/test/driver/base.rb +5 -0
- data/lib/fluent/test/formatter_test.rb +2 -0
- data/lib/fluent/test/parser_test.rb +2 -0
- data/lib/fluent/test/startup_shutdown.rb +33 -0
- data/lib/fluent/version.rb +1 -1
- data/lib/fluent/winsvc.rb +1 -2
- data/test/compat/test_calls_super.rb +2 -0
- data/test/config/test_configurable.rb +88 -0
- data/test/config/test_types.rb +7 -3
- data/test/plugin/test_filter.rb +121 -23
- data/test/plugin/test_filter_record_transformer.rb +22 -6
- data/test/plugin/test_in_debug_agent.rb +2 -2
- data/test/plugin/test_in_forward.rb +920 -407
- data/test/plugin/test_in_monitor_agent.rb +333 -0
- data/test/plugin/test_in_tail.rb +686 -595
- data/test/plugin/test_out_forward.rb +150 -207
- data/test/plugin/test_out_null.rb +85 -9
- data/test/plugin/test_out_secondary_file.rb +432 -0
- data/test/plugin/test_out_stdout.rb +143 -45
- data/test/plugin/test_output.rb +53 -0
- data/test/plugin/test_output_as_buffered.rb +13 -0
- data/test/plugin/test_output_as_buffered_overflow.rb +3 -0
- data/test/plugin/test_output_as_buffered_retries.rb +11 -0
- data/test/plugin/test_output_as_buffered_secondary.rb +12 -0
- data/test/plugin/test_output_as_standard.rb +12 -0
- data/test/plugin_helper/test_compat_parameters.rb +2 -0
- data/test/plugin_helper/test_timer.rb +31 -0
- data/test/test_event_router.rb +87 -0
- data/test/test_filter.rb +48 -6
- data/test/test_log.rb +5 -2
- data/test/test_output.rb +41 -1
- data/test/test_plugin_classes.rb +17 -9
- data/test/test_root_agent.rb +188 -0
- metadata +62 -68
- data/lib/fluent/plugin/out_buffered_null.rb +0 -59
- data/lib/fluent/plugin/out_buffered_stdout.rb +0 -70
- data/test/plugin/test_out_buffered_null.rb +0 -79
- data/test/plugin/test_out_buffered_stdout.rb +0 -122
|
@@ -42,6 +42,8 @@ module Fluent
|
|
|
42
42
|
config_param :linger_timeout, :integer, default: 0
|
|
43
43
|
# This option is for Cool.io's loop wait timeout to avoid loop stuck at shutdown. Almost users don't need to change this value.
|
|
44
44
|
config_param :blocking_timeout, :time, default: 0.5
|
|
45
|
+
desc 'Connections will be disconnected right after receiving first message if this value is true.'
|
|
46
|
+
config_param :deny_keepalive, :bool, default: false
|
|
45
47
|
|
|
46
48
|
desc 'Log warning if received chunk size is larger than this value.'
|
|
47
49
|
config_param :chunk_size_warn_limit, :size, default: nil
|
|
@@ -49,9 +51,80 @@ module Fluent
|
|
|
49
51
|
config_param :chunk_size_limit, :size, default: nil
|
|
50
52
|
desc 'Skip an event if incoming event is invalid.'
|
|
51
53
|
config_param :skip_invalid_event, :bool, default: false
|
|
54
|
+
desc "The field name of the client's hostname."
|
|
55
|
+
config_param :source_hostname_key, :string, default: nil
|
|
56
|
+
|
|
57
|
+
config_section :security, required: false, multi: false do
|
|
58
|
+
desc 'The hostname'
|
|
59
|
+
config_param :self_hostname, :string
|
|
60
|
+
desc 'Shared key for authentication'
|
|
61
|
+
config_param :shared_key, :string
|
|
62
|
+
desc 'If true, use user based authentication'
|
|
63
|
+
config_param :user_auth, :bool, default: false
|
|
64
|
+
desc 'Allow anonymous source. <client> sections required if disabled.'
|
|
65
|
+
config_param :allow_anonymous_source, :bool, default: true
|
|
66
|
+
|
|
67
|
+
### User based authentication
|
|
68
|
+
config_section :user, param_name: :users, required: false, multi: true do
|
|
69
|
+
desc 'The username for authentication'
|
|
70
|
+
config_param :username, :string
|
|
71
|
+
desc 'The password for authentication'
|
|
72
|
+
config_param :password, :string
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
### Client ip/network authentication & per_host shared key
|
|
76
|
+
config_section :client, param_name: :clients, required: false, multi: true do
|
|
77
|
+
desc 'The IP address or host name of the client'
|
|
78
|
+
config_param :host, :string, default: nil
|
|
79
|
+
desc 'Network address specification'
|
|
80
|
+
config_param :network, :string, default: nil
|
|
81
|
+
desc 'Shared key per client'
|
|
82
|
+
config_param :shared_key, :string, default: nil
|
|
83
|
+
desc 'Array of username.'
|
|
84
|
+
config_param :users, :array, default: []
|
|
85
|
+
end
|
|
86
|
+
end
|
|
52
87
|
|
|
53
88
|
def configure(conf)
|
|
54
89
|
super
|
|
90
|
+
|
|
91
|
+
if @security
|
|
92
|
+
if @security.user_auth && @security.users.empty?
|
|
93
|
+
raise Fluent::ConfigError, "<user> sections required if user_auth enabled"
|
|
94
|
+
end
|
|
95
|
+
if !@security.allow_anonymous_source && @security.clients.empty?
|
|
96
|
+
raise Fluent::ConfigError, "<client> sections required if allow_anonymous_source disabled"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
@nodes = []
|
|
100
|
+
|
|
101
|
+
@security.clients.each do |client|
|
|
102
|
+
if client.host && client.network
|
|
103
|
+
raise Fluent::ConfigError, "both of 'host' and 'network' are specified for client"
|
|
104
|
+
end
|
|
105
|
+
if !client.host && !client.network
|
|
106
|
+
raise Fluent::ConfigError, "Either of 'host' and 'network' must be specified for client"
|
|
107
|
+
end
|
|
108
|
+
source = nil
|
|
109
|
+
if client.host
|
|
110
|
+
begin
|
|
111
|
+
source = IPSocket.getaddress(client.host)
|
|
112
|
+
rescue SocketError => e
|
|
113
|
+
raise Fluent::ConfigError, "host '#{client.host}' cannot be resolved"
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
source_addr = begin
|
|
117
|
+
IPAddr.new(source || client.network)
|
|
118
|
+
rescue ArgumentError => e
|
|
119
|
+
raise Fluent::ConfigError, "network '#{client.network}' address format is invalid"
|
|
120
|
+
end
|
|
121
|
+
@nodes.push({
|
|
122
|
+
address: source_addr,
|
|
123
|
+
shared_key: (client.shared_key || @security.shared_key),
|
|
124
|
+
users: client.users
|
|
125
|
+
})
|
|
126
|
+
end
|
|
127
|
+
end
|
|
55
128
|
end
|
|
56
129
|
|
|
57
130
|
def start
|
|
@@ -98,7 +171,7 @@ module Fluent
|
|
|
98
171
|
def listen(client)
|
|
99
172
|
log.info "listening fluent socket on #{@bind}:#{@port}"
|
|
100
173
|
sock = client.listen_tcp(@bind, @port)
|
|
101
|
-
s = Coolio::TCPServer.new(sock, nil, Handler, @linger_timeout, log, method(:
|
|
174
|
+
s = Coolio::TCPServer.new(sock, nil, Handler, @linger_timeout, log, method(:handle_connection))
|
|
102
175
|
s.listen(@backlog) unless @backlog.nil?
|
|
103
176
|
s
|
|
104
177
|
end
|
|
@@ -122,6 +195,99 @@ module Fluent
|
|
|
122
195
|
|
|
123
196
|
private
|
|
124
197
|
|
|
198
|
+
def handle_connection(conn)
|
|
199
|
+
send_data = ->(serializer, data){ conn.write serializer.call(data) }
|
|
200
|
+
|
|
201
|
+
log.trace "connected fluent socket", address: conn.remote_addr, port: conn.remote_port
|
|
202
|
+
state = :established
|
|
203
|
+
nonce = nil
|
|
204
|
+
user_auth_salt = nil
|
|
205
|
+
|
|
206
|
+
if @security
|
|
207
|
+
# security enabled session MUST use MessagePack as serialization format
|
|
208
|
+
state = :helo
|
|
209
|
+
nonce = generate_salt
|
|
210
|
+
user_auth_salt = generate_salt
|
|
211
|
+
send_data.call(:to_msgpack.to_proc, generate_helo(nonce, user_auth_salt))
|
|
212
|
+
state = :pingpong
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
log.trace "accepted fluent socket", address: conn.remote_addr, port: conn.remote_port
|
|
216
|
+
|
|
217
|
+
read_messages(conn) do |msg, chunk_size, serializer|
|
|
218
|
+
case state
|
|
219
|
+
when :pingpong
|
|
220
|
+
success, reason_or_salt, shared_key = check_ping(msg, conn.remote_addr, user_auth_salt, nonce)
|
|
221
|
+
unless success
|
|
222
|
+
send_data.call(serializer, generate_pong(false, reason_or_salt, nonce, shared_key))
|
|
223
|
+
conn.close
|
|
224
|
+
next
|
|
225
|
+
end
|
|
226
|
+
send_data.call(serializer, generate_pong(true, reason_or_salt, nonce, shared_key))
|
|
227
|
+
|
|
228
|
+
log.debug "connection established", address: conn.remote_addr, port: conn.remote_port
|
|
229
|
+
state = :established
|
|
230
|
+
when :established
|
|
231
|
+
options = on_message(msg, chunk_size, conn.remote_addr)
|
|
232
|
+
if options && r = response(options)
|
|
233
|
+
send_data.call(serializer, r)
|
|
234
|
+
log.trace "sent response to fluent socket", address: conn.remote_addr, response: r
|
|
235
|
+
if @deny_keepalive
|
|
236
|
+
conn.on_write_complete do
|
|
237
|
+
conn.close
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
else
|
|
241
|
+
if @deny_keepalive
|
|
242
|
+
conn.close
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
else
|
|
246
|
+
raise "BUG: unknown session state: #{state}"
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def read_messages(conn, &block)
|
|
252
|
+
feeder = nil
|
|
253
|
+
serializer = nil
|
|
254
|
+
bytes = 0
|
|
255
|
+
conn.on_data do |data|
|
|
256
|
+
# only for first call of callback
|
|
257
|
+
unless feeder
|
|
258
|
+
first = data[0]
|
|
259
|
+
if first == '{' || first == '[' # json
|
|
260
|
+
parser = Yajl::Parser.new
|
|
261
|
+
parser.on_parse_complete = ->(obj){
|
|
262
|
+
block.call(obj, bytes, serializer)
|
|
263
|
+
bytes = 0
|
|
264
|
+
}
|
|
265
|
+
serializer = :to_json.to_proc
|
|
266
|
+
feeder = ->(d){ parser << d }
|
|
267
|
+
else # msgpack
|
|
268
|
+
parser = Fluent::Engine.msgpack_factory.unpacker
|
|
269
|
+
serializer = :to_msgpack.to_proc
|
|
270
|
+
feeder = ->(d){
|
|
271
|
+
parser.feed_each(d){|obj|
|
|
272
|
+
block.call(obj, bytes, serializer)
|
|
273
|
+
bytes = 0
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
bytes += data.bytesize
|
|
280
|
+
feeder.call(data)
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def response(option)
|
|
285
|
+
if option && option['chunk']
|
|
286
|
+
return { 'ack' => option['chunk'] }
|
|
287
|
+
end
|
|
288
|
+
nil
|
|
289
|
+
end
|
|
290
|
+
|
|
125
291
|
# message Entry {
|
|
126
292
|
# 1: long time
|
|
127
293
|
# 2: object record
|
|
@@ -145,7 +311,7 @@ module Fluent
|
|
|
145
311
|
# 3: object record
|
|
146
312
|
# 4: object option (optional)
|
|
147
313
|
# }
|
|
148
|
-
def on_message(msg, chunk_size,
|
|
314
|
+
def on_message(msg, chunk_size, peeraddr)
|
|
149
315
|
if msg.nil?
|
|
150
316
|
# for future TCP heartbeat_request
|
|
151
317
|
return
|
|
@@ -153,7 +319,7 @@ module Fluent
|
|
|
153
319
|
|
|
154
320
|
# TODO: raise an exception if broken chunk is generated by recoverable situation
|
|
155
321
|
unless msg.is_a?(Array)
|
|
156
|
-
log.warn "incoming chunk is broken:", source:
|
|
322
|
+
log.warn "incoming chunk is broken:", source: source_message(peeraddr), msg: msg
|
|
157
323
|
return
|
|
158
324
|
end
|
|
159
325
|
|
|
@@ -161,24 +327,26 @@ module Fluent
|
|
|
161
327
|
entries = msg[1]
|
|
162
328
|
|
|
163
329
|
if @chunk_size_limit && (chunk_size > @chunk_size_limit)
|
|
164
|
-
log.warn "Input chunk size is larger than 'chunk_size_limit', dropped:", tag: tag, source:
|
|
330
|
+
log.warn "Input chunk size is larger than 'chunk_size_limit', dropped:", tag: tag, source: source_message(peeraddr), limit: @chunk_size_limit, size: chunk_size
|
|
165
331
|
return
|
|
166
332
|
elsif @chunk_size_warn_limit && (chunk_size > @chunk_size_warn_limit)
|
|
167
|
-
log.warn "Input chunk size is larger than 'chunk_size_warn_limit':", tag: tag, source:
|
|
333
|
+
log.warn "Input chunk size is larger than 'chunk_size_warn_limit':", tag: tag, source: source_message(peeraddr), limit: @chunk_size_warn_limit, size: chunk_size
|
|
168
334
|
end
|
|
169
335
|
|
|
170
|
-
|
|
336
|
+
case entries
|
|
337
|
+
when String
|
|
171
338
|
# PackedForward
|
|
172
339
|
option = msg[2]
|
|
173
340
|
size = (option && option['size']) || 0
|
|
174
341
|
es = MessagePackEventStream.new(entries, nil, size.to_i)
|
|
175
|
-
es = check_and_skip_invalid_event(tag, es,
|
|
342
|
+
es = check_and_skip_invalid_event(tag, es, peeraddr) if @skip_invalid_event
|
|
343
|
+
es = add_source_host(es, peeraddr[2]) if @source_hostname_key
|
|
176
344
|
router.emit_stream(tag, es)
|
|
177
345
|
|
|
178
|
-
|
|
346
|
+
when Array
|
|
179
347
|
# Forward
|
|
180
348
|
es = if @skip_invalid_event
|
|
181
|
-
check_and_skip_invalid_event(tag, entries,
|
|
349
|
+
check_and_skip_invalid_event(tag, entries, peeraddr)
|
|
182
350
|
else
|
|
183
351
|
es = MultiEventStream.new
|
|
184
352
|
entries.each { |e|
|
|
@@ -190,6 +358,7 @@ module Fluent
|
|
|
190
358
|
}
|
|
191
359
|
es
|
|
192
360
|
end
|
|
361
|
+
es = add_source_host(es, peeraddr[2]) if @source_hostname_key
|
|
193
362
|
router.emit_stream(tag, es)
|
|
194
363
|
option = msg[2]
|
|
195
364
|
|
|
@@ -198,11 +367,12 @@ module Fluent
|
|
|
198
367
|
time = msg[1]
|
|
199
368
|
record = msg[2]
|
|
200
369
|
if @skip_invalid_event && invalid_event?(tag, time, record)
|
|
201
|
-
log.warn "got invalid event and drop it:", source:
|
|
370
|
+
log.warn "got invalid event and drop it:", source: source_message(peeraddr), tag: tag, time: time, record: record
|
|
202
371
|
return msg[3] # retry never succeeded so return ack and drop incoming event.
|
|
203
372
|
end
|
|
204
373
|
return if record.nil?
|
|
205
374
|
time = Engine.now if time.to_i == 0
|
|
375
|
+
record[@source_hostname_key] = peeraddr[2] if @source_hostname_key
|
|
206
376
|
router.emit(tag, time, record)
|
|
207
377
|
option = msg[3]
|
|
208
378
|
end
|
|
@@ -215,11 +385,11 @@ module Fluent
|
|
|
215
385
|
!((time.is_a?(Integer) || time.is_a?(::Fluent::EventTime)) && record.is_a?(Hash) && tag.is_a?(String))
|
|
216
386
|
end
|
|
217
387
|
|
|
218
|
-
def check_and_skip_invalid_event(tag, es,
|
|
388
|
+
def check_and_skip_invalid_event(tag, es, peeraddr)
|
|
219
389
|
new_es = MultiEventStream.new
|
|
220
390
|
es.each { |time, record|
|
|
221
391
|
if invalid_event?(tag, time, record)
|
|
222
|
-
log.warn "skip invalid event:", source:
|
|
392
|
+
log.warn "skip invalid event:", source: source_message(peeraddr), tag: tag, time: time, record: record
|
|
223
393
|
next
|
|
224
394
|
end
|
|
225
395
|
new_es.add(time, record)
|
|
@@ -227,22 +397,143 @@ module Fluent
|
|
|
227
397
|
new_es
|
|
228
398
|
end
|
|
229
399
|
|
|
400
|
+
def add_source_host(es, host)
|
|
401
|
+
new_es = MultiEventStream.new
|
|
402
|
+
es.each { |time, record|
|
|
403
|
+
record[@source_hostname_key] = host
|
|
404
|
+
new_es.add(time, record)
|
|
405
|
+
}
|
|
406
|
+
new_es
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
def source_message(peeraddr)
|
|
410
|
+
_, port, host, addr = peeraddr
|
|
411
|
+
"host: #{host}, addr: #{addr}, port: #{port}"
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
def select_authenticate_users(node, username)
|
|
415
|
+
if node.nil? || node[:users].empty?
|
|
416
|
+
@security.users.select{|u| u.username == username}
|
|
417
|
+
else
|
|
418
|
+
@security.users.select{|u| node[:users].include?(u.username) && u.username == username}
|
|
419
|
+
end
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
def generate_salt
|
|
423
|
+
OpenSSL::Random.random_bytes(16)
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
def generate_helo(nonce, user_auth_salt)
|
|
427
|
+
log.debug "generating helo"
|
|
428
|
+
# ['HELO', options(hash)]
|
|
429
|
+
['HELO', {'nonce' => nonce, 'auth' => (@security ? user_auth_salt : ''), 'keepalive' => !@deny_keepalive}]
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
##### Authentication Handshake
|
|
433
|
+
#
|
|
434
|
+
# 1. (client) connect to server
|
|
435
|
+
# * Socket handshake, checks certificate and its significate (in client, if using SSL)
|
|
436
|
+
# 2. (server)
|
|
437
|
+
# * check network/domain acl (if enabled)
|
|
438
|
+
# * disconnect when failed
|
|
439
|
+
# 3. (server) send HELO
|
|
440
|
+
# * ['HELO', options(hash)]
|
|
441
|
+
# * options:
|
|
442
|
+
# * nonce: string (required)
|
|
443
|
+
# * auth: string or blank_string (string: authentication required, and its salt is this value)
|
|
444
|
+
# 4. (client) send PING
|
|
445
|
+
# * ['PING', selfhostname, sharedkey_salt, sha512_hex(sharedkey_salt + selfhostname + nonce + sharedkey), username || '', sha512_hex(auth_salt + username + password) || '']
|
|
446
|
+
# 5. (server) check PING
|
|
447
|
+
# * check sharedkey
|
|
448
|
+
# * check username / password (if required)
|
|
449
|
+
# * send PONG FAILURE if failed
|
|
450
|
+
# * ['PONG', false, 'reason of authentication failure', '', '']
|
|
451
|
+
# 6. (server) send PONG
|
|
452
|
+
# * ['PONG', bool(authentication result), 'reason if authentication failed', selfhostname, sha512_hex(salt + selfhostname + nonce + sharedkey)]
|
|
453
|
+
# 7. (client) check PONG
|
|
454
|
+
# * check sharedkey
|
|
455
|
+
# * disconnect when failed
|
|
456
|
+
# 8. connection established
|
|
457
|
+
# * send data from client
|
|
458
|
+
|
|
459
|
+
def check_ping(message, remote_addr, user_auth_salt, nonce)
|
|
460
|
+
log.debug "checking ping"
|
|
461
|
+
# ['PING', self_hostname, shared_key_salt, sha512_hex(shared_key_salt + self_hostname + nonce + shared_key), username || '', sha512_hex(auth_salt + username + password) || '']
|
|
462
|
+
unless message.size == 6 && message[0] == 'PING'
|
|
463
|
+
return false, 'invalid ping message'
|
|
464
|
+
end
|
|
465
|
+
_ping, hostname, shared_key_salt, shared_key_hexdigest, username, password_digest = message
|
|
466
|
+
|
|
467
|
+
node = @nodes.select{|n| n[:address].include?(remote_addr) rescue false }.first
|
|
468
|
+
if !node && !@security.allow_anonymous_source
|
|
469
|
+
log.warn "Anonymous client disallowed", address: remote_addr, hostname: hostname
|
|
470
|
+
return false, "anonymous source host '#{remote_addr}' denied", nil
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
shared_key = node ? node[:shared_key] : @security.shared_key
|
|
474
|
+
serverside = Digest::SHA512.new.update(shared_key_salt).update(hostname).update(nonce).update(shared_key).hexdigest
|
|
475
|
+
if shared_key_hexdigest != serverside
|
|
476
|
+
log.warn "Shared key mismatch", address: remote_addr, hostname: hostname
|
|
477
|
+
return false, 'shared_key mismatch', nil
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
if @security.user_auth
|
|
481
|
+
users = select_authenticate_users(node, username)
|
|
482
|
+
success = false
|
|
483
|
+
users.each do |user|
|
|
484
|
+
passhash = Digest::SHA512.new.update(user_auth_salt).update(username).update(user[:password]).hexdigest
|
|
485
|
+
success ||= (passhash == password_digest)
|
|
486
|
+
end
|
|
487
|
+
unless success
|
|
488
|
+
log.warn "Authentication failed", address: remote_addr, hostname: hostname, username: username
|
|
489
|
+
return false, 'username/password mismatch', nil
|
|
490
|
+
end
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
return true, shared_key_salt, shared_key
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
def generate_pong(auth_result, reason_or_salt, nonce, shared_key)
|
|
497
|
+
log.debug "generating pong"
|
|
498
|
+
# ['PONG', bool(authentication result), 'reason if authentication failed', self_hostname, sha512_hex(salt + self_hostname + nonce + sharedkey)]
|
|
499
|
+
unless auth_result
|
|
500
|
+
return ['PONG', false, reason_or_salt, '', '']
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
shared_key_digest_hex = Digest::SHA512.new.update(reason_or_salt).update(@security.self_hostname).update(nonce).update(shared_key).hexdigest
|
|
504
|
+
['PONG', true, '', @security.self_hostname, shared_key_digest_hex]
|
|
505
|
+
end
|
|
506
|
+
|
|
230
507
|
class Handler < Coolio::Socket
|
|
508
|
+
attr_reader :protocol, :remote_port, :remote_addr, :remote_host
|
|
509
|
+
|
|
231
510
|
PEERADDR_FAILED = ["?", "?", "name resolusion failed", "?"]
|
|
232
511
|
|
|
233
|
-
def initialize(io, linger_timeout, log,
|
|
512
|
+
def initialize(io, linger_timeout, log, on_connect_callback)
|
|
234
513
|
super(io)
|
|
235
514
|
|
|
515
|
+
@peeraddr = nil
|
|
236
516
|
if io.is_a?(TCPSocket) # for unix domain socket support in the future
|
|
237
|
-
|
|
238
|
-
@source = "host: #{host}, addr: #{addr}, port: #{port}"
|
|
239
|
-
|
|
517
|
+
@peeraddr = (io.peeraddr rescue PEERADDR_FAILED)
|
|
240
518
|
opt = [1, linger_timeout].pack('I!I!') # { int l_onoff; int l_linger; }
|
|
241
519
|
io.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, opt)
|
|
242
520
|
end
|
|
243
521
|
|
|
522
|
+
### TODO: disabling name rev resolv
|
|
523
|
+
proto, port, host, addr = ( io.peeraddr rescue PEERADDR_FAILED )
|
|
524
|
+
if addr == '?'
|
|
525
|
+
port, addr = *Socket.unpack_sockaddr_in(io.getpeername) rescue nil
|
|
526
|
+
end
|
|
527
|
+
@protocol = proto
|
|
528
|
+
@remote_port = port
|
|
529
|
+
@remote_addr = addr
|
|
530
|
+
@remote_host = host
|
|
531
|
+
@writing = false
|
|
532
|
+
@closing = false
|
|
533
|
+
@mutex = Mutex.new
|
|
534
|
+
|
|
244
535
|
@chunk_counter = 0
|
|
245
|
-
@
|
|
536
|
+
@on_connect_callback = on_connect_callback
|
|
246
537
|
@log = log
|
|
247
538
|
@log.trace {
|
|
248
539
|
begin
|
|
@@ -251,69 +542,46 @@ module Fluent
|
|
|
251
542
|
remote_port = nil
|
|
252
543
|
remote_addr = nil
|
|
253
544
|
end
|
|
254
|
-
"accepted fluent socket
|
|
545
|
+
[ "accepted fluent socket", {address: remote_addr, port: remote_port, instance: self.object_id} ]
|
|
255
546
|
}
|
|
256
547
|
end
|
|
257
548
|
|
|
258
549
|
def on_connect
|
|
550
|
+
@on_connect_callback.call(self)
|
|
259
551
|
end
|
|
260
552
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
m = method(:on_read_json)
|
|
265
|
-
@serializer = :to_json.to_proc
|
|
266
|
-
@y = Yajl::Parser.new
|
|
267
|
-
@y.on_parse_complete = lambda { |obj|
|
|
268
|
-
option = @on_message.call(obj, @chunk_counter, @source)
|
|
269
|
-
respond option
|
|
270
|
-
@chunk_counter = 0
|
|
271
|
-
}
|
|
272
|
-
else
|
|
273
|
-
m = method(:on_read_msgpack)
|
|
274
|
-
@serializer = :to_msgpack.to_proc
|
|
275
|
-
@u = Fluent::Engine.msgpack_factory.unpacker
|
|
276
|
-
end
|
|
277
|
-
|
|
278
|
-
(class << self; self; end).module_eval do
|
|
279
|
-
define_method(:on_read, m)
|
|
280
|
-
end
|
|
281
|
-
m.call(data)
|
|
553
|
+
# API to register callback for data arrival
|
|
554
|
+
def on_data(&callback)
|
|
555
|
+
@on_read_callback = callback
|
|
282
556
|
end
|
|
283
557
|
|
|
284
|
-
def
|
|
285
|
-
@
|
|
286
|
-
@y << data
|
|
558
|
+
def on_read(data)
|
|
559
|
+
@on_read_callback.call(data)
|
|
287
560
|
rescue => e
|
|
288
|
-
@log.error "
|
|
561
|
+
@log.error "unexpected error on reading data from client", address: @remote_addr, error: e
|
|
289
562
|
@log.error_backtrace
|
|
290
563
|
close
|
|
291
564
|
end
|
|
292
565
|
|
|
293
|
-
def
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
566
|
+
def on_write_complete
|
|
567
|
+
closing = @mutex.synchronize {
|
|
568
|
+
@writing = false
|
|
569
|
+
@closing
|
|
570
|
+
}
|
|
571
|
+
if closing
|
|
572
|
+
close
|
|
299
573
|
end
|
|
300
|
-
rescue => e
|
|
301
|
-
@log.error "forward error", error: e, error_class: e.class
|
|
302
|
-
@log.error_backtrace
|
|
303
|
-
close
|
|
304
574
|
end
|
|
305
575
|
|
|
306
|
-
def
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
576
|
+
def close
|
|
577
|
+
writing = @mutex.synchronize {
|
|
578
|
+
@closing = true
|
|
579
|
+
@writing
|
|
580
|
+
}
|
|
581
|
+
unless writing
|
|
582
|
+
super
|
|
311
583
|
end
|
|
312
584
|
end
|
|
313
|
-
|
|
314
|
-
def on_close
|
|
315
|
-
@log.trace { "closed socket" }
|
|
316
|
-
end
|
|
317
585
|
end
|
|
318
586
|
|
|
319
587
|
class HeartbeatRequestHandler < Coolio::IO
|