fluent-plugin-secure-forward 0.2.6 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|