Skip to content
Closed
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
20 changes: 20 additions & 0 deletions docs/index.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,9 @@ This plugin supports the following configuration options plus the
| <<plugins-{type}s-{plugin}-ilm_policy>> |<<string,string>>|No
| <<plugins-{type}s-{plugin}-ilm_rollover_alias>> |<<string,string>>|No
| <<plugins-{type}s-{plugin}-index>> |<<string,string>>|No
| <<plugins-{type}s-{plugin}-tls_certificate>> |a valid filesystem path|No
| <<plugins-{type}s-{plugin}-keystore>> |a valid filesystem path|No
| <<plugins-{type}s-{plugin}-tls_private_key>> |a valid filesystem path|No
| <<plugins-{type}s-{plugin}-keystore_password>> |<<password,password>>|No
| <<plugins-{type}s-{plugin}-manage_template>> |<<boolean,boolean>>|No
| <<plugins-{type}s-{plugin}-parameters>> |<<hash,hash>>|No
Expand Down Expand Up @@ -738,6 +740,24 @@ Logstash uses
http://www.joda.org/joda-time/apidocs/org/joda/time/format/DateTimeFormat.html[Joda
formats] for the index pattern from event timestamp.

[id="plugins-{type}s-{plugin}-tls_certificate"]
===== `tls_certificate`

* Value type is <<path,path>>
* There is no default value for this setting.

The tls_certificate used to present a client certificate to the server.
It accepts .pem formatted files

[id="plugins-{type}s-{plugin}-tls_private_key"]
===== `tls_private_key`

* Value type is <<path,path>>
* There is no default value for this setting.

The tls_private_key used to present a client private key to the server, to be used in conjunction with tls_certificate.
It accepts .pkcs8 formatted files


[id="plugins-{type}s-{plugin}-keystore"]
===== `keystore`
Expand Down
16 changes: 14 additions & 2 deletions lib/logstash/outputs/elasticsearch/http_client_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,21 @@ def self.setup_ssl(logger, params)

return {:ssl => {:enabled => false}} if params["ssl"] == false

cacert, truststore, truststore_password, keystore, keystore_password =
params.values_at('cacert', 'truststore', 'truststore_password', 'keystore', 'keystore_password')
cacert, truststore, truststore_password, keystore, keystore_password, tls_certificate, tls_private_key =
params.values_at('cacert', 'truststore', 'truststore_password', 'keystore', 'keystore_password', 'tls_certificate', 'tls_private_key')

if cacert && truststore
raise(LogStash::ConfigurationError, "Use either \"cacert\" or \"truststore\" when configuring the CA certificate") if truststore
end

if tls_certificate && keystore
raise(LogStash::ConfigurationError, "Use either \"tls_certificate\" or \"keystore\" when configuring the client certificate")
end

if (tls_private_key && !tls_certificate) || (tls_certificate && !tls_private_key)
raise(LogStash::ConfigurationError, "Both a \"tls_private_key\" and a \"tls_certificate\" need to be present")
end

ssl_options = {:enabled => true}

if cacert
Expand All @@ -131,7 +139,11 @@ def self.setup_ssl(logger, params)
if keystore
ssl_options[:keystore] = keystore
ssl_options[:keystore_password] = keystore_password.value if keystore_password
elsif tls_certificate && tls_private_key
ssl_options[:client_cert] = tls_certificate
ssl_options[:client_key] = tls_private_key
end

if !params["ssl_certificate_verification"]
logger.warn [
"** WARNING ** Detected UNSAFE options in elasticsearch output configuration!",
Expand Down
5 changes: 5 additions & 0 deletions lib/logstash/plugin_mixins/elasticsearch/api_configs.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ module APIConfigs
# Set the keystore password
:keystore_password => { :validate => :password },

# The certificate to present to the server. (only pem format supported)
:tls_certificate => { :validate => :path },
# The private key to present to the server. (only pkcs8 format supported)
:tls_private_key => { :validate => :path },

# This setting asks Elasticsearch for the list of all cluster nodes and adds them to the hosts list.
# Note: This will return ALL nodes with HTTP enabled (including master nodes!). If you use
# this with master nodes, you probably want to disable HTTP on them by setting
Expand Down
115 changes: 115 additions & 0 deletions spec/unit/outputs/elasticsearch_ssl_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,121 @@
end.and_call_original
subject.register
end
end

context "when using ssl with pem-encoded client certificates" do
let(:tls_certificate) { Stud::Temporary.file.path }
let(:tls_private_key) { Stud::Temporary.file.path }
before do
`openssl req -x509 -batch -nodes -newkey rsa:2048 -keyout #{tls_private_key} -out #{tls_certificate}`
end

after :each do
File.delete(tls_certificate)
File.delete(tls_private_key)
subject.close
end

subject do
settings = {
"hosts" => "node01",
"ssl" => true,
"tls_certificate" => tls_certificate,
"tls_private_key" => tls_private_key
}
next LogStash::Outputs::ElasticSearch.new(settings)
end

it "should pass the pem certificate parameters to the ES client" do
expect(::Manticore::Client)
.to receive(:new) { |args| expect(args[:ssl]).to include(:client_cert => tls_certificate, :client_key => tls_private_key) }
.and_return(manticore_double)
subject.register
end

end

context "when using both pem-encoded and jks-encoded client certificates" do
let(:tls_certificate) { Stud::Temporary.file.path }
let(:tls_private_key) { Stud::Temporary.file.path }
before do
`openssl req -x509 -batch -nodes -newkey rsa:2048 -keyout #{tls_private_key} -out #{tls_certificate}`
end

after :each do
File.delete(tls_private_key)
File.delete(tls_certificate)
subject.close
end

subject do
settings = {
"hosts" => "node01",
"ssl" => true,
"tls_certificate" => tls_certificate,
"tls_private_key" => tls_private_key,
# just any file will do for this test
"keystore" => tls_certificate
}
next LogStash::Outputs::ElasticSearch.new(settings)
end

it "should fail to load the plugin" do
expect { subject.register }.to raise_error(LogStash::ConfigurationError)
end
end

context "when configuring only tls_certificate but ommitting the private_key" do
let(:tls_certificate) { Stud::Temporary.file.path }
let(:tls_private_key) { Stud::Temporary.file.path }
before do
`openssl req -x509 -batch -nodes -newkey rsa:2048 -keyout #{tls_private_key} -out #{tls_certificate}`
end

after :each do
File.delete(tls_private_key)
File.delete(tls_certificate)
subject.close
end

subject do
settings = {
"hosts" => "node01",
"ssl" => true,
"tls_certificate" => tls_certificate,
}
next LogStash::Outputs::ElasticSearch.new(settings)
end

it "should fail to load the plugin" do
expect { subject.register }.to raise_error(LogStash::ConfigurationError)
end
end

context "when configuring only private_key but ommitting the tls_certificate" do
let(:tls_certificate) { Stud::Temporary.file.path }
let(:tls_private_key) { Stud::Temporary.file.path }
before do
`openssl req -x509 -batch -nodes -newkey rsa:2048 -keyout #{tls_private_key} -out #{tls_certificate}`
end

after :each do
File.delete(tls_private_key)
File.delete(tls_certificate)
subject.close
end

subject do
settings = {
"hosts" => "node01",
"ssl" => true,
"tls_private_key" => tls_private_key,
}
next LogStash::Outputs::ElasticSearch.new(settings)
end

it "should fail to load the plugin" do
expect { subject.register }.to raise_error(LogStash::ConfigurationError)
end
end
end