fluent-plugin-secure-forward 0.1.8 → 0.1.9.pre.rc1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9299ae4d7e763b5778a45ce5c98563f3ecc4e86f
4
- data.tar.gz: b187ec1e5f56f6ad7cb854241d88b36ecae9a9f3
3
+ metadata.gz: 2fc54e54fa73f9f47d2edbc7c57727d88909718e
4
+ data.tar.gz: 17e46a3f3a58d57ab468d05972fa72da68f5d692
5
5
  SHA512:
6
- metadata.gz: d5666bc81bc5aa40d47996069b1192368080dbad8e17245cefd7929bf3962f34c46623d841a91007929a414cca24566d86da45d2f4a74e62e8a2eb7acd551470
7
- data.tar.gz: e7aee097f8536c0dde5adbda7637f02ee19c829feeeff003bed3de19cedd2b9e472d5216c040196f5a94276a13a5bb6faf9d9feb6c03fe88b19ecc8704755462
6
+ metadata.gz: df677feca1b1252f6b6b22c43c199654728d3e39b2569f20b1207d682075c75d4389b77977955992bb3d5756d620c5119e43d2d6c466986572aae5e09684d78c
7
+ data.tar.gz: e6bbbe1860763d53c4aea3534bfb5dde66b153d5580d4503c26f947cb278f6bdb64d810c6d4b04170ecd1997a25a63fdae6058e99b526af37ae5d1ecf2ad1280
data/README.md CHANGED
@@ -75,12 +75,14 @@ To deny unknown source IP/hosts:
75
75
  allow_anonymous_source no # Allow to accept from nodes of <client>
76
76
  <client>
77
77
  host 192.168.10.30
78
- # network address (ex: 192.168.10.0/24) NOT Supported now
79
78
  </client>
80
79
  <client>
81
80
  host your.host.fqdn.local
82
81
  # wildcard (ex: *.host.fqdn.local) NOT Supported now
83
82
  </client>
83
+ <client>
84
+ network 192.168.16.0/24 # network address specification
85
+ </client>
84
86
  </source>
85
87
 
86
88
  You can use both of username/password check and client check:
@@ -103,7 +105,7 @@ You can use both of username/password check and client check:
103
105
  <user>
104
106
  username repeatedly
105
107
  password sushi
106
- </user
108
+ </user>
107
109
  <client>
108
110
  host 192.168.10.30 # allow all users to connect from 192.168.10.30
109
111
  </client>
data/example/client.conf CHANGED
@@ -9,14 +9,13 @@
9
9
  keepalive 30
10
10
  <server>
11
11
  host localhost
12
- standby
13
12
  </server>
14
13
  <server>
15
14
  host localhost
16
15
  standby yes
17
16
  </server>
18
17
  <server>
19
- host localhost2
18
+ host localhost
20
19
  </server>
21
20
  flush_interval 1s
22
21
  </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.1.8"
4
+ gem.version = "0.1.9-rc1"
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}
@@ -14,7 +14,7 @@ Gem::Specification.new do |gem|
14
14
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
15
15
  gem.require_paths = ["lib"]
16
16
 
17
- gem.add_runtime_dependency "fluentd"
17
+ gem.add_runtime_dependency "fluentd", ">= 0.10.46"
18
18
  gem.add_runtime_dependency "fluent-mixin-config-placeholders"
19
19
  gem.add_runtime_dependency "resolve-hostname"
20
20
  gem.add_development_dependency "rake"
@@ -48,22 +48,24 @@ module Fluent
48
48
 
49
49
  attr_reader :read_interval, :socket_interval
50
50
 
51
- attr_reader :users # list of (username, password) by <user> tag
52
- # <user>
53
- # username ....
54
- # password ....
55
- # </user>
56
- attr_reader :nodes # list of hosts, allowed to connect <server> tag (it includes source ip, shared_key(optional))
57
- # <client>
58
- # host ipaddr/hostname
59
- # shared_key .... # optional shared key
60
- # users username,list,of,allowed
61
- # </client>
51
+ config_section :user, param_name: :users do
52
+ config_param :username, :string
53
+ config_param :password, :string
54
+ end
55
+
56
+ config_section :client, param_name: :clients do
57
+ config_param :host, :string, default: nil
58
+ config_param :network, :string, default: nil
59
+ config_param :shared_key, :string, default: nil
60
+ config_param :users, :string, default: nil # comma separated username list
61
+ end
62
+ attr_reader :nodes
62
63
 
63
64
  attr_reader :sessions # node/socket/thread list which has sslsocket instance keepaliving to client
64
65
 
65
66
  def initialize
66
67
  super
68
+ require 'ipaddr'
67
69
  require 'socket'
68
70
  require 'openssl'
69
71
  require 'digest'
@@ -84,30 +86,33 @@ module Fluent
84
86
  @read_interval = @read_interval_msec / 1000.0
85
87
  @socket_interval = @socket_interval_msec / 1000.0
86
88
 
87
- @users = []
88
89
  @nodes = []
89
- conf.elements.each do |element|
90
- case element.name
91
- when 'user'
92
- unless element['username'] && element['password']
93
- raise Fluent::ConfigError, "username/password pair missing in <user>"
94
- end
95
- @users.push({
96
- username: element['username'],
97
- password: element['password']
98
- })
99
- when 'client'
100
- unless element['host']
101
- raise Fluent::ConfigError, "host missing in <client>"
90
+
91
+ @clients.each do |client|
92
+ if client.host && client.network
93
+ raise Fluent::ConfigError, "both of 'host' and 'network' are specified for client"
94
+ end
95
+ if !client.host && !client.network
96
+ raise Fluent::ConfigError, "Either of 'host' and 'network' must be specified for client"
97
+ end
98
+ source = nil
99
+ if client.host
100
+ begin
101
+ source = IPSocket.getaddress(client.host)
102
+ rescue SocketError => e
103
+ raise Fluent::ConfigError, "host '#{client.host}' cannot be resolved"
102
104
  end
103
- @nodes.push({
104
- host: element['host'],
105
- shared_key: (element['shared_key'] || @shared_key),
106
- users: (element['users'] ? element['users'].split(',') : nil),
107
- })
108
- else
109
- raise Fluent::ConfigError, "unknown config tag name"
110
105
  end
106
+ source_addr = begin
107
+ IPAddr.new(source || client.network)
108
+ rescue ArgumentError => e
109
+ raise Fluent::ConfigError, "network '#{client.network}' address format is invalid"
110
+ end
111
+ @nodes.push({
112
+ address: source_addr,
113
+ shared_key: (client.shared_key || @shared_key),
114
+ users: (client.users ? client.users.split(',') : nil)
115
+ })
111
116
  end
112
117
 
113
118
  @generate_cert_common_name ||= @self_hostname
@@ -132,9 +137,9 @@ module Fluent
132
137
 
133
138
  def select_authenticate_users(node, username)
134
139
  if node.nil? || node[:users].nil?
135
- @users.select{|u| u[:username] == username}
140
+ @users.select{|u| u.username == username}
136
141
  else
137
- @users.select{|u| node[:users].include?(u[:username]) && u[:username] == username}
142
+ @users.select{|u| node[:users].include?(u.username) && u.username == username}
138
143
  end
139
144
  end
140
145
 
@@ -1,8 +1,8 @@
1
- # require 'msgpack'
2
- # require 'socket'
3
- # require 'openssl'
4
- # require 'digest'
5
- ### require 'resolv'
1
+ require 'msgpack'
2
+ require 'socket'
3
+ require 'openssl'
4
+ require 'digest'
5
+ # require 'resolv'
6
6
 
7
7
  class Fluent::SecureForwardInput::Session
8
8
  attr_accessor :receiver
@@ -22,6 +22,10 @@ class Fluent::SecureForwardInput::Session
22
22
  @thread = Thread.new(&method(:start))
23
23
  end
24
24
 
25
+ def log
26
+ @receiver.log
27
+ end
28
+
25
29
  def established?
26
30
  @state == :established
27
31
  end
@@ -30,12 +34,10 @@ class Fluent::SecureForwardInput::Session
30
34
  OpenSSL::Random.random_bytes(16)
31
35
  end
32
36
 
33
- def check_node(hostname, ipaddress, port, proto)
37
+ def check_node(ipaddress)
34
38
  node = nil
35
- family = Socket.const_get(proto)
36
39
  @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
40
+ if n[:address].include?(ipaddress)
39
41
  node = n
40
42
  break
41
43
  end
@@ -54,13 +56,13 @@ class Fluent::SecureForwardInput::Session
54
56
  # end
55
57
 
56
58
  def generate_helo
57
- $log.debug "generating helo"
59
+ log.debug "generating helo"
58
60
  # ['HELO', options(hash)]
59
61
  [ 'HELO', {'auth' => (@receiver.authentication ? @auth_key_salt : ''), 'keepalive' => @receiver.allow_keepalive } ]
60
62
  end
61
63
 
62
64
  def check_ping(message)
63
- $log.debug "checking ping"
65
+ log.debug "checking ping"
64
66
  # ['PING', self_hostname, shared_key\_salt, sha512\_hex(shared_key\_salt + self_hostname + shared_key),
65
67
  # username || '', sha512\_hex(auth\_salt + username + password) || '']
66
68
  unless message.size == 6 && message[0] == 'PING'
@@ -75,7 +77,7 @@ class Fluent::SecureForwardInput::Session
75
77
  end
76
78
  serverside = Digest::SHA512.new.update(shared_key_salt).update(hostname).update(shared_key).hexdigest
77
79
  if shared_key_hexdigest != serverside
78
- $log.warn "Shared key mismatch from '#{hostname}'"
80
+ log.warn "Shared key mismatch from '#{hostname}'"
79
81
  return false, 'shared_key mismatch'
80
82
  end
81
83
 
@@ -87,7 +89,7 @@ class Fluent::SecureForwardInput::Session
87
89
  success ||= (passhash == password_digest)
88
90
  end
89
91
  unless success
90
- $log.warn "Authentication failed from client '#{hostname}', username '#{username}'"
92
+ log.warn "Authentication failed from client '#{hostname}', username '#{username}'"
91
93
  return false, 'username/password mismatch'
92
94
  end
93
95
  end
@@ -96,7 +98,7 @@ class Fluent::SecureForwardInput::Session
96
98
  end
97
99
 
98
100
  def generate_pong(auth_result, reason_or_salt)
99
- $log.debug "generating pong"
101
+ log.debug "generating pong"
100
102
  # ['PONG', bool(authentication result), 'reason if authentication failed',
101
103
  # self_hostname, sha512\_hex(salt + self_hostname + sharedkey)]
102
104
  if not auth_result
@@ -113,7 +115,7 @@ class Fluent::SecureForwardInput::Session
113
115
  end
114
116
 
115
117
  def on_read(data)
116
- $log.debug "on_read"
118
+ log.debug "on_read"
117
119
  if self.established?
118
120
  @receiver.on_message(data)
119
121
  end
@@ -128,7 +130,7 @@ class Fluent::SecureForwardInput::Session
128
130
  end
129
131
  send_data generate_pong(true, reason_or_salt)
130
132
 
131
- $log.debug "connection established"
133
+ log.debug "connection established"
132
134
  @state = :established
133
135
  end
134
136
  end
@@ -139,21 +141,21 @@ class Fluent::SecureForwardInput::Session
139
141
  end
140
142
 
141
143
  def start
142
- $log.debug "starting server"
144
+ log.debug "starting server"
143
145
 
144
- $log.trace "accepting ssl session"
146
+ log.trace "accepting ssl session"
145
147
  begin
146
148
  @socket.accept
147
149
  rescue OpenSSL::SSL::SSLError => e
148
- $log.debug "failed to establish ssl session"
150
+ log.debug "failed to establish ssl session"
149
151
  self.shutdown
150
152
  return
151
153
  end
152
154
 
153
155
  proto, port, host, ipaddr = @socket.io.peeraddr
154
- @node = check_node(host, ipaddr, port, proto)
156
+ @node = check_node(ipaddr)
155
157
  if @node.nil? && (! @receiver.allow_anonymous_source)
156
- $log.warn "Connection required from unknown host '#{host}' (#{ipaddr}), disconnecting..."
158
+ log.warn "Connection required from unknown host '#{host}' (#{ipaddr}), disconnecting..."
157
159
  self.shutdown
158
160
  return
159
161
  end
@@ -182,14 +184,14 @@ class Fluent::SecureForwardInput::Session
182
184
  # to wait i/o restart
183
185
  sleep socket_interval
184
186
  rescue EOFError => e
185
- $log.debug "Connection closed from '#{host}'(#{ipaddr})"
187
+ log.debug "Connection closed from '#{host}'(#{ipaddr})"
186
188
  break
187
189
  end
188
190
  end
189
191
  rescue Errno::ECONNRESET => e
190
192
  # disconnected from client
191
193
  rescue => e
192
- $log.warn "unexpected error in in_secure_forward", :error_class => e.class, :error => e
194
+ log.warn "unexpected error in in_secure_forward", :error_class => e.class, :error => e
193
195
  ensure
194
196
  self.shutdown
195
197
  end
@@ -207,6 +209,6 @@ class Fluent::SecureForwardInput::Session
207
209
  @socket.close
208
210
  end
209
211
  rescue => e
210
- $log.debug "#{e.class}:#{e.message}"
212
+ log.debug "#{e.class}:#{e.message}"
211
213
  end
212
214
  end
@@ -20,7 +20,7 @@ module Fluent
20
20
 
21
21
  config_param :shared_key, :string
22
22
 
23
- config_param :keepalive, :time, :default => nil # nil/0 means disable keepalive
23
+ config_param :keepalive, :time, :default => nil # nil/0 means disable keepalive expiration
24
24
 
25
25
  config_param :send_timeout, :time, :default => 60
26
26
  # config_param :hard_timeout, :time, :default => 60
@@ -34,6 +34,7 @@ module Fluent
34
34
  config_param :socket_interval_msec, :integer, :default => 200 # 200ms
35
35
 
36
36
  config_param :reconnect_interval, :time, :default => 5
37
+ config_param :established_timeout, :time, :default => 10
37
38
 
38
39
  attr_reader :read_interval, :socket_interval
39
40
 
@@ -129,6 +130,8 @@ module Fluent
129
130
  end
130
131
 
131
132
  def node_watcher
133
+ reconnectings = Array.new(@nodes.size)
134
+
132
135
  loop do
133
136
  sleep @reconnect_interval
134
137
 
@@ -139,17 +142,51 @@ module Fluent
139
142
 
140
143
  next if @nodes[i].established? && ! @nodes[i].expired?
141
144
 
145
+ next if reconnectings[i]
146
+
142
147
  log.info "dead connection found: #{@nodes[i].host}, reconnecting..." unless @nodes[i].established?
143
148
 
144
149
  node = @nodes[i]
145
150
  log.debug "reconnecting to node", :host => node.host, :port => node.port, :expire => node.expire, :expired => node.expired?
146
151
 
147
- @nodes[i] = node.dup
148
- @nodes[i].start
152
+ renewed = node.dup
153
+ begin
154
+ renewed.start
155
+ Thread.pass # to connection thread
156
+ reconnectings[i] = { :conn => renewed, :at => Time.now }
157
+ rescue => e
158
+ log.debug "Some error occured on start of renewed connection", :error_class => e2.class, :error => e2, :host => renewed.host, :port => renewed.port
159
+ end
160
+ end
161
+
162
+ (0...(reconnectings.size)).each do |i|
163
+ next unless reconnectings[i]
164
+
165
+ if reconnectings[i][:conn].established?
166
+ oldconn = @nodes[i]
167
+ @nodes[i] = reconnectings[i][:conn]
168
+ begin
169
+ oldconn.shutdown
170
+ rescue => e
171
+ log.debug "Some error occured on shutdown of expired connection", :error_class => e.class, :error => e, :host => renewed.host, :port => renewed.port
172
+ end
173
+
174
+ reconnectings[i] = nil
175
+ next
176
+ end
177
+
178
+ # not connected yet
179
+
180
+ next if reconnectings[i][:at] < Time.now + @established_timeout
181
+
182
+ # not connected yet, and timeout
149
183
  begin
150
- node.shutdown
184
+ timeout_conn = reconnectings[i][:conn]
185
+ log.debug "SSL connection is not established until timemout", :host => timeout_conn.host, :port => timeout_conn.port, :timeout => @established_timeout
186
+ reconnectings[i] = nil
187
+ timeout_conn.shutdown
151
188
  rescue => e
152
- log.warn "error in shutdown of dead connection", :error_class => e.class, :error => e
189
+ log.debug "Some error occured on shutdown of timeout re-connection", :error_class => e.class, :error => e
153
190
  end
154
191
  end
155
192
  end
@@ -43,6 +43,10 @@ class Fluent::SecureForwardOutput::Node
43
43
  @thread = nil
44
44
  end
45
45
 
46
+ def log
47
+ @sender.log
48
+ end
49
+
46
50
  def dup
47
51
  renewed = self.class.new(
48
52
  @sender,
@@ -58,7 +62,7 @@ class Fluent::SecureForwardOutput::Node
58
62
  end
59
63
 
60
64
  def shutdown
61
- $log.debug "shutting down node #{@host}"
65
+ log.debug "shutting down node #{@host}"
62
66
  @state = :closed
63
67
 
64
68
  if @thread == Thread.current
@@ -74,7 +78,7 @@ class Fluent::SecureForwardOutput::Node
74
78
  @socket.close if @socket
75
79
  end
76
80
  rescue => e
77
- $log.debug "error on node shutdown #{e.class}:#{e.message}"
81
+ log.debug "error on node shutdown #{e.class}:#{e.message}"
78
82
  end
79
83
 
80
84
  def join
@@ -98,7 +102,7 @@ class Fluent::SecureForwardOutput::Node
98
102
  end
99
103
 
100
104
  def check_helo(message)
101
- $log.debug "checking helo"
105
+ log.debug "checking helo"
102
106
  # ['HELO', options(hash)]
103
107
  unless message.size == 2 && message[0] == 'HELO'
104
108
  return false
@@ -110,7 +114,7 @@ class Fluent::SecureForwardOutput::Node
110
114
  end
111
115
 
112
116
  def generate_ping
113
- $log.debug "generating ping"
117
+ log.debug "generating ping"
114
118
  # ['PING', self_hostname, sharedkey\_salt, sha512\_hex(sharedkey\_salt + self_hostname + shared_key),
115
119
  # username || '', sha512\_hex(auth\_salt + username + password) || '']
116
120
  shared_key_hexdigest = Digest::SHA512.new.update(@shared_key_salt).update(@sender.self_hostname).update(@shared_key).hexdigest
@@ -125,7 +129,7 @@ class Fluent::SecureForwardOutput::Node
125
129
  end
126
130
 
127
131
  def check_pong(message)
128
- $log.debug "checking pong"
132
+ log.debug "checking pong"
129
133
  # ['PONG', bool(authentication result), 'reason if authentication failed',
130
134
  # self_hostname, sha512\_hex(salt + self_hostname + sharedkey)]
131
135
  unless message.size == 5 && message[0] == 'PONG'
@@ -150,17 +154,17 @@ class Fluent::SecureForwardOutput::Node
150
154
  end
151
155
 
152
156
  def on_read(data)
153
- $log.debug "on_read"
157
+ log.debug "on_read"
154
158
  if self.established?
155
159
  #TODO: ACK
156
- $log.warn "unknown packets arrived..."
160
+ log.warn "unknown packets arrived..."
157
161
  return
158
162
  end
159
163
 
160
164
  case @state
161
165
  when :helo
162
166
  unless check_helo(data)
163
- $log.warn "received invalid helo message from #{@host}"
167
+ log.warn "received invalid helo message from #{@host}"
164
168
  self.shutdown
165
169
  return
166
170
  end
@@ -169,25 +173,25 @@ class Fluent::SecureForwardOutput::Node
169
173
  when :pingpong
170
174
  success, reason = check_pong(data)
171
175
  unless success
172
- $log.warn "connection refused to #{@host}:" + reason
176
+ log.warn "connection refused to #{@host}:" + reason
173
177
  self.shutdown
174
178
  return
175
179
  end
176
- $log.info "connection established to #{@host}" if @first_session
180
+ log.info "connection established to #{@host}" if @first_session
177
181
  @state = :established
178
182
  @expire = Time.now + @keepalive if @keepalive && @keepalive > 0
179
- $log.debug "connection established", :host => @host, :port => @port, :expire => @expire
183
+ log.debug "connection established", :host => @host, :port => @port, :expire => @expire
180
184
  end
181
185
  end
182
186
 
183
187
  def connect
184
- $log.debug "starting client"
188
+ log.debug "starting client"
185
189
 
186
190
  addr = @sender.hostname_resolver.getaddress(@host)
187
- $log.debug "create tcp socket to node", :host => @host, :address => addr, :port => @port
191
+ log.debug "create tcp socket to node", :host => @host, :address => addr, :port => @port
188
192
  sock = TCPSocket.new(addr, @port)
189
193
 
190
- $log.trace "changing socket options"
194
+ log.trace "changing socket options"
191
195
  opt = [1, @sender.send_timeout.to_i].pack('I!I!') # { int l_onoff; int l_linger; }
192
196
  sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, opt)
193
197
 
@@ -195,36 +199,36 @@ class Fluent::SecureForwardOutput::Node
195
199
  sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, opt)
196
200
 
197
201
  # TODO: SSLContext constructer parameter (SSL/TLS protocol version)
198
- $log.trace "initializing SSL contexts"
202
+ log.trace "initializing SSL contexts"
199
203
  context = OpenSSL::SSL::SSLContext.new
200
204
  # TODO: context.ca_file = (ca_file_path)
201
205
  # TODO: context.ciphers = (SSL Shared key chiper protocols)
202
206
 
203
- $log.debug "trying to connect ssl session", :host => @host, :ipaddr => addr, :port => @port
207
+ log.debug "trying to connect ssl session", :host => @host, :ipaddr => addr, :port => @port
204
208
  sslsession = OpenSSL::SSL::SSLSocket.new(sock, context)
205
209
  # TODO: check connection failure
206
210
  sslsession.connect
207
- $log.debug "ssl session connected", :host => @host, :port => @port
211
+ log.debug "ssl session connected", :host => @host, :port => @port
208
212
 
209
213
  begin
210
214
  unless @sender.allow_self_signed_certificate
211
- $log.debug "checking peer's certificate", :subject => sslsession.peer_cert.subject
215
+ log.debug "checking peer's certificate", :subject => sslsession.peer_cert.subject
212
216
  sslsession.post_connection_check(@hostlabel)
213
217
  verify = sslsession.verify_result
214
218
  if verify != OpenSSL::X509::V_OK
215
219
  err_name = Fluent::SecureForwardOutput::OpenSSLUtil.verify_result_name(verify)
216
- $log.warn "failed to verify certification while connecting host #{@host} as #{@hostlabel} (but not raised, why?)"
217
- $log.warn "verify_result: #{err_name}"
220
+ log.warn "failed to verify certification while connecting host #{@host} as #{@hostlabel} (but not raised, why?)"
221
+ log.warn "verify_result: #{err_name}"
218
222
  raise RuntimeError, "failed to verify certification while connecting host #{@host} as #{@hostlabel}"
219
223
  end
220
224
  end
221
225
  rescue OpenSSL::SSL::SSLError => e
222
- $log.warn "failed to verify certification while connecting ssl session", :host => @host, :hostlabel => @hostlabel
226
+ log.warn "failed to verify certification while connecting ssl session", :host => @host, :hostlabel => @hostlabel
223
227
  self.shutdown
224
228
  raise
225
229
  end
226
230
 
227
- $log.debug "ssl sessison connected", :host => @host, :port => @port
231
+ log.debug "ssl sessison connected", :host => @host, :port => @port
228
232
  @socket = sock
229
233
  @sslsession = sslsession
230
234
 
@@ -249,7 +253,7 @@ class Fluent::SecureForwardOutput::Node
249
253
  # to wait i/o restart
250
254
  sleep socket_interval
251
255
  rescue EOFError
252
- $log.warn "disconnected from #{@host}"
256
+ log.warn "disconnected from #{@host}"
253
257
  break
254
258
  end
255
259
  end
data/test/helper.rb CHANGED
@@ -12,18 +12,55 @@ require 'test/unit'
12
12
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
13
13
  $LOAD_PATH.unshift(File.dirname(__FILE__))
14
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
15
+
16
+ $log = Fluent::Log.new(Fluent::Test::DummyLogDevice.new, Fluent::Log::LEVEL_INFO)
24
17
 
25
18
  require 'fluent/plugin/in_secure_forward'
26
19
  require 'fluent/plugin/out_secure_forward'
27
20
 
21
+ class DummySocket
22
+ attr_accessor :sync
23
+ end
24
+
25
+ class DummyInputPlugin
26
+ attr_reader :log, :users, :nodes, :authentication, :allow_anonymous_source, :allow_keepalive
27
+ attr_reader :shared_key, :self_hostname
28
+ attr_reader :read_length, :read_interval, :socket_interval
29
+
30
+ attr_reader :data
31
+
32
+ def initialize(opts={})
33
+ @log = $log
34
+ @users = opts.fetch(:users, [])
35
+ @nodes = opts.fetch(:nodes, [])
36
+ @authentication = opts.fetch(:authentication, false)
37
+ @allow_anonymous_source = opts.fetch(:allow_anonymous_source, true)
38
+ @allow_keepalive = opts.fetch(:allow_keepalive, true)
39
+ @shared_key = opts.fetch(:shared_key, 'shared key')
40
+ @self_hostname = opts.fetch(:self_hostname, 'hostname.local')
41
+ @read_length = opts.fetch(:read_length, 8*1024*1024)
42
+ @read_interval = opts.fetch(:read_interval, 0.05)
43
+ @socket_interval = opts.fetch(:socket_interval, 0.2)
44
+
45
+ @data = []
46
+ end
47
+
48
+ def select_authenticate_users(node, username)
49
+ if node.nil? || node[:users].nil?
50
+ self.users.select{|u| u[:username] == username}
51
+ else
52
+ self.users.select{|u| node[:users].include?(u[:username]) && u[:username] == username}
53
+ end
54
+ end
55
+
56
+ def on_message(data)
57
+ raise NotImplementedError
58
+ end
59
+ end
60
+
61
+ class DummyOutputPlugin
62
+ end
63
+
64
+
28
65
  class Test::Unit::TestCase
29
66
  end
@@ -2,9 +2,167 @@ require 'helper'
2
2
 
3
3
  class SecureForwardInputTest < Test::Unit::TestCase
4
4
  CONFIG = %[
5
+
5
6
  ]
6
7
 
7
8
  def create_driver(conf=CONFIG,tag='test')
8
- Fluent::Test::InputTestDriver.new(Fluent::SecureForwardInput, tag).configure(conf)
9
+ Fluent::Test::InputTestDriver.new(Fluent::SecureForwardInput).configure(conf)
10
+ end
11
+
12
+ def test_configure
13
+ p1 = nil
14
+ assert_nothing_raised { p1 = create_driver(<<CONFIG).instance }
15
+ type secure_forward
16
+ shared_key secret_string
17
+ self_hostname server.fqdn.local # This fqdn is used as CN (Common Name) of certificates
18
+ cert_auto_generate yes # This parameter MUST be specified
19
+ CONFIG
20
+ assert_equal 'secret_string', p1.shared_key
21
+ assert_equal 'server.fqdn.local', p1.self_hostname
22
+ assert p1.cert_auto_generate
23
+
24
+ assert_raise(Fluent::ConfigError){ create_driver(<<CONFIG) }
25
+ type secure_forward
26
+ shared_key secret_string
27
+ self_hostname server.fqdn.local
28
+ cert_auto_generate yes
29
+ authentication yes # Deny clients without valid username/password
30
+ <user>
31
+ username tagomoris
32
+ password foobar012
33
+ </user>
34
+ <user>
35
+ password yakiniku
36
+ </user>
37
+ CONFIG
38
+ assert_raise(Fluent::ConfigError){ create_driver(<<CONFIG) }
39
+ type secure_forward
40
+ shared_key secret_string
41
+ self_hostname server.fqdn.local
42
+ cert_auto_generate yes
43
+ authentication yes # Deny clients without valid username/password
44
+ <user>
45
+ username tagomoris
46
+ password foobar012
47
+ </user>
48
+ <user>
49
+ username frsyuki
50
+ </user>
51
+ CONFIG
52
+
53
+ p2 = nil
54
+ assert_nothing_raised { p2 = create_driver(<<CONFIG).instance }
55
+ type secure_forward
56
+ shared_key secret_string
57
+ self_hostname server.fqdn.local
58
+ cert_auto_generate yes
59
+ authentication yes # Deny clients without valid username/password
60
+ <user>
61
+ username tagomoris
62
+ password foobar012
63
+ </user>
64
+ <user>
65
+ username frsyuki
66
+ password yakiniku
67
+ </user>
68
+ CONFIG
69
+ assert_equal 2, p2.users.size
70
+ assert_equal 'tagomoris', p2.users[0].username
71
+ assert_equal 'foobar012', p2.users[0].password
72
+
73
+ assert_raise(Fluent::ConfigError){ create_driver(<<CONFIG) }
74
+ type secure_forward
75
+ shared_key secret_string
76
+ self_hostname server.fqdn.local
77
+ cert_auto_generate yes
78
+ allow_anonymous_source no # Allow to accept from nodes of <client>
79
+ <client>
80
+ host 192.168.10.30
81
+ # network address (ex: 192.168.10.0/24) NOT Supported now
82
+ </client>
83
+ <client>
84
+ host localhost
85
+ network 192.168.1.1/32
86
+ </client>
87
+ <client>
88
+ network 192.168.16.0/24
89
+ </client>
90
+ CONFIG
91
+ assert_raise(Fluent::ConfigError){ create_driver(<<CONFIG) }
92
+ type secure_forward
93
+ shared_key secret_string
94
+ self_hostname server.fqdn.local
95
+ cert_auto_generate yes
96
+ allow_anonymous_source no # Allow to accept from nodes of <client>
97
+ <client>
98
+ host 192.168.10.30
99
+ # network address (ex: 192.168.10.0/24) NOT Supported now
100
+ </client>
101
+ <client>
102
+ </client>
103
+ <client>
104
+ network 192.168.16.0/24
105
+ </client>
106
+ CONFIG
107
+
108
+ p3 = nil
109
+ assert_nothing_raised { p3 = create_driver(<<CONFIG).instance }
110
+ type secure_forward
111
+ shared_key secret_string
112
+ self_hostname server.fqdn.local
113
+ cert_auto_generate yes
114
+ allow_anonymous_source no # Allow to accept from nodes of <client>
115
+ <client>
116
+ host 192.168.10.30
117
+ # network address (ex: 192.168.10.0/24) NOT Supported now
118
+ </client>
119
+ <client>
120
+ host localhost
121
+ # wildcard (ex: *.host.fqdn.local) NOT Supported now
122
+ </client>
123
+ <client>
124
+ network 192.168.16.0/24
125
+ </client>
126
+ CONFIG
127
+ assert (not p3.allow_anonymous_source)
128
+ assert_equal 3, p3.clients.size
129
+ assert_equal '192.168.16.0/24', p3.clients[2].network
130
+ assert_equal 3, p3.nodes.size
131
+ assert_equal IPAddr.new('192.168.10.30'), p3.nodes[0][:address]
132
+ assert_equal IPAddr.new('192.168.16.0/24'), p3.nodes[2][:address]
133
+
134
+ p4 = nil
135
+ assert_nothing_raised { p4 = create_driver(<<CONFIG).instance }
136
+ shared_key secret_string
137
+ self_hostname server.fqdn.local
138
+ cert_auto_generate yes
139
+ allow_anonymous_source no # Allow to accept from nodes of <client>
140
+ authentication yes # Deny clients without valid username/password
141
+ <user>
142
+ username tagomoris
143
+ password foobar012
144
+ </user>
145
+ <user>
146
+ username frsyuki
147
+ password sukiyaki
148
+ </user>
149
+ <user>
150
+ username repeatedly
151
+ password sushi
152
+ </user>
153
+ <client>
154
+ host 192.168.10.30 # allow all users to connect from 192.168.10.30
155
+ </client>
156
+ <client>
157
+ host 192.168.10.31
158
+ users tagomoris,frsyuki # deny repeatedly from 192.168.10.31
159
+ </client>
160
+ <client>
161
+ host 192.168.10.32
162
+ shared_key less_secret_string # limited shared_key for 192.168.10.32
163
+ users repeatedly # and repatedly only
164
+ </client>
165
+ CONFIG
166
+ assert_equal ['tagomoris','frsyuki'], p4.nodes[1][:users]
9
167
  end
10
168
  end
@@ -0,0 +1,43 @@
1
+ require 'helper'
2
+
3
+ require 'fluent/plugin/input_session'
4
+
5
+ require 'ipaddr'
6
+
7
+ class InputSessionTest < Test::Unit::TestCase
8
+
9
+ def test_check_node
10
+ # def check_node(hostname, ipaddress, port, proto)
11
+ nodes = [
12
+ { address: IPAddr.new('127.0.0.1'), shared_key: 'shared_key', users: ['tagomoris', 'repeatedly'] },
13
+ { address: IPAddr.new('2001:DB8::9'), shared_key: 'shared_key2', users: nil },
14
+ { address: IPAddr.new('127.0.0.0/24'), shared_key: 'shared_key3', users: ['tagomoris', 'repeatedly'] },
15
+ ]
16
+ p1 = DummyInputPlugin.new(nodes: nodes)
17
+ s1 = Fluent::SecureForwardInput::Session.new(p1, DummySocket.new)
18
+
19
+ assert s1.check_node('127.0.0.1')
20
+ assert_equal 'shared_key', s1.check_node('127.0.0.1')[:shared_key]
21
+
22
+ assert s1.check_node('127.0.0.127')
23
+ assert_equal 'shared_key3', s1.check_node('127.0.0.127')[:shared_key]
24
+
25
+ assert_nil s1.check_node('192.0.2.8')
26
+ assert_nil s1.check_node('2001:DB8::8')
27
+
28
+ assert s1.check_node('2001:DB8::9')
29
+ assert_equal 'shared_key2', s1.check_node('2001:DB8::9')[:shared_key]
30
+ end
31
+
32
+ def test_generate_helo
33
+ end
34
+
35
+ def test_check_ping
36
+ end
37
+
38
+ def test_generate_pong
39
+ end
40
+
41
+ def test_on_read
42
+ end
43
+ 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.1.8
4
+ version: 0.1.9.pre.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - TAGOMORI Satoshi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-31 00:00:00.000000000 Z
11
+ date: 2014-07-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fluentd
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: 0.10.46
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: 0.10.46
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: fluent-mixin-config-placeholders
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -95,6 +95,7 @@ files:
95
95
  - lib/fluent/plugin/output_node.rb
96
96
  - test/helper.rb
97
97
  - test/plugin/test_in_secure_forward.rb
98
+ - test/plugin/test_input_session.rb
98
99
  - test/plugin/test_out_secure_forward.rb
99
100
  homepage: https://github.com/tagomoris/fluent-plugin-secure-forward
100
101
  licenses:
@@ -111,9 +112,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
111
112
  version: '0'
112
113
  required_rubygems_version: !ruby/object:Gem::Requirement
113
114
  requirements:
114
- - - ">="
115
+ - - ">"
115
116
  - !ruby/object:Gem::Version
116
- version: '0'
117
+ version: 1.3.1
117
118
  requirements: []
118
119
  rubyforge_project:
119
120
  rubygems_version: 2.2.2
@@ -123,4 +124,5 @@ summary: Fluentd input/output plugin to forward over SSL with authentications
123
124
  test_files:
124
125
  - test/helper.rb
125
126
  - test/plugin/test_in_secure_forward.rb
127
+ - test/plugin/test_input_session.rb
126
128
  - test/plugin/test_out_secure_forward.rb