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

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: 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