fluent-plugin-secure-forward 0.2.6 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +184 -43
- data/bin/secure-forward-ca-generate +34 -0
- data/example/cert_client.conf +1 -2
- data/example/cert_server.conf +5 -4
- data/example/client.conf +10 -7
- data/example/insecure_client.conf +23 -0
- data/example/insecure_server.conf +10 -0
- data/example/server.conf +5 -2
- data/fluent-plugin-secure-forward.gemspec +1 -1
- data/lib/fluent/plugin/in_secure_forward.rb +89 -50
- data/lib/fluent/plugin/input_session.rb +2 -2
- data/lib/fluent/plugin/out_secure_forward.rb +35 -18
- data/lib/fluent/plugin/output_node.rb +46 -23
- data/lib/fluent/plugin/secure_forward/cert_util.rb +85 -0
- data/test/plugin/test_in_secure_forward.rb +73 -8
- data/test/plugin/test_out_secure_forward.rb +45 -0
- metadata +9 -3
@@ -8,6 +8,7 @@ module Fluent
|
|
8
8
|
end
|
9
9
|
|
10
10
|
require_relative 'input_session'
|
11
|
+
require_relative './secure_forward/cert_util'
|
11
12
|
|
12
13
|
module Fluent
|
13
14
|
class SecureForwardInput < Input
|
@@ -15,36 +16,44 @@ module Fluent
|
|
15
16
|
|
16
17
|
Fluent::Plugin.register_input('secure_forward', self)
|
17
18
|
|
19
|
+
config_param :secure, :bool # if secure, cert_path or ca_cert_path required
|
20
|
+
|
18
21
|
config_param :self_hostname, :string
|
19
22
|
include Fluent::Mixin::ConfigPlaceholders
|
20
23
|
|
21
24
|
config_param :shared_key, :string
|
22
25
|
|
23
|
-
config_param :bind, :string, :
|
24
|
-
config_param :port, :integer, :
|
25
|
-
config_param :allow_keepalive, :bool, :
|
26
|
+
config_param :bind, :string, default: '0.0.0.0'
|
27
|
+
config_param :port, :integer, default: DEFAULT_SECURE_LISTEN_PORT
|
28
|
+
config_param :allow_keepalive, :bool, default: true #TODO: implement
|
29
|
+
|
30
|
+
config_param :allow_anonymous_source, :bool, default: true
|
31
|
+
config_param :authentication, :bool, default: false
|
26
32
|
|
27
|
-
config_param :
|
28
|
-
config_param :
|
33
|
+
config_param :ssl_version, :string, default: 'TLSv1_2'
|
34
|
+
config_param :ssl_ciphers, :string, default: nil
|
29
35
|
|
30
|
-
|
31
|
-
|
36
|
+
# Cert signed by public CA
|
37
|
+
config_param :cert_path, :string, default: nil
|
38
|
+
config_param :private_key_path, :string, default: nil
|
39
|
+
config_param :private_key_passphrase, :string, default: nil
|
32
40
|
|
33
|
-
|
34
|
-
config_param :
|
41
|
+
# Cert automatically generated and signed by private CA
|
42
|
+
config_param :ca_cert_path, :string, default: nil
|
43
|
+
config_param :ca_private_key_path, :string, default: nil
|
44
|
+
config_param :ca_private_key_passphrase, :string, default: nil
|
35
45
|
|
36
|
-
|
37
|
-
config_param :generate_cert_state, :string, :default => 'CA'
|
38
|
-
config_param :generate_cert_locality, :string, :default => 'Mountain View'
|
39
|
-
config_param :generate_cert_common_name, :string, :default => nil
|
46
|
+
# Otherwise: Cert automatically generated and signed by itself (for without any verification)
|
40
47
|
|
41
|
-
config_param :
|
42
|
-
config_param :
|
43
|
-
config_param :
|
48
|
+
config_param :generate_private_key_length, :integer, default: 2048
|
49
|
+
config_param :generate_cert_country, :string, default: 'US'
|
50
|
+
config_param :generate_cert_state, :string, default: 'CA'
|
51
|
+
config_param :generate_cert_locality, :string, default: 'Mountain View'
|
52
|
+
config_param :generate_cert_common_name, :string, default: nil
|
44
53
|
|
45
|
-
config_param :read_length, :size, :
|
46
|
-
config_param :read_interval_msec, :integer, :
|
47
|
-
config_param :socket_interval_msec, :integer, :
|
54
|
+
config_param :read_length, :size, default: 8*1024*1024 # 8MB
|
55
|
+
config_param :read_interval_msec, :integer, default: 50 # 50ms
|
56
|
+
config_param :socket_interval_msec, :integer, default: 200 # 200ms
|
48
57
|
|
49
58
|
attr_reader :read_interval, :socket_interval
|
50
59
|
|
@@ -80,8 +89,19 @@ module Fluent
|
|
80
89
|
def configure(conf)
|
81
90
|
super
|
82
91
|
|
83
|
-
|
84
|
-
|
92
|
+
if @secure
|
93
|
+
unless @cert_path || @ca_cert_path
|
94
|
+
raise Fluent::ConfigError, "cert_path or ca_cert_path required for secure communication"
|
95
|
+
end
|
96
|
+
if @cert_path
|
97
|
+
raise Fluent::ConfigError, "private_key_path required" unless @private_key_path
|
98
|
+
raise Fluent::ConfigError, "private_key_passphrase required" unless @private_key_passphrase
|
99
|
+
else # @ca_cert_path
|
100
|
+
raise Fluent::ConfigError, "ca_private_key_path required" unless @ca_private_key_path
|
101
|
+
raise Fluent::ConfigError, "ca_private_key_passphrase required" unless @ca_private_key_passphrase
|
102
|
+
end
|
103
|
+
else
|
104
|
+
log.warn "'insecure' mode has vulnerability for man-in-the-middle attacks for clients (output plugins)."
|
85
105
|
end
|
86
106
|
|
87
107
|
@read_interval = @read_interval_msec / 1000.0
|
@@ -117,7 +137,10 @@ module Fluent
|
|
117
137
|
end
|
118
138
|
|
119
139
|
@generate_cert_common_name ||= @self_hostname
|
140
|
+
|
141
|
+
# To check whether certificates are successfully generated/loaded at startup time
|
120
142
|
self.certificate
|
143
|
+
|
121
144
|
true
|
122
145
|
end
|
123
146
|
|
@@ -127,6 +150,7 @@ module Fluent
|
|
127
150
|
@sessions = []
|
128
151
|
@sock = nil
|
129
152
|
@listener = Thread.new(&method(:run))
|
153
|
+
@listener.abort_on_exception
|
130
154
|
end
|
131
155
|
|
132
156
|
def shutdown
|
@@ -147,44 +171,59 @@ module Fluent
|
|
147
171
|
def certificate
|
148
172
|
return @cert, @key if @cert && @key
|
149
173
|
|
150
|
-
if @
|
151
|
-
key = OpenSSL::PKey::RSA.
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
174
|
+
if @cert_path
|
175
|
+
@key = OpenSSL::PKey::RSA.new(File.read(@private_key_path), @private_key_passphrase)
|
176
|
+
@cert = OpenSSL::X509::Certificate.new(File.read(@cert_path))
|
177
|
+
elsif @ca_cert_path
|
178
|
+
opts = {
|
179
|
+
ca_cert_path: @ca_cert_path,
|
180
|
+
ca_key_path: @ca_private_key_path,
|
181
|
+
ca_key_passphrase: @ca_private_key_passphrase,
|
182
|
+
private_key_length: @generate_private_key_length,
|
183
|
+
country: @generate_cert_country,
|
184
|
+
state: @generate_cert_state,
|
185
|
+
locality: @generate_cert_locality,
|
186
|
+
common_name: @generate_cert_common_name,
|
187
|
+
}
|
188
|
+
@cert, @key = Fluent::SecureForward::CertUtil.generate_server_pair(opts)
|
189
|
+
else
|
190
|
+
opts = {
|
191
|
+
private_key_length: @generate_private_key_length,
|
192
|
+
country: @generate_cert_country,
|
193
|
+
state: @generate_cert_state,
|
194
|
+
locality: @generate_cert_locality,
|
195
|
+
common_name: @generate_cert_common_name,
|
196
|
+
}
|
197
|
+
@cert, @key = Fluent::SecureForward::CertUtil.generate_self_signed_server_pair(opts)
|
172
198
|
end
|
173
|
-
|
174
|
-
@cert = OpenSSL::X509::Certificate.new(File.read(@cert_file_path))
|
175
|
-
@key = OpenSSL::PKey::RSA.new(File.read(@private_key_file), @private_key_passphrase)
|
199
|
+
return @cert, @key
|
176
200
|
end
|
177
201
|
|
178
202
|
def run # sslsocket server thread
|
179
203
|
log.trace "setup for ssl sessions"
|
180
204
|
cert, key = self.certificate
|
181
|
-
|
205
|
+
|
206
|
+
ctx = OpenSSL::SSL::SSLContext.new(@ssl_version)
|
207
|
+
if @secure
|
208
|
+
# inject OpenSSL::SSL::SSLContext::DEFAULT_PARAMS
|
209
|
+
# https://bugs.ruby-lang.org/issues/9424
|
210
|
+
ctx.set_params({})
|
211
|
+
|
212
|
+
if @ssl_ciphers
|
213
|
+
ctx.ciphers = @ssl_ciphers
|
214
|
+
else
|
215
|
+
### follow httpclient configuration by nahi
|
216
|
+
# OpenSSL 0.9.8 default: "ALL:!ADH:!LOW:!EXP:!MD5:+SSLv2:@STRENGTH"
|
217
|
+
ctx.ciphers = "ALL:!aNULL:!eNULL:!SSLv2" # OpenSSL >1.0.0 default
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
182
221
|
ctx.cert = cert
|
183
222
|
ctx.key = key
|
184
223
|
|
185
|
-
log.trace "start to listen", :
|
224
|
+
log.trace "start to listen", bind: @bind, port: @port
|
186
225
|
server = TCPServer.new(@bind, @port)
|
187
|
-
log.trace "starting SSL server", :
|
226
|
+
log.trace "starting SSL server", bind: @bind, port: @port
|
188
227
|
@sock = OpenSSL::SSL::SSLServer.new(server, ctx)
|
189
228
|
@sock.start_immediately = false
|
190
229
|
begin
|
@@ -196,7 +235,7 @@ module Fluent
|
|
196
235
|
|
197
236
|
# cleanup closed session instance
|
198
237
|
@sessions.delete_if(&:closed?)
|
199
|
-
log.trace "session instances:", :
|
238
|
+
log.trace "session instances:", all: @sessions.size, closed: @sessions.select(&:closed?).size
|
200
239
|
end
|
201
240
|
end
|
202
241
|
rescue OpenSSL::SSL::SSLError => e
|
@@ -151,7 +151,7 @@ class Fluent::SecureForwardInput::Session
|
|
151
151
|
begin
|
152
152
|
@socket.accept
|
153
153
|
rescue OpenSSL::SSL::SSLError => e
|
154
|
-
log.debug "failed to establish ssl session"
|
154
|
+
log.debug "failed to establish ssl session", error_class: e.class, error: e
|
155
155
|
self.shutdown
|
156
156
|
return
|
157
157
|
end
|
@@ -195,7 +195,7 @@ class Fluent::SecureForwardInput::Session
|
|
195
195
|
rescue Errno::ECONNRESET => e
|
196
196
|
# disconnected from client
|
197
197
|
rescue => e
|
198
|
-
log.warn "unexpected error in in_secure_forward", :
|
198
|
+
log.warn "unexpected error in in_secure_forward", error_class: e.class, error: e
|
199
199
|
ensure
|
200
200
|
self.shutdown
|
201
201
|
end
|
@@ -15,26 +15,31 @@ module Fluent
|
|
15
15
|
|
16
16
|
Fluent::Plugin.register_output('secure_forward', self)
|
17
17
|
|
18
|
+
config_param :secure, :bool
|
19
|
+
|
18
20
|
config_param :self_hostname, :string
|
19
21
|
include Fluent::Mixin::ConfigPlaceholders
|
20
22
|
|
21
23
|
config_param :shared_key, :string
|
22
24
|
|
23
|
-
config_param :keepalive, :time, :
|
25
|
+
config_param :keepalive, :time, default: nil # nil/0 means disable keepalive expiration
|
24
26
|
|
25
|
-
config_param :send_timeout, :time, :
|
27
|
+
config_param :send_timeout, :time, default: 60
|
26
28
|
# config_param :hard_timeout, :time, :default => 60
|
27
29
|
# config_param :expire_dns_cache, :time, :default => 0 # 0 means disable cache
|
28
30
|
|
29
|
-
config_param :
|
30
|
-
|
31
|
+
config_param :ca_cert_path, :string, default: nil
|
32
|
+
|
33
|
+
config_param :enable_strict_verification, :bool, default: nil # FQDN check with hostlabel
|
34
|
+
config_param :ssl_version, :string, default: 'TLSv1_2'
|
35
|
+
config_param :ssl_ciphers, :string, default: nil
|
31
36
|
|
32
|
-
config_param :read_length, :size, :
|
33
|
-
config_param :read_interval_msec, :integer, :
|
34
|
-
config_param :socket_interval_msec, :integer, :
|
37
|
+
config_param :read_length, :size, default: 512 # 512bytes
|
38
|
+
config_param :read_interval_msec, :integer, default: 50 # 50ms
|
39
|
+
config_param :socket_interval_msec, :integer, default: 200 # 200ms
|
35
40
|
|
36
|
-
config_param :reconnect_interval, :time, :
|
37
|
-
config_param :established_timeout, :time, :
|
41
|
+
config_param :reconnect_interval, :time, default: 5
|
42
|
+
config_param :established_timeout, :time, default: 10
|
38
43
|
|
39
44
|
attr_reader :read_interval, :socket_interval
|
40
45
|
|
@@ -68,8 +73,20 @@ module Fluent
|
|
68
73
|
def configure(conf)
|
69
74
|
super
|
70
75
|
|
71
|
-
|
72
|
-
|
76
|
+
if @secure
|
77
|
+
if @ca_cert_path
|
78
|
+
raise Fluent::ConfigError, "CA cert file not found nor readable at '#{@ca_cert_path}'" unless File.readable?(@ca_cert_path)
|
79
|
+
begin
|
80
|
+
OpenSSL::X509::Certificate.new File.read(@ca_cert_path)
|
81
|
+
rescue OpenSSL::X509::CertificateError => e
|
82
|
+
raise Fluent::ConfigError, "failed to load CA cert file"
|
83
|
+
end
|
84
|
+
else
|
85
|
+
raise Fluent::ConfigError, "FQDN verification required for certificates issued from public CA" unless @enable_strict_verification
|
86
|
+
log.info "secure connection with valid certificates issued from public CA"
|
87
|
+
end
|
88
|
+
else
|
89
|
+
log.warn "'insecure' mode has vulnerability for man-in-the-middle attacks."
|
73
90
|
end
|
74
91
|
|
75
92
|
@read_interval = @read_interval_msec / 1000.0
|
@@ -89,7 +106,7 @@ module Fluent
|
|
89
106
|
@next_node = 0
|
90
107
|
@mutex = Mutex.new
|
91
108
|
|
92
|
-
@hostname_resolver = Resolve::Hostname.new(:
|
109
|
+
@hostname_resolver = Resolve::Hostname.new(system_resolver: true)
|
93
110
|
|
94
111
|
true
|
95
112
|
end
|
@@ -122,7 +139,7 @@ module Fluent
|
|
122
139
|
OpenSSL::Random.seed(SecureRandom.random_bytes(16))
|
123
140
|
log.debug "start to connect target nodes"
|
124
141
|
@nodes.each do |node|
|
125
|
-
log.debug "connecting node", :
|
142
|
+
log.debug "connecting node", host: node.host, port: node.port
|
126
143
|
node.start
|
127
144
|
end
|
128
145
|
@nodewatcher = Thread.new(&method(:node_watcher))
|
@@ -153,13 +170,13 @@ module Fluent
|
|
153
170
|
end
|
154
171
|
|
155
172
|
node = @nodes[i]
|
156
|
-
log.debug "reconnecting to node", :
|
173
|
+
log.debug "reconnecting to node", host: node.host, port: node.port, expire: node.expire, expired: node.expired?, detached: node.detached?
|
157
174
|
|
158
175
|
renewed = node.dup
|
159
176
|
renewed.start
|
160
177
|
|
161
178
|
Thread.pass # to connection thread
|
162
|
-
reconnectings[i] = { :
|
179
|
+
reconnectings[i] = { conn: renewed, at: Time.now, reason: reason }
|
163
180
|
end
|
164
181
|
|
165
182
|
(0...nodes_size).each do |i|
|
@@ -191,7 +208,7 @@ module Fluent
|
|
191
208
|
|
192
209
|
# not connected yet, and timeout
|
193
210
|
timeout_conn = reconnectings[i][:conn]
|
194
|
-
log.debug "SSL connection is not established until timemout", :
|
211
|
+
log.debug "SSL connection is not established until timemout", host: timeout_conn.host, port: timeout_conn.port, timeout: @established_timeout
|
195
212
|
reconnectings[i] = nil
|
196
213
|
timeout_conn.detach! if timeout_conn # connection object doesn't raise any exceptions
|
197
214
|
end
|
@@ -215,13 +232,13 @@ module Fluent
|
|
215
232
|
unless node
|
216
233
|
raise "no one nodes with valid ssl session"
|
217
234
|
end
|
218
|
-
log.trace "selected node", :
|
235
|
+
log.trace "selected node", host: node.host, port: node.port, standby: node.standby
|
219
236
|
|
220
237
|
begin
|
221
238
|
send_data(node, tag, es)
|
222
239
|
node.release!
|
223
240
|
rescue Errno::EPIPE, IOError, OpenSSL::SSL::SSLError => e
|
224
|
-
log.warn "Failed to send messages to #{node.host}, parging.", :
|
241
|
+
log.warn "Failed to send messages to #{node.host}, parging.", error_class: e.class, error: e
|
225
242
|
node.release!
|
226
243
|
node.detach!
|
227
244
|
|
@@ -60,8 +60,6 @@ class Fluent::SecureForwardOutput::Node
|
|
60
60
|
|
61
61
|
def start
|
62
62
|
@thread = Thread.new(&method(:connect))
|
63
|
-
## If you want to check code bug, turn this line enable
|
64
|
-
# @thread.abort_on_exception = true
|
65
63
|
end
|
66
64
|
|
67
65
|
def detach!
|
@@ -165,6 +163,10 @@ class Fluent::SecureForwardOutput::Node
|
|
165
163
|
return false, 'authentication failed: ' + reason
|
166
164
|
end
|
167
165
|
|
166
|
+
if hostname == @sender.self_hostname
|
167
|
+
return false, 'same hostname between input and output: invalid configuration'
|
168
|
+
end
|
169
|
+
|
168
170
|
clientside = Digest::SHA512.new.update(@shared_key_salt).update(hostname).update(@shared_key).hexdigest
|
169
171
|
unless shared_key_hexdigest == clientside
|
170
172
|
return false, 'shared key mismatch'
|
@@ -204,19 +206,20 @@ class Fluent::SecureForwardOutput::Node
|
|
204
206
|
log.info "connection established to #{@host}" if @first_session
|
205
207
|
@state = :established
|
206
208
|
@expire = Time.now + @keepalive if @keepalive && @keepalive > 0
|
207
|
-
log.debug "connection established", :
|
209
|
+
log.debug "connection established", host: @host, port: @port, expire: @expire
|
208
210
|
end
|
209
211
|
end
|
210
212
|
|
211
213
|
def connect
|
214
|
+
Thread.current.abort_on_exception = true
|
212
215
|
log.debug "starting client"
|
213
216
|
|
214
217
|
addr = @sender.hostname_resolver.getaddress(@host)
|
215
|
-
log.debug "create tcp socket to node", :
|
218
|
+
log.debug "create tcp socket to node", host: @host, address: addr, port: @port
|
216
219
|
begin
|
217
220
|
sock = TCPSocket.new(addr, @port)
|
218
221
|
rescue => e
|
219
|
-
log.warn "failed to connect for secure-forward", :
|
222
|
+
log.warn "failed to connect for secure-forward", error_class: e.class, error: e, host: @host, address: addr, port: @port
|
220
223
|
@state = :failed
|
221
224
|
return
|
222
225
|
end
|
@@ -228,45 +231,65 @@ class Fluent::SecureForwardOutput::Node
|
|
228
231
|
opt = [@sender.send_timeout.to_i, 0].pack('L!L!') # struct timeval
|
229
232
|
sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, opt)
|
230
233
|
|
231
|
-
# TODO: SSLContext constructer parameter (SSL/TLS protocol version)
|
232
234
|
log.trace "initializing SSL contexts"
|
233
|
-
context = OpenSSL::SSL::SSLContext.new
|
234
|
-
# TODO: context.ca_file = (ca_file_path)
|
235
|
-
# TODO: context.ciphers = (SSL Shared key chiper protocols)
|
236
235
|
|
237
|
-
|
236
|
+
context = OpenSSL::SSL::SSLContext.new(@sender.ssl_version)
|
237
|
+
|
238
|
+
log.trace "setting SSL verification options"
|
239
|
+
|
240
|
+
if @sender.secure
|
241
|
+
# inject OpenSSL::SSL::SSLContext::DEFAULT_PARAMS
|
242
|
+
# https://bugs.ruby-lang.org/issues/9424
|
243
|
+
context.set_params({})
|
244
|
+
|
245
|
+
if @sender.ssl_ciphers
|
246
|
+
context.ciphers = @sender.ssl_ciphers
|
247
|
+
else
|
248
|
+
### follow httpclient configuration by nahi
|
249
|
+
# OpenSSL 0.9.8 default: "ALL:!ADH:!LOW:!EXP:!MD5:+SSLv2:@STRENGTH"
|
250
|
+
context.ciphers = "ALL:!aNULL:!eNULL:!SSLv2" # OpenSSL >1.0.0 default
|
251
|
+
end
|
252
|
+
|
253
|
+
log.trace "set verify_mode VERIFY_PEER"
|
254
|
+
context.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
255
|
+
if @sender.ca_cert_path
|
256
|
+
log.trace "set to use private CA", path: @sender.ca_cert_path
|
257
|
+
context.ca_file = @sender.ca_cert_path
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
log.debug "trying to connect ssl session", host: @host, address: addr, port: @port
|
238
262
|
begin
|
239
263
|
sslsession = OpenSSL::SSL::SSLSocket.new(sock, context)
|
264
|
+
log.trace "connecting...", host: @host, address: addr, port: @port
|
265
|
+
sslsession.connect
|
240
266
|
rescue => e
|
241
|
-
log.warn "failed to establish SSL connection", :host
|
242
|
-
end
|
243
|
-
|
244
|
-
unless sslsession.connect
|
245
|
-
log.debug "failed to connect", :host => @host, :address => addr, :port => @port
|
267
|
+
log.warn "failed to establish SSL connection", error_class: e.class, error: e, host: @host, address: addr, port: @port
|
246
268
|
@state = :failed
|
247
269
|
return
|
248
270
|
end
|
249
|
-
|
271
|
+
|
272
|
+
log.debug "ssl session connected", host: @host, port: @port
|
250
273
|
|
251
274
|
begin
|
252
|
-
|
253
|
-
log.debug "checking peer's certificate", :
|
275
|
+
if @sender.enable_strict_verification
|
276
|
+
log.debug "checking peer's certificate", subject: sslsession.peer_cert.subject
|
254
277
|
sslsession.post_connection_check(@hostlabel)
|
255
278
|
verify = sslsession.verify_result
|
256
279
|
if verify != OpenSSL::X509::V_OK
|
257
280
|
err_name = Fluent::SecureForwardOutput::OpenSSLUtil.verify_result_name(verify)
|
258
|
-
log.warn "failed to verify certification while connecting host #{@host} as #{@hostlabel} (but not raised, why?)"
|
259
|
-
log.warn "verify_result: #{err_name}"
|
260
|
-
raise RuntimeError, "failed to verify certification while connecting host #{@host} as #{@hostlabel}"
|
281
|
+
log.warn "BUG: failed to verify certification while connecting host #{@host} as #{@hostlabel} (but not raised, why?)"
|
282
|
+
log.warn "BUG: verify_result: #{err_name}"
|
283
|
+
raise RuntimeError, "BUG: failed to verify certification and to handle it correctly while connecting host #{@host} as #{@hostlabel}"
|
261
284
|
end
|
262
285
|
end
|
263
286
|
rescue OpenSSL::SSL::SSLError => e
|
264
|
-
log.warn "failed to verify certification while connecting ssl session", :
|
287
|
+
log.warn "failed to verify certification while connecting ssl session", host: @host, hostlabel: @hostlabel
|
265
288
|
self.shutdown
|
266
289
|
raise
|
267
290
|
end
|
268
291
|
|
269
|
-
log.debug "ssl
|
292
|
+
log.debug "ssl session connected", host: @host, port: @port
|
270
293
|
@socket = sock
|
271
294
|
@sslsession = sslsession
|
272
295
|
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
|
3
|
+
module Fluent
|
4
|
+
module SecureForward
|
5
|
+
module CertUtil
|
6
|
+
def self.generate_ca_pair(opts={})
|
7
|
+
key = OpenSSL::PKey::RSA.generate(opts[:private_key_length])
|
8
|
+
|
9
|
+
issuer = subject = OpenSSL::X509::Name.new
|
10
|
+
subject.add_entry('C', opts[:cert_country])
|
11
|
+
subject.add_entry('ST', opts[:cert_state])
|
12
|
+
subject.add_entry('L', opts[:cert_locality])
|
13
|
+
subject.add_entry('CN', opts[:cert_common_name])
|
14
|
+
|
15
|
+
digest = OpenSSL::Digest::SHA1.new
|
16
|
+
|
17
|
+
cert = OpenSSL::X509::Certificate.new
|
18
|
+
cert.not_before = Time.at(0)
|
19
|
+
cert.not_after = Time.now + 5 * 365 * 86400 # 5 years after
|
20
|
+
cert.public_key = key
|
21
|
+
cert.serial = 1
|
22
|
+
cert.issuer = issuer
|
23
|
+
cert.subject = subject
|
24
|
+
cert.add_extension OpenSSL::X509::Extension.new('basicConstraints', OpenSSL::ASN1.Sequence([OpenSSL::ASN1::Boolean(true)]))
|
25
|
+
cert.sign(key, digest)
|
26
|
+
|
27
|
+
return cert, key
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.generate_server_pair(opts={})
|
31
|
+
key = OpenSSL::PKey::RSA.generate(opts[:private_key_length])
|
32
|
+
|
33
|
+
ca_key = OpenSSL::PKey::RSA.new(File.read(opts[:ca_key_path]), opts[:ca_key_passphrase])
|
34
|
+
ca_cert = OpenSSL::X509::Certificate.new(File.read(opts[:ca_cert_path]))
|
35
|
+
issuer = ca_cert.issuer
|
36
|
+
|
37
|
+
subject = OpenSSL::X509::Name.new
|
38
|
+
subject.add_entry('C', opts[:country])
|
39
|
+
subject.add_entry('ST', opts[:state])
|
40
|
+
subject.add_entry('L', opts[:locality])
|
41
|
+
subject.add_entry('CN', opts[:common_name])
|
42
|
+
|
43
|
+
digest = OpenSSL::Digest::SHA1.new
|
44
|
+
|
45
|
+
cert = OpenSSL::X509::Certificate.new
|
46
|
+
cert.not_before = Time.at(0)
|
47
|
+
cert.not_after = Time.now + 5 * 365 * 86400 # 5 years after
|
48
|
+
cert.public_key = key
|
49
|
+
cert.serial = 2
|
50
|
+
cert.issuer = issuer
|
51
|
+
cert.subject = subject
|
52
|
+
|
53
|
+
cert.add_extension OpenSSL::X509::Extension.new('basicConstraints', OpenSSL::ASN1.Sequence([OpenSSL::ASN1::Boolean(false)]))
|
54
|
+
cert.add_extension OpenSSL::X509::Extension.new('nsCertType', 'server')
|
55
|
+
|
56
|
+
cert.sign ca_key, digest
|
57
|
+
|
58
|
+
return cert, key
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.generate_self_signed_server_pair(opts={})
|
62
|
+
key = OpenSSL::PKey::RSA.generate(opts[:private_key_length])
|
63
|
+
|
64
|
+
issuer = subject = OpenSSL::X509::Name.new
|
65
|
+
subject.add_entry('C', opts[:country])
|
66
|
+
subject.add_entry('ST', opts[:state])
|
67
|
+
subject.add_entry('L', opts[:locality])
|
68
|
+
subject.add_entry('CN', opts[:common_name])
|
69
|
+
|
70
|
+
digest = OpenSSL::Digest::SHA1.new
|
71
|
+
|
72
|
+
cert = OpenSSL::X509::Certificate.new
|
73
|
+
cert.not_before = Time.at(0)
|
74
|
+
cert.not_after = Time.now + 5 * 365 * 86400 # 5 years after
|
75
|
+
cert.public_key = key
|
76
|
+
cert.serial = 1
|
77
|
+
cert.issuer = issuer
|
78
|
+
cert.subject = subject
|
79
|
+
cert.sign(key, digest)
|
80
|
+
|
81
|
+
return cert, key
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|