logstash-input-tcp 5.2.0-java → 5.2.1-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/docs/index.asciidoc +2 -2
- data/lib/logstash/inputs/tcp/compat_ssl_options.rb +48 -13
- data/spec/inputs/tcp_spec.rb +96 -47
- data/spec/spec_helper.rb +33 -4
- data/vendor/jar-dependencies/org/logstash/inputs/logstash-input-tcp/{5.2.0/logstash-input-tcp-5.2.0.jar → 5.2.1/logstash-input-tcp-5.2.1.jar} +0 -0
- data/version +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6ee7c79fca2dceea8688b100903ab8318ad2cd652740b0538c102504209bd1e0
|
4
|
+
data.tar.gz: fee6f5e7d1804c8ef60fd771a3d50caeeee289c9e517a93c2a33a5d207202bdb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 970218c4f6ed173209de6d40113423a49e356e1eeb3a8cf8603b5ddd7c51ae4766cadf73e78d917a17ba36b4d447fc1d9512ea12081dbd1cbb2e28e0f8ea9340
|
7
|
+
data.tar.gz: 0b4120780dcae5f582c118247a064025a57f998ddbe20ff5cb15d3c4ecbdef0a0a522cd6bd2968e77db75ff034d6810f4e13f8372f2de49ce08ea1cada19e573
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
## 5.2.1
|
2
|
+
- Support multiple certificates per file [#140](https://github.com/logstash-plugins/logstash-input-tcp/pull/140)
|
3
|
+
- Fixed support for encrypted pkcs8 private keys [#133](https://github.com/logstash-plugins/logstash-input-tcp/pull/133)
|
4
|
+
- Added support for encrypted pem pkcs1 private keys [#131](https://github.com/logstash-plugins/logstash-input-tcp/pull/131)
|
5
|
+
- Changed testing to docker [#128](https://github.com/logstash-plugins/logstash-input-tcp/pull/128)
|
6
|
+
- Fixed heading for `ssl_certificate_authorities` docs [#130](https://github.com/logstash-plugins/logstash-input-tcp/pull/130)
|
7
|
+
|
1
8
|
## 5.2.0
|
2
9
|
- Added support for pkcs1 and pkcs8 key formats [#122](https://github.com/logstash-plugins/logstash-input-tcp/issues/122)
|
3
10
|
- Changed server-mode SSL to run on top of Netty [#122](https://github.com/logstash-plugins/logstash-input-tcp/issues/122)
|
data/docs/index.asciidoc
CHANGED
@@ -145,7 +145,7 @@ Path to certificate in PEM format. This certificate will be presented
|
|
145
145
|
to the connecting clients.
|
146
146
|
|
147
147
|
[id="plugins-{type}s-{plugin}-ssl_certificate_authorities"]
|
148
|
-
===== `
|
148
|
+
===== `ssl_certificate_authorities`
|
149
149
|
|
150
150
|
* Value type is <<array,array>>
|
151
151
|
* Default value is `[]`
|
@@ -218,4 +218,4 @@ at the TCP layer and IPs will not be resolved to hostnames.
|
|
218
218
|
[id="plugins-{type}s-{plugin}-common-options"]
|
219
219
|
include::{include_path}/{type}.asciidoc[]
|
220
220
|
|
221
|
-
:default_codec!:
|
221
|
+
:default_codec!:
|
@@ -7,9 +7,15 @@ java_import 'java.io.FileReader'
|
|
7
7
|
java_import 'java.security.cert.CertificateFactory'
|
8
8
|
java_import 'java.security.cert.X509Certificate'
|
9
9
|
java_import 'org.bouncycastle.asn1.pkcs.PrivateKeyInfo'
|
10
|
+
java_import 'org.bouncycastle.jce.provider.BouncyCastleProvider'
|
10
11
|
java_import 'org.bouncycastle.openssl.PEMKeyPair'
|
11
12
|
java_import 'org.bouncycastle.openssl.PEMParser'
|
13
|
+
java_import 'org.bouncycastle.openssl.PEMEncryptedKeyPair'
|
12
14
|
java_import 'org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter'
|
15
|
+
java_import 'org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder'
|
16
|
+
java_import 'org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder'
|
17
|
+
java_import 'org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo'
|
18
|
+
|
13
19
|
|
14
20
|
# Simulate a normal SslOptions builder:
|
15
21
|
#
|
@@ -70,34 +76,63 @@ class SslOptions
|
|
70
76
|
# create certificate object
|
71
77
|
cf = CertificateFactory.getInstance("X.509")
|
72
78
|
cert_chain = []
|
73
|
-
|
79
|
+
fetch_certificates_from_file(@ssl_cert_path, cf) do |cert|
|
80
|
+
cert_chain << cert
|
81
|
+
end
|
74
82
|
|
75
83
|
# convert key from pkcs1 to pkcs8 and get PrivateKey object
|
76
84
|
pem_parser = PEMParser.new(FileReader.new(@ssl_key_path))
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
85
|
+
java.security.Security.addProvider(BouncyCastleProvider.new)
|
86
|
+
converter = JcaPEMKeyConverter.new
|
87
|
+
case obj = pem_parser.readObject
|
88
|
+
when PEMKeyPair # unencrypted pkcs#1
|
89
|
+
private_key = converter.getKeyPair(obj).private
|
90
|
+
when PrivateKeyInfo # unencrypted pkcs#8
|
91
|
+
private_key = converter.getPrivateKey(obj)
|
92
|
+
when PEMEncryptedKeyPair # encrypted pkcs#1
|
93
|
+
key_char_array = @ssl_key_passphrase.to_java.toCharArray
|
94
|
+
decryptor = JcePEMDecryptorProviderBuilder.new.build(key_char_array)
|
95
|
+
key_pair = obj.decryptKeyPair(decryptor)
|
96
|
+
private_key = converter.getKeyPair(key_pair).private
|
97
|
+
when PKCS8EncryptedPrivateKeyInfo # encrypted pkcs#8
|
98
|
+
key_char_array = @ssl_key_passphrase.to_java.toCharArray
|
99
|
+
key = JceOpenSSLPKCS8DecryptorProviderBuilder.new.build(key_char_array)
|
100
|
+
private_key = converter.getPrivateKey(obj.decryptPrivateKeyInfo(key))
|
83
101
|
else
|
84
102
|
raise "Could not recognize 'ssl_key' format. Class: #{obj.class}"
|
85
103
|
end
|
86
104
|
|
87
|
-
@ssl_extra_chain_certs.each do |
|
88
|
-
|
105
|
+
@ssl_extra_chain_certs.each do |file|
|
106
|
+
fetch_certificates_from_file(file, cf) do |cert|
|
107
|
+
cert_chain << cert
|
108
|
+
end
|
89
109
|
end
|
90
|
-
sslContextBuilder = SslContextBuilder.forServer(private_key, @ssl_key_passphrase, cert_chain.to_java(
|
110
|
+
sslContextBuilder = SslContextBuilder.forServer(private_key, @ssl_key_passphrase, cert_chain.to_java(X509Certificate))
|
111
|
+
|
112
|
+
trust_certs = []
|
91
113
|
|
92
|
-
|
93
|
-
cf
|
114
|
+
@ssl_certificate_authorities.each do |file|
|
115
|
+
fetch_certificates_from_file(file, cf) do |cert|
|
116
|
+
trust_certs << cert
|
117
|
+
end
|
94
118
|
end
|
95
119
|
|
96
120
|
if trust_certs.any?
|
97
|
-
sslContextBuilder.trustManager(trust_certs.to_java(
|
121
|
+
sslContextBuilder.trustManager(trust_certs.to_java(X509Certificate))
|
98
122
|
end
|
99
123
|
|
100
124
|
sslContextBuilder.clientAuth(@ssl_verify ? ClientAuth::REQUIRE : ClientAuth::NONE)
|
101
125
|
sslContextBuilder.build()
|
102
126
|
end
|
127
|
+
|
128
|
+
private
|
129
|
+
def fetch_certificates_from_file(file, cf)
|
130
|
+
fis = FileInputStream.new(file)
|
131
|
+
|
132
|
+
while (fis.available > 0) do
|
133
|
+
yield cf.generateCertificate(fis)
|
134
|
+
end
|
135
|
+
ensure
|
136
|
+
fis.close if fis
|
137
|
+
end
|
103
138
|
end
|
data/spec/inputs/tcp_spec.rb
CHANGED
@@ -391,41 +391,21 @@ describe LogStash::Inputs::Tcp do
|
|
391
391
|
end
|
392
392
|
|
393
393
|
context "when ssl_enable is true" do
|
394
|
-
let(:ssc) { SelfSignedCertificate.new }
|
395
|
-
let(:certificate_file) { ssc.certificate }
|
396
|
-
let(:key_file) { ssc.private_key}
|
397
|
-
let(:queue) { Queue.new }
|
398
|
-
|
399
|
-
let(:config) do
|
400
|
-
{
|
401
|
-
"host" => "127.0.0.1",
|
402
|
-
"port" => port,
|
403
|
-
"ssl_enable" => true,
|
404
|
-
"ssl_cert" => certificate_file.path,
|
405
|
-
"ssl_key" => key_file.path,
|
406
|
-
"ssl_certificate_authorities" => [ certificate_file.path ]
|
407
|
-
}
|
408
|
-
end
|
409
|
-
|
410
394
|
let(:input) { subject }
|
411
|
-
|
412
|
-
|
395
|
+
let(:queue) { Queue.new }
|
396
|
+
before(:each) { subject.register }
|
413
397
|
|
414
398
|
context "when using a certificate chain" do
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
"
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
"ssl_certificate_authorities" => [ chain_of_certificates[:root_ca].path ],
|
425
|
-
"ssl_verify" => true
|
426
|
-
}
|
399
|
+
chain_of_certificates = TcpHelpers.new.chain_of_certificates
|
400
|
+
|
401
|
+
let(:tcp) do
|
402
|
+
begin
|
403
|
+
socket = TCPSocket.new("127.0.0.1", port)
|
404
|
+
rescue Errno::ECONNREFUSED
|
405
|
+
sleep 1
|
406
|
+
socket = TCPSocket.new("127.0.0.1", port)
|
407
|
+
end
|
427
408
|
end
|
428
|
-
let(:tcp) { TCPSocket.new("127.0.0.1", port) }
|
429
409
|
let(:sslcontext) do
|
430
410
|
sslcontext = OpenSSL::SSL::SSLContext.new
|
431
411
|
sslcontext.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
@@ -435,23 +415,86 @@ describe LogStash::Inputs::Tcp do
|
|
435
415
|
sslcontext
|
436
416
|
end
|
437
417
|
let(:sslsocket) { OpenSSL::SSL::SSLSocket.new(tcp, sslcontext) }
|
438
|
-
let(:
|
439
|
-
|
440
|
-
|
441
|
-
|
418
|
+
let(:message) { "message to #{port}" }
|
419
|
+
|
420
|
+
context "with a non encrypted private key" do
|
421
|
+
let(:config) do
|
422
|
+
{
|
423
|
+
"host" => "127.0.0.1",
|
424
|
+
"port" => port,
|
425
|
+
"ssl_enable" => true,
|
426
|
+
"ssl_cert" => chain_of_certificates[:b_cert].path,
|
427
|
+
"ssl_key" => chain_of_certificates[:b_key].path,
|
428
|
+
"ssl_extra_chain_certs" => [ chain_of_certificates[:a_cert].path ],
|
429
|
+
"ssl_certificate_authorities" => [ chain_of_certificates[:root_ca].path ],
|
430
|
+
"ssl_verify" => true
|
431
|
+
}
|
432
|
+
end
|
433
|
+
it "should be able to connect and write data" do
|
434
|
+
result = TcpHelpers.pipelineless_input(subject, 1) do
|
435
|
+
sslsocket.connect
|
436
|
+
sslsocket.write("#{message}\n")
|
437
|
+
tcp.flush
|
438
|
+
sslsocket.close
|
439
|
+
tcp.close
|
440
|
+
end
|
441
|
+
expect(result.size).to eq(1)
|
442
|
+
expect(result.first.get("message")).to eq(message)
|
443
|
+
end
|
442
444
|
end
|
443
445
|
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
446
|
+
context "when using an encrypted private pkcs1 key" do
|
447
|
+
let(:config) do
|
448
|
+
{
|
449
|
+
"host" => "127.0.0.1",
|
450
|
+
"port" => port,
|
451
|
+
"ssl_enable" => true,
|
452
|
+
"ssl_cert" => chain_of_certificates[:be_cert].path,
|
453
|
+
"ssl_key" => chain_of_certificates[:be_key].path,
|
454
|
+
"ssl_key_passphrase" => "passpasspassword",
|
455
|
+
"ssl_extra_chain_certs" => [ chain_of_certificates[:a_cert].path ],
|
456
|
+
"ssl_certificate_authorities" => [ chain_of_certificates[:root_ca].path ],
|
457
|
+
"ssl_verify" => true
|
458
|
+
}
|
459
|
+
end
|
460
|
+
it "should be able to connect and write data" do
|
461
|
+
result = TcpHelpers.pipelineless_input(subject, 1) do
|
462
|
+
sslsocket.connect
|
463
|
+
sslsocket.write("#{message}\n")
|
464
|
+
tcp.flush
|
465
|
+
sslsocket.close
|
466
|
+
tcp.close
|
467
|
+
end
|
468
|
+
expect(result.size).to eq(1)
|
469
|
+
expect(result.first.get("message")).to eq(message)
|
470
|
+
end
|
471
|
+
end
|
472
|
+
context "when using an encrypted private pkcs8 key" do
|
473
|
+
let(:config) do
|
474
|
+
{
|
475
|
+
"host" => "127.0.0.1",
|
476
|
+
"port" => port,
|
477
|
+
"ssl_enable" => true,
|
478
|
+
"ssl_cert" => chain_of_certificates[:be_cert].path,
|
479
|
+
"ssl_key" => chain_of_certificates[:be_key_pkcs8].path,
|
480
|
+
"ssl_key_passphrase" => "passpasspassword",
|
481
|
+
"ssl_extra_chain_certs" => [ chain_of_certificates[:a_cert].path ],
|
482
|
+
"ssl_certificate_authorities" => [ chain_of_certificates[:root_ca].path ],
|
483
|
+
"ssl_verify" => true
|
484
|
+
}
|
485
|
+
end
|
486
|
+
it "should be able to connect and write data" do
|
487
|
+
result = TcpHelpers.pipelineless_input(subject, 1) do
|
488
|
+
sslsocket.connect
|
489
|
+
sslsocket.write("#{message}\n")
|
490
|
+
tcp.flush
|
491
|
+
sslsocket.close
|
492
|
+
tcp.close
|
493
|
+
end
|
494
|
+
expect(result.size).to eq(1)
|
495
|
+
expect(result.first.get("message")).to eq(message)
|
496
|
+
end
|
453
497
|
end
|
454
|
-
|
455
498
|
end
|
456
499
|
|
457
500
|
context "with a poorly-behaving client" do
|
@@ -459,8 +502,14 @@ describe LogStash::Inputs::Tcp do
|
|
459
502
|
|
460
503
|
context "that disconnects before doing TLS handshake" do
|
461
504
|
before do
|
462
|
-
|
463
|
-
|
505
|
+
begin
|
506
|
+
client = TCPSocket.new("127.0.0.1", port)
|
507
|
+
client.close
|
508
|
+
rescue Errno::ECONNREFUSED
|
509
|
+
sleep 1
|
510
|
+
client = TCPSocket.new("127.0.0.1", port)
|
511
|
+
client.close
|
512
|
+
end
|
464
513
|
end
|
465
514
|
|
466
515
|
it "should not negatively impact the plugin" do
|
data/spec/spec_helper.rb
CHANGED
@@ -3,11 +3,15 @@ require "logstash/devutils/rspec/spec_helper"
|
|
3
3
|
require "tempfile"
|
4
4
|
require "stud/temporary"
|
5
5
|
|
6
|
+
java_import 'org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8EncryptorBuilder'
|
7
|
+
java_import 'org.bouncycastle.openssl.jcajce.JcaPEMWriter'
|
8
|
+
java_import 'org.bouncycastle.openssl.jcajce.JcaPKCS8Generator'
|
9
|
+
java_import 'org.bouncycastle.jce.provider.BouncyCastleProvider'
|
6
10
|
# this has been taken from the udp input, it should be DRYed
|
7
11
|
|
8
12
|
class TcpHelpers
|
9
13
|
|
10
|
-
def pipelineless_input(plugin, size, &block)
|
14
|
+
def self.pipelineless_input(plugin, size, &block)
|
11
15
|
queue = Queue.new
|
12
16
|
input_thread = Thread.new do
|
13
17
|
plugin.run(queue)
|
@@ -32,12 +36,32 @@ class TcpHelpers
|
|
32
36
|
a_cert, a_key = build_certificate(root_ca, root_key, "A_Cert")
|
33
37
|
aa_cert, aa_key = build_certificate(root_ca, root_key, "AA_Cert")
|
34
38
|
b_cert, b_key = build_certificate(a_cert, a_key, "B_Cert")
|
39
|
+
be_cert, be_key, be_key_text = build_certificate(a_cert, a_key, "BE_Cert", "passpasspassword")
|
40
|
+
be_key_pkcs8 = convert_private_key_to_pkcs8_with_passpharse(be_key, "passpasspassword")
|
35
41
|
c_cert, c_key = build_certificate(b_cert, b_key, "C_Cert")
|
36
42
|
{ :root_ca => new_temp_file('', root_ca), :root_key => new_temp_file('', root_key),
|
37
43
|
:a_cert => new_temp_file('', a_cert), :a_key => new_temp_file('', a_key),
|
38
44
|
:aa_cert => new_temp_file('', aa_cert), :aa_key => new_temp_file('', aa_key),
|
39
45
|
:b_cert => new_temp_file('', b_cert), :b_key => new_temp_file('', b_key),
|
40
|
-
:
|
46
|
+
:be_cert => new_temp_file('', be_cert), :be_key => new_temp_file('', be_key_text), :be_key_pkcs8 => new_temp_file('', be_key_pkcs8),
|
47
|
+
:c_cert => new_temp_file('', c_cert), :c_key => new_temp_file('', c_key),
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
def convert_private_key_to_pkcs8_with_passpharse(pkcs1key, passphrase)
|
52
|
+
pem_parser = PEMParser.new(java.io.StringReader.new(pkcs1key.to_pem))
|
53
|
+
kp = pem_parser.read_object
|
54
|
+
java.security.Security.addProvider(BouncyCastleProvider.new)
|
55
|
+
converter = JcaPEMKeyConverter.new.setProvider("BC")
|
56
|
+
key = converter.getPrivateKey(kp.get_private_key_info)
|
57
|
+
alg = org.bouncycastle.openssl.PKCS8Generator::PBE_SHA1_RC4_128
|
58
|
+
enc = JceOpenSSLPKCS8EncryptorBuilder.new(alg).set_passsword(passphrase.to_java.to_char_array).build
|
59
|
+
sw = java.io.StringWriter.new
|
60
|
+
writer = JcaPEMWriter.new(sw)
|
61
|
+
writer.write_object(JcaPKCS8Generator.new(key, enc))
|
62
|
+
writer.flush
|
63
|
+
writer.close
|
64
|
+
sw
|
41
65
|
end
|
42
66
|
|
43
67
|
private
|
@@ -49,12 +73,17 @@ class TcpHelpers
|
|
49
73
|
file
|
50
74
|
end
|
51
75
|
|
52
|
-
def build_certificate(root_ca, root_key
|
76
|
+
def build_certificate(root_ca, root_key, name, password=nil)
|
53
77
|
key = ( root_key.nil? ? OpenSSL::PKey::RSA.new(2048) : root_key )
|
54
78
|
options = { :serial => 2, :subject => "/DC=org/DC=ruby-lang/CN=Ruby#{name}", :key => key, :issuer => root_ca.subject}
|
55
79
|
cert = new_certificate(options)
|
56
80
|
add_ca_extensions(cert, nil, root_ca)
|
57
|
-
|
81
|
+
if password
|
82
|
+
key_text = key.to_pem(OpenSSL::Cipher::AES256.new(:CFB), password)
|
83
|
+
[ cert.sign(key, OpenSSL::Digest::SHA256.new), key, key_text ]
|
84
|
+
else
|
85
|
+
[ cert.sign(key, OpenSSL::Digest::SHA256.new), key ]
|
86
|
+
end
|
58
87
|
end
|
59
88
|
|
60
89
|
def build_root_ca
|
Binary file
|
data/version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
5.2.
|
1
|
+
5.2.1
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: logstash-input-tcp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.2.
|
4
|
+
version: 5.2.1
|
5
5
|
platform: java
|
6
6
|
authors:
|
7
7
|
- Elastic
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-03-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
@@ -164,7 +164,7 @@ files:
|
|
164
164
|
- logstash-input-tcp.gemspec
|
165
165
|
- spec/inputs/tcp_spec.rb
|
166
166
|
- spec/spec_helper.rb
|
167
|
-
- vendor/jar-dependencies/org/logstash/inputs/logstash-input-tcp/5.2.
|
167
|
+
- vendor/jar-dependencies/org/logstash/inputs/logstash-input-tcp/5.2.1/logstash-input-tcp-5.2.1.jar
|
168
168
|
- version
|
169
169
|
homepage: http://www.elastic.co/guide/en/logstash/current/index.html
|
170
170
|
licenses:
|