Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: add support for TLSv1.3 #447

Merged
merged 7 commits into from
Mar 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## 6.3.0
- Added support for TLSv1.3. [#447](https://github.com/logstash-plugins/logstash-input-beats/pull/447)

## 6.2.6
- Update guidance regarding the private key format and encoding [#445](https://github.com/logstash-plugins/logstash-input-beats/pull/445)

Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
6.2.6
6.3.0
10 changes: 5 additions & 5 deletions docs/index.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -199,9 +199,10 @@ Flag to determine whether to add `host` field to event using the value supplied
===== `cipher_suites`

* Value type is <<array,array>>
* Default value is `java.lang.String[TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256]@459cfcca`
* Default value is `java.lang.String[TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256]@459cfcca`

The list of ciphers suite to use, listed by priorities.
The default values applies for OpenJDK 11.0.14 and higher, for older versions the list does not include suites not supported by the JDK, such as the ChaCha20 family of ciphers.

[id="plugins-{type}s-{plugin}-client_inactivity_timeout"]
===== `client_inactivity_timeout`
Expand Down Expand Up @@ -360,10 +361,10 @@ This option is only valid when `ssl_verify_mode` is set to `peer` or `force_peer
===== `tls_max_version`

* Value type is <<number,number>>
* Default value is `1.2`
* Default value is `1.3`

The maximum TLS version allowed for the encrypted connections. The value must be the one of the following:
1.0 for TLS 1.0, 1.1 for TLS 1.1, 1.2 for TLS 1.2
1.0 for TLS 1.0, 1.1 for TLS 1.1, 1.2 for TLS 1.2, 1.3 for TLS 1.3

[id="plugins-{type}s-{plugin}-tls_min_version"]
===== `tls_min_version`
Expand All @@ -372,12 +373,11 @@ The maximum TLS version allowed for the encrypted connections. The value must be
* Default value is `1`

The minimum TLS version allowed for the encrypted connections. The value must be one of the following:
1.0 for TLS 1.0, 1.1 for TLS 1.1, 1.2 for TLS 1.2
1.0 for TLS 1.0, 1.1 for TLS 1.1, 1.2 for TLS 1.2, 1.3 for TLS 1.3



[id="plugins-{type}s-{plugin}-common-options"]
include::{include_path}/{type}.asciidoc[]

:default_codec!:

3 changes: 2 additions & 1 deletion lib/logstash/inputs/beats/tls.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ def <=>(other)
TLS_PROTOCOL_OPTIONS = [
TLSOption.new("TLSv1", 1),
TLSOption.new("TLSv1.1", 1.1),
TLSOption.new("TLSv1.2", 1.2)
TLSOption.new("TLSv1.2", 1.2),
TLSOption.new("TLSv1.3", 1.3)
]

def self.min
Expand Down
4 changes: 2 additions & 2 deletions lib/tasks/test.rake
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ VENDOR_PATH = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "ve

#TODO: Figure out better means to keep this version in sync
if OS_PLATFORM == "linux"
FILEBEAT_URL = "https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-6.5.4-linux-x86_64.tar.gz"
FILEBEAT_URL = "https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-7.6.0-linux-x86_64.tar.gz"
elsif OS_PLATFORM == "darwin"
FILEBEAT_URL = "https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-6.5.4-darwin-x86_64.tar.gz"
FILEBEAT_URL = "https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-7.6.0-darwin-x86_64.tar.gz"
end

LSF_URL = "https://download.elastic.co/logstash-forwarder/binaries/logstash-forwarder_#{OS_PLATFORM}_amd64"
Expand Down
4 changes: 2 additions & 2 deletions spec/inputs/beats/tls_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
end

it "returns the maximum supported tls" do
expect(subject.max.version).to eq(1.2)
expect(subject.max.name).to eq("TLSv1.2")
expect(subject.max.version).to eq(1.3)
expect(subject.max.name).to eq("TLSv1.3")
end

describe ".get_supported" do
Expand Down
30 changes: 29 additions & 1 deletion spec/integration/filebeat_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
let(:filebeat_config) do
{
"filebeat" => {
"prospectors" => [{ "paths" => [log_file], "type" => "log" }],
"inputs" => [{ "paths" => [log_file], "type" => "log" }],
"scan_frequency" => "1s"
},
"output" => {
Expand Down Expand Up @@ -174,6 +174,34 @@
end
end

context "with TLSv1.3 client" do
let(:filebeat_config) do
super().merge({
"output" => {
"logstash" => {
"hosts" => ["#{host}:#{port}"],
"ssl" => {
"certificate_authorities" => certificate_authorities,
"versions" => ["TLSv1.3"],
}
}
},
"logging" => { "level" => "debug" }
})
end
include_examples "send events"

context "when TLSv1.3 enforced in plugin" do
let(:input_config) {
super().merge({
"tls_min_version" => "1.3"
})
}

include_examples "send events"
end
end

# Refactor this to use Flores's PKI instead of openssl command line
# see: https://github.com/jordansissel/ruby-flores/issues/7
context "with a passphrase" do
Expand Down
2 changes: 1 addition & 1 deletion spec/support/file_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def self.included(base)
end

def write_to_tmp_file(content)
file = Stud::Temporary.file
file = Stud::Temporary.file("test-logstash-input-beats", "w+", 0600)
file.write(content.to_s)
file.close
file.path
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/logstash/beats/Runner.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ static public void main(String[] args) throws Exception {


SslContextBuilder sslBuilder = new SslContextBuilder(sslCertificate, sslKey, null)
.setProtocols(new String[] { "TLSv1.2" })
.setProtocols(new String[] { "TLSv1.2", "TLSv1.3" })
.setCertificateAuthorities(certificateAuthorities);
SslHandlerProvider sslHandlerProvider = new SslHandlerProvider(sslBuilder.buildContext(), 10000);
server.setSslHandlerProvider(sslHandlerProvider);
Expand Down
38 changes: 26 additions & 12 deletions src/main/java/org/logstash/netty/SslContextBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
* Created by ph on 2016-05-27.
Expand All @@ -38,22 +40,36 @@ public enum SslClientVerifyMode {
private File sslCertificateFile;
private SslClientVerifyMode verifyMode = SslClientVerifyMode.FORCE_PEER;

private long handshakeTimeoutMilliseconds = 10000;
static final Set<String> SUPPORTED_CIPHERS = new HashSet<>(Arrays.asList(
((SSLServerSocketFactory) SSLServerSocketFactory.getDefault()).getSupportedCipherSuites()
));

/*
Mordern Ciphers List from
https://wiki.mozilla.org/Security/Server_Side_TLS
*/
private final static String[] DEFAULT_CIPHERS = new String[] {
private final static String[] DEFAULT_CIPHERS;
static {
String[] defaultCipherCandidates = new String[] {
// Modern compatibility
"TLS_AES_128_GCM_SHA256", // TLS 1.3
"TLS_AES_256_GCM_SHA384", // TLS 1.3
"TLS_CHACHA20_POLY1305_SHA256", // TLS 1.3 (since Java 11.0.14)
// Intermediate compatibility
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", // (since Java 11.0.14)
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", // (since Java 11.0.14)
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
// Backward compatibility
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256"
};
};
DEFAULT_CIPHERS = Arrays.stream(defaultCipherCandidates).filter(SUPPORTED_CIPHERS::contains).toArray(String[]::new);
}

/*
Reduced set of ciphers available when JCE Unlimited Strength Jurisdiction Policy is not installed.
Expand All @@ -65,10 +81,8 @@ public enum SslClientVerifyMode {
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256"
};

private String[] supportedCiphers = ((SSLServerSocketFactory)SSLServerSocketFactory
.getDefault()).getSupportedCipherSuites();
private String[] ciphers = DEFAULT_CIPHERS;
private String[] protocols = new String[] { "TLSv1.2" };
private String[] protocols = new String[] { "TLSv1.2", "TLSv1.3" };
private String[] certificateAuthorities;
private String passPhrase;

Expand All @@ -92,10 +106,10 @@ public SslContextBuilder setProtocols(String[] protocols) {
}

public SslContextBuilder setCipherSuites(String[] ciphersSuite) throws IllegalArgumentException {
for(String cipher : ciphersSuite) {
if(Arrays.asList(supportedCiphers).contains(cipher)) {
logger.debug("Cipher is supported: {}", cipher);
}else{
for (String cipher : ciphersSuite) {
if (SUPPORTED_CIPHERS.contains(cipher)) {
logger.debug("{} cipher is supported", cipher);
} else {
if (!isUnlimitedJCEAvailable()) {
logger.warn("JCE Unlimited Strength Jurisdiction Policy not installed");
}
Expand All @@ -108,7 +122,7 @@ public SslContextBuilder setCipherSuites(String[] ciphersSuite) throws IllegalAr
}

public static String[] getDefaultCiphers(){
if (isUnlimitedJCEAvailable()){
if (isUnlimitedJCEAvailable()) {
return DEFAULT_CIPHERS;
} else {
logger.warn("JCE Unlimited Strength Jurisdiction Policy not installed - max key length is 128 bits");
Expand Down Expand Up @@ -146,7 +160,7 @@ public SslContext buildContext() throws Exception {
io.netty.handler.ssl.SslContextBuilder builder = io.netty.handler.ssl.SslContextBuilder.forServer(sslCertificateFile, sslKeyFile, passPhrase);

if (logger.isDebugEnabled()) {
logger.debug("Available ciphers: " + Arrays.toString(supportedCiphers));
logger.debug("Available ciphers: " + SUPPORTED_CIPHERS);
logger.debug("Ciphers: " + Arrays.toString(ciphers));
}

Expand Down
26 changes: 23 additions & 3 deletions src/test/java/org/logstash/netty/SslContextBuilderTest.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,40 @@
package org.logstash.netty;

import org.hamcrest.core.Every;
import org.junit.Test;

import static org.junit.Assert.*;
import static org.hamcrest.Matchers.isIn;

import java.util.Arrays;
import java.util.List;

import javax.net.ssl.SSLServerSocketFactory;

/**
* Unit test for {@link SslContextBuilder}
*/
public class SslContextBuilderTest {
@Test
public void setProtocols() throws Exception {
SslContextBuilder sslContextBuilder = new SslContextBuilder("/tmp", "mykeyfile", "mypass");
assertArrayEquals(new String[]{"TLSv1.2"}, sslContextBuilder.getProtocols());
SslContextBuilder sslContextBuilder = new SslContextBuilder("src/test/resources/encrypted-pkcs8.crt", "src/test/resources/encrypted-pkcs8.key", "1234");
assertArrayEquals(new String[]{"TLSv1.2", "TLSv1.3"}, sslContextBuilder.getProtocols());
sslContextBuilder.setProtocols(new String[]{"TLSv1.1"});
assertArrayEquals(new String[]{"TLSv1.1"}, sslContextBuilder.getProtocols());
sslContextBuilder.setProtocols(new String[]{"TLSv1.1", "TLSv1.2"});
assertArrayEquals(new String[]{"TLSv1.1", "TLSv1.2"}, sslContextBuilder.getProtocols());
}
}

@Test
public void getDefaultCiphers() throws Exception
{
String[] defaultCiphers = SslContextBuilder.getDefaultCiphers();

assertTrue(defaultCiphers.length > 0);

// Check that default ciphers is the subset of default ciphers of current Java version.
SSLServerSocketFactory ssf = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
List<String> availableCiphers = Arrays.asList(ssf.getSupportedCipherSuites());
assertThat(Arrays.asList(defaultCiphers), Every.everyItem(isIn(availableCiphers)));
}
}
11 changes: 11 additions & 0 deletions src/test/resources/encrypted-pkcs8.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-----BEGIN CERTIFICATE-----
MIIBqjCCARMCFCWVL+Vfx2IIr8d2/GMqsmfzPLD/MA0GCSqGSIb3DQEBDQUAMBQx
EjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0yMTA4MjUxMDUyNTRaFw0yNDEyMDcxMDUy
NTRaMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
gYkCgYEA8HAsVcCjcolfzYR0siWOAg+xFG4fIahO6PH2Oi0l3zosa4KX2dlt3nFS
2PmgG9MNIDfXwI+BoM6QXB7O7Ch/YUhOz4GGDv3ptCjTYWyA9KZBrgLpiBFsCdt+
DiW7JBbt0OnMJGhVEsZa2Byh3HOxYqkvC2y4fET4OXdj2uX56B0CAwEAATANBgkq
hkiG9w0BAQ0FAAOBgQAW/tHI3AnyKYsJ9uaqvndUnVTIDHEEPNFE/xMM3mtQiL8f
qVYPq4V4C1Z5RD2xBI/skPngaZRWmqFrshEz2EccKe8gzdfyGQG89MQAB8QWn4dJ
bXUcnXO4hcSD4y3SiZYXJYNj37I2qJ2DfYBx7pScGYdjzIr/OJNK5EIGZI1Bvg==
-----END CERTIFICATE-----
18 changes: 18 additions & 0 deletions src/test/resources/encrypted-pkcs8.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIC1DBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQIE3AUYJOvgkYCAggA
MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECO0VArsmaac3BIICgOpQitrzl0AC
uV2AD2kgBfOUvTVe9jVJZ2Pvawo7IRxZZQl/7wMHfm6uB666OVSOVfKEa6nXJhBn
JzJYXJHBqzQwgsoGfg25Y6bSvIU8ydkCzwEf2P93cexFCCDJE1B8p1/lxJViEfOq
2CujkKgS3YZCet/03qt/ktkx3qBk9dOk6+xCXorUgzjwGL2SzSH7Su1kXoVKTu5/
TRCPyD552l8kjtyqNngfOu3xcd0+FFF/e/bUe4qX1bpYNza/Cs/AJAoxHhhGag+O
26bZ/LTbsN56fM95PvytRvomyj2rMGFJtz1j56R7iYujLUZu7XgCkxq0/t/3mWgX
0VxOy0zvppZO0XRky1uwSLPsaopZlPIN6s61JAJciT0O884Hi4citNA0hskOCnJA
vyy0lGa9goZh/cJKjr7W6w2ZkkPwMOKI6YMIup7Fo+7pfG2h0EDxCZyK2JWVl2n0
vyKMUNl3yrqDbxTk0vURz9qMx/q2cY6zK6+gi+uvfjvRM4oz3nCREbuVjykvEAXo
OhmN69CN6f8QwJ6wF4JNtxwwkHy+70dLoQg/FYKIfSbA3aoxpgXtr/2d/vkZYjya
zjcaIrM/WEoPLFvieMHrOmlRZhbVI9BVjhrAyTmT+sQV+GJ7GJirJpmn06VHz5Ln
ZNE4+ZSH4ODf+JQdh+LajyI8wQKfh4YMr498i5qHyw/KnKtbPjevbY25uWvEKzce
RWv9nt7VsSQhXuR2U85yc6sqFQQ01QM9xUdhcVB9Eu7FvloDrC6pgaFIXy3IVcfH
AJPiyRBv1bNad9wdh/+O5MyoKrhAI37YfH2fEKMdR2QKBEnCs94mjFPLy2Kc9kww
eVwJJw/VXbE=
-----END ENCRYPTED PRIVATE KEY-----