Skip to content

Commit cfe44f3

Browse files
authored
Standardize and add SSL settings (#1118)
This commit made the plugin SSL settings consistent with the naming convention defined in the meta issue: elastic/logstash#14905. It added the following SSL settings: ssl_truststore_type: The format of the truststore file ssl_keystore_type: The format of the keystore file ssl_certificate: OpenSSL-style X.509 certificate file to authenticate the client ssl_key: OpenSSL-style RSA private key that corresponds to the ssl_certificate ssl_cipher_suites: The list of cipher suites And deprecated: ssl in favor of ssl_enabled cacert in favor of ssl_certificate_authorities keystore in favor of ssl_keystore_path keystore_password in favor of ssl_keystore_password truststore in favor of ssl_truststore_path truststore_password in favor of ssl_truststore_password ssl_certificate_verification in favor of ssl_verification_mode
1 parent 4507240 commit cfe44f3

File tree

11 files changed

+652
-176
lines changed

11 files changed

+652
-176
lines changed

CHANGELOG.md

+16
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
## 11.14.0
2+
- Added SSL settings for: [#1115](https://github.com/logstash-plugins/logstash-output-elasticsearch/pull/1115)
3+
- `ssl_truststore_type`: The format of the truststore file
4+
- `ssl_keystore_type`: The format of the keystore file
5+
- `ssl_certificate`: OpenSSL-style X.509 certificate file to authenticate the client
6+
- `ssl_key`: OpenSSL-style RSA private key that corresponds to the `ssl_certificate`
7+
- `ssl_cipher_suites`: The list of cipher suites
8+
- Reviewed and deprecated SSL settings to comply with Logstash's naming convention
9+
- Deprecated `ssl` in favor of `ssl_enabled`
10+
- Deprecated `cacert` in favor of `ssl_certificate_authorities`
11+
- Deprecated `keystore` in favor of `ssl_keystore_path`
12+
- Deprecated `keystore_password` in favor of `ssl_keystore_password`
13+
- Deprecated `truststore` in favor of `ssl_truststore_path`
14+
- Deprecated `truststore_password` in favor of `ssl_truststore_password`
15+
- Deprecated `ssl_certificate_verification` in favor of `ssl_verification_mode`
16+
117
## 11.13.1
218
- Avoid crash by ensuring ILM settings are injected in the correct location depending on the default (or custom) template format, template_api setting and ES version [#1102](https://github.com/logstash-plugins/logstash-output-elasticsearch/pull/1102)
319

docs/index.asciidoc

+211-62
Large diffs are not rendered by default.

lib/logstash/outputs/elasticsearch.rb

+52
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,14 @@ class LogStash::Outputs::ElasticSearch < LogStash::Outputs::Base
9696
require "logstash/outputs/elasticsearch/data_stream_support"
9797
require 'logstash/plugin_mixins/ecs_compatibility_support'
9898
require 'logstash/plugin_mixins/deprecation_logger_support'
99+
require 'logstash/plugin_mixins/normalize_config_support'
99100

100101
# Protocol agnostic methods
101102
include(LogStash::PluginMixins::ElasticSearch::Common)
102103

104+
# Config normalization helpers
105+
include(LogStash::PluginMixins::NormalizeConfigSupport)
106+
103107
# Methods for ILM support
104108
include(LogStash::Outputs::ElasticSearch::Ilm)
105109

@@ -282,6 +286,8 @@ def initialize(*params)
282286
end
283287

284288
def register
289+
setup_ssl_params!
290+
285291
if !failure_type_logging_whitelist.empty?
286292
log_message = "'failure_type_logging_whitelist' is deprecated and in a future version of Elasticsearch " +
287293
"output plugin will be removed, please use 'silence_errors_in_log' instead."
@@ -622,6 +628,52 @@ def setup_template_manager_defaults(data_stream_enabled)
622628
end
623629
end
624630

631+
def setup_ssl_params!
632+
@ssl_enabled = normalize_config(:ssl_enabled) do |normalize|
633+
normalize.with_deprecated_alias(:ssl)
634+
end
635+
636+
@ssl_certificate_authorities = normalize_config(:ssl_certificate_authorities) do |normalize|
637+
normalize.with_deprecated_mapping(:cacert) do |cacert|
638+
[cacert]
639+
end
640+
end
641+
642+
@ssl_keystore_path = normalize_config(:ssl_keystore_path) do |normalize|
643+
normalize.with_deprecated_alias(:keystore)
644+
end
645+
646+
@ssl_keystore_password = normalize_config(:ssl_keystore_password) do |normalize|
647+
normalize.with_deprecated_alias(:keystore_password)
648+
end
649+
650+
@ssl_truststore_path = normalize_config(:ssl_truststore_path) do |normalize|
651+
normalize.with_deprecated_alias(:truststore)
652+
end
653+
654+
@ssl_truststore_password = normalize_config(:ssl_truststore_password) do |normalize|
655+
normalize.with_deprecated_alias(:truststore_password)
656+
end
657+
658+
@ssl_verification_mode = normalize_config(:ssl_verification_mode) do |normalize|
659+
normalize.with_deprecated_mapping(:ssl_certificate_verification) do |ssl_certificate_verification|
660+
if ssl_certificate_verification == true
661+
"full"
662+
else
663+
"none"
664+
end
665+
end
666+
end
667+
668+
params['ssl_enabled'] = @ssl_enabled unless @ssl_enabled.nil?
669+
params['ssl_certificate_authorities'] = @ssl_certificate_authorities unless @ssl_certificate_authorities.nil?
670+
params['ssl_keystore_path'] = @ssl_keystore_path unless @ssl_keystore_path.nil?
671+
params['ssl_keystore_password'] = @ssl_keystore_password unless @ssl_keystore_password.nil?
672+
params['ssl_truststore_path'] = @ssl_truststore_path unless @ssl_truststore_path.nil?
673+
params['ssl_truststore_password'] = @ssl_truststore_password unless @ssl_truststore_password.nil?
674+
params['ssl_verification_mode'] = @ssl_verification_mode unless @ssl_verification_mode.nil?
675+
end
676+
625677
# To be overidden by the -java version
626678
VALID_HTTP_ACTIONS = ["index", "delete", "create", "update"]
627679
def valid_actions

lib/logstash/outputs/elasticsearch/http_client_builder.rb

+44-19
Original file line numberDiff line numberDiff line change
@@ -107,38 +107,53 @@ def self.create_http_client(options)
107107
end
108108

109109
def self.setup_ssl(logger, params)
110-
params["ssl"] = true if params["hosts"].any? {|h| h.scheme == "https" }
111-
return {} if params["ssl"].nil?
110+
params["ssl_enabled"] = true if params["hosts"].any? {|h| h.scheme == "https" }
111+
return {} if params["ssl_enabled"].nil?
112112

113-
return {:ssl => {:enabled => false}} if params["ssl"] == false
113+
return {:ssl => {:enabled => false}} if params["ssl_enabled"] == false
114114

115-
cacert, truststore, truststore_password, keystore, keystore_password =
116-
params.values_at('cacert', 'truststore', 'truststore_password', 'keystore', 'keystore_password')
115+
ssl_certificate_authorities, ssl_truststore_path, ssl_certificate, ssl_keystore_path = params.values_at('ssl_certificate_authorities', 'ssl_truststore_path', 'ssl_certificate', 'ssl_keystore_path')
117116

118-
if cacert && truststore
119-
raise(LogStash::ConfigurationError, "Use either \"cacert\" or \"truststore\" when configuring the CA certificate") if truststore
117+
if ssl_certificate_authorities && ssl_truststore_path
118+
raise LogStash::ConfigurationError, 'Use either "ssl_certificate_authorities/cacert" or "ssl_truststore_path/truststore" when configuring the CA certificate'
119+
end
120+
121+
if ssl_certificate && ssl_keystore_path
122+
raise LogStash::ConfigurationError, 'Use either "ssl_certificate" or "ssl_keystore_path/keystore" when configuring client certificates'
120123
end
121124

122125
ssl_options = {:enabled => true}
123126

124-
if cacert
125-
ssl_options[:ca_file] = cacert
126-
elsif truststore
127-
ssl_options[:truststore_password] = truststore_password.value if truststore_password
127+
if ssl_certificate_authorities&.any?
128+
raise LogStash::ConfigurationError, 'Multiple values on "ssl_certificate_authorities" are not supported by this plugin' if ssl_certificate_authorities.size > 1
129+
ssl_options[:ca_file] = ssl_certificate_authorities.first
128130
end
129131

130-
ssl_options[:truststore] = truststore if truststore
131-
if keystore
132-
ssl_options[:keystore] = keystore
133-
ssl_options[:keystore_password] = keystore_password.value if keystore_password
132+
setup_ssl_store(ssl_options, 'truststore', params)
133+
setup_ssl_store(ssl_options, 'keystore', params)
134+
135+
ssl_key = params["ssl_key"]
136+
if ssl_certificate
137+
raise LogStash::ConfigurationError, 'Using an "ssl_certificate" requires an "ssl_key"' unless ssl_key
138+
ssl_options[:client_cert] = ssl_certificate
139+
ssl_options[:client_key] = ssl_key
140+
elsif !ssl_key.nil?
141+
raise LogStash::ConfigurationError, 'An "ssl_certificate" is required when using an "ssl_key"'
134142
end
135143

136-
if !params["ssl_certificate_verification"]
137-
logger.warn "You have enabled encryption but DISABLED certificate verification, " +
138-
"to make sure your data is secure remove `ssl_certificate_verification => false`"
139-
ssl_options[:verify] = :disable # false accepts self-signed but still validates hostname
144+
ssl_verification_mode = params["ssl_verification_mode"]
145+
unless ssl_verification_mode.nil?
146+
case ssl_verification_mode
147+
when 'none'
148+
logger.warn "You have enabled encryption but DISABLED certificate verification, " +
149+
"to make sure your data is secure set `ssl_verification_mode => full`"
150+
ssl_options[:verify] = :disable
151+
else
152+
ssl_options[:verify] = :strict
153+
end
140154
end
141155

156+
ssl_options[:cipher_suites] = params["ssl_cipher_suites"] if params.include?("ssl_cipher_suites")
142157
ssl_options[:trust_strategy] = params["ssl_trust_strategy"] if params.include?("ssl_trust_strategy")
143158

144159
protocols = params['ssl_supported_protocols']
@@ -147,6 +162,16 @@ def self.setup_ssl(logger, params)
147162
{ ssl: ssl_options }
148163
end
149164

165+
# @param kind is a string [truststore|keystore]
166+
def self.setup_ssl_store(ssl_options, kind, params)
167+
store_path = params["ssl_#{kind}_path"]
168+
if store_path
169+
ssl_options[kind.to_sym] = store_path
170+
ssl_options["#{kind}_type".to_sym] = params["ssl_#{kind}_type"] if params.include?("ssl_#{kind}_type")
171+
ssl_options["#{kind}_password".to_sym] = params["ssl_#{kind}_password"].value if params.include?("ssl_#{kind}_password")
172+
end
173+
end
174+
150175
def self.setup_basic_auth(logger, params)
151176
user, password = params["user"], params["password"]
152177

lib/logstash/plugin_mixins/elasticsearch/api_configs.rb

+51-7
Original file line numberDiff line numberDiff line change
@@ -45,35 +45,79 @@ module APIConfigs
4545
# Enable SSL/TLS secured communication to Elasticsearch cluster. Leaving this unspecified will use whatever scheme
4646
# is specified in the URLs listed in 'hosts'. If no explicit protocol is specified plain HTTP will be used.
4747
# If SSL is explicitly disabled here the plugin will refuse to start if an HTTPS URL is given in 'hosts'
48-
:ssl => { :validate => :boolean },
48+
:ssl => { :validate => :boolean, :deprecated => "Set 'ssl_enabled' instead." },
49+
50+
# Enable SSL/TLS secured communication to Elasticsearch cluster. Leaving this unspecified will use whatever scheme
51+
# is specified in the URLs listed in 'hosts'. If no explicit protocol is specified plain HTTP will be used.
52+
# If SSL is explicitly disabled here the plugin will refuse to start if an HTTPS URL is given in 'hosts'
53+
:ssl_enabled => { :validate => :boolean },
4954

5055
# Option to validate the server's certificate. Disabling this severely compromises security.
5156
# For more information on disabling certificate verification please read
5257
# https://www.cs.utexas.edu/~shmat/shmat_ccs12.pdf
53-
:ssl_certificate_verification => { :validate => :boolean, :default => true },
58+
:ssl_certificate_verification => { :validate => :boolean, :default => true, :deprecated => "Set 'ssl_verification_mode' instead." },
59+
60+
# Options to verify the server's certificate.
61+
# "full": validates that the provided certificate has an issue date that’s within the not_before and not_after dates;
62+
# chains to a trusted Certificate Authority (CA); has a hostname or IP address that matches the names within the certificate.
63+
# "none": performs no certificate validation. Disabling this severely compromises security (https://www.cs.utexas.edu/~shmat/shmat_ccs12.pdf)
64+
:ssl_verification_mode => { :validate => %w[full none], :default => 'full' },
5465

5566
# The .cer or .pem file to validate the server's certificate
56-
:cacert => { :validate => :path },
67+
:cacert => { :validate => :path, :deprecated => "Set 'ssl_certificate_authorities' instead." },
68+
69+
# The .cer or .pem files to validate the server's certificate
70+
:ssl_certificate_authorities => { :validate => :path, :list => true },
5771

5872
# One or more hex-encoded SHA256 fingerprints to trust as Certificate Authorities
5973
:ca_trusted_fingerprint => LogStash::PluginMixins::CATrustedFingerprintSupport,
6074

6175
# The JKS truststore to validate the server's certificate.
6276
# Use either `:truststore` or `:cacert`
63-
:truststore => { :validate => :path },
77+
:truststore => { :validate => :path, :deprecated => "Set 'ssl_truststore_path' instead." },
78+
79+
# The JKS truststore to validate the server's certificate.
80+
# Use either `:ssl_truststore_path` or `:ssl_certificate_authorities`
81+
:ssl_truststore_path => { :validate => :path },
82+
83+
# The format of the truststore file. It must be either jks or pkcs12
84+
:ssl_truststore_type => { :validate => %w[pkcs12 jks] },
85+
86+
# Set the truststore password
87+
:truststore_password => { :validate => :password, :deprecated => "Use 'ssl_truststore_password' instead." },
6488

6589
# Set the truststore password
66-
:truststore_password => { :validate => :password },
90+
:ssl_truststore_password => { :validate => :password },
6791

6892
# The keystore used to present a certificate to the server.
6993
# It can be either .jks or .p12
70-
:keystore => { :validate => :path },
94+
:keystore => { :validate => :path, :deprecated => "Set 'ssl_keystore_path' instead." },
95+
96+
# The keystore used to present a certificate to the server.
97+
# It can be either .jks or .p12
98+
:ssl_keystore_path => { :validate => :path },
99+
100+
# The format of the keystore file. It must be either jks or pkcs12
101+
:ssl_keystore_type => { :validate => %w[pkcs12 jks] },
71102

72103
# Set the keystore password
73-
:keystore_password => { :validate => :password },
104+
:keystore_password => { :validate => :password, :deprecated => "Set 'ssl_keystore_password' instead." },
105+
106+
# Set the keystore password
107+
:ssl_keystore_password => { :validate => :password },
74108

75109
:ssl_supported_protocols => { :validate => ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'], :default => [], :list => true },
76110

111+
# OpenSSL-style X.509 certificate certificate to authenticate the client
112+
:ssl_certificate => { :validate => :path },
113+
114+
# OpenSSL-style RSA private key to authenticate the client
115+
:ssl_key => { :validate => :path },
116+
117+
# The list of cipher suites to use, listed by priorities.
118+
# Supported cipher suites vary depending on which version of Java is used.
119+
:ssl_cipher_suites => { :validate => :string, :list => true },
120+
77121
# This setting asks Elasticsearch for the list of all cluster nodes and adds them to the hosts list.
78122
# Note: This will return ALL nodes with HTTP enabled (including master nodes!). If you use
79123
# this with master nodes, you probably want to disable HTTP on them by setting

lib/logstash/plugin_mixins/elasticsearch/common.rb

+2-3
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ def build_client(license_checker = nil)
2828

2929
setup_hosts
3030

31-
32-
params['ssl'] = effectively_ssl? unless params.include?('ssl')
31+
params['ssl_enabled'] = effectively_ssl? unless params.include?('ssl_enabled')
3332

3433
# inject the TrustStrategy from CATrustedFingerprintSupport
3534
if trust_strategy_for_ca_trusted_fingerprint
@@ -74,7 +73,7 @@ def setup_hosts
7473
end
7574

7675
def effectively_ssl?
77-
return @ssl unless @ssl.nil?
76+
return @ssl_enabled unless @ssl_enabled.nil?
7877

7978
hosts = Array(@hosts)
8079
return false if hosts.nil? || hosts.empty?

logstash-output-elasticsearch.gemspec

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Gem::Specification.new do |s|
22
s.name = 'logstash-output-elasticsearch'
3-
s.version = '11.13.1'
3+
s.version = '11.14.0'
44
s.licenses = ['apache-2.0']
55
s.summary = "Stores logs in Elasticsearch"
66
s.description = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program"
@@ -26,6 +26,7 @@ Gem::Specification.new do |s|
2626
s.add_runtime_dependency 'logstash-mixin-ecs_compatibility_support', '~>1.0'
2727
s.add_runtime_dependency 'logstash-mixin-deprecation_logger_support', '~>1.0'
2828
s.add_runtime_dependency 'logstash-mixin-ca_trusted_fingerprint_support', '~>1.0'
29+
s.add_runtime_dependency 'logstash-mixin-normalize_config_support', '~>1.0'
2930

3031
s.add_development_dependency 'logstash-codec-plain'
3132
s.add_development_dependency 'logstash-devutils'

spec/integration/outputs/index_spec.rb

+16-16
Original file line numberDiff line numberDiff line change
@@ -289,8 +289,8 @@ def curl_and_get_json_response(url, method: :get, retrieve_err_payload: false);
289289
"hosts" => [ get_host_port ],
290290
"user" => user,
291291
"password" => password,
292-
"ssl" => true,
293-
"cacert" => cacert,
292+
"ssl_enabled" => true,
293+
"ssl_certificate_authorities" => cacert,
294294
"index" => index
295295
}
296296
end
@@ -302,7 +302,7 @@ def curl_and_get_json_response(url, method: :get, retrieve_err_payload: false);
302302

303303
context "when no keystore nor ca cert set and verification is disabled" do
304304
let(:config) do
305-
super().tap { |config| config.delete('cacert') }.merge('ssl_certificate_verification' => false)
305+
super().tap { |config| config.delete('ssl_certificate_authorities') }.merge('ssl_verification_mode' => 'none')
306306
end
307307

308308
include_examples("an indexer", true)
@@ -311,9 +311,9 @@ def curl_and_get_json_response(url, method: :get, retrieve_err_payload: false);
311311
context "when keystore is set and verification is disabled" do
312312
let(:config) do
313313
super().merge(
314-
'ssl_certificate_verification' => false,
315-
'keystore' => 'spec/fixtures/test_certs/test.p12',
316-
'keystore_password' => '1234567890'
314+
'ssl_verification_mode' => 'none',
315+
'ssl_keystore_path' => 'spec/fixtures/test_certs/test.p12',
316+
'ssl_keystore_password' => '1234567890'
317317
)
318318
end
319319

@@ -322,10 +322,10 @@ def curl_and_get_json_response(url, method: :get, retrieve_err_payload: false);
322322

323323
context "when keystore has self-signed cert and verification is disabled" do
324324
let(:config) do
325-
super().tap { |config| config.delete('cacert') }.merge(
326-
'ssl_certificate_verification' => false,
327-
'keystore' => 'spec/fixtures/test_certs/test_self_signed.p12',
328-
'keystore_password' => '1234567890'
325+
super().tap { |config| config.delete('ssl_certificate_authorities') }.merge(
326+
'ssl_verification_mode' => 'none',
327+
'ssl_keystore_path' => 'spec/fixtures/test_certs/test_self_signed.p12',
328+
'ssl_keystore_password' => '1234567890'
329329
)
330330
end
331331

@@ -349,30 +349,30 @@ def curl_and_get_json_response(url, method: :get, retrieve_err_payload: false);
349349
let(:config) do
350350
{
351351
"hosts" => ["https://#{CGI.escape(user)}:#{CGI.escape(password)}@elasticsearch:9200"],
352-
"ssl" => true,
353-
"cacert" => "spec/fixtures/test_certs/test.crt",
352+
"ssl_enabled" => true,
353+
"ssl_certificate_authorities" => "spec/fixtures/test_certs/test.crt",
354354
"index" => index
355355
}
356356
end
357357

358358
include_examples("an indexer", true)
359359
end
360360

361-
context "without providing `cacert`" do
361+
context "without providing `ssl_certificate_authorities`" do
362362
let(:config) do
363363
super().tap do |c|
364-
c.delete("cacert")
364+
c.delete("ssl_certificate_authorities")
365365
end
366366
end
367367

368368
it_behaves_like("PKIX path failure")
369369
end
370370

371371
if Gem::Version.new(LOGSTASH_VERSION) >= Gem::Version.new("8.3.0")
372-
context "with `ca_trusted_fingerprint` instead of `cacert`" do
372+
context "with `ca_trusted_fingerprint` instead of `ssl_certificate_authorities`" do
373373
let(:config) do
374374
super().tap do |c|
375-
c.delete("cacert")
375+
c.delete("ssl_certificate_authorities")
376376
c.update("ca_trusted_fingerprint" => ca_trusted_fingerprint)
377377
end
378378
end

0 commit comments

Comments
 (0)