logstash-input-tcp 5.2.0-java → 5.2.1-java
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/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:
|