Skip to content

Commit 42e0a07

Browse files
UXabreArend Lapere
authored andcommitted
Support TLS certificate & key pair
1 parent f0b6a6a commit 42e0a07

File tree

4 files changed

+154
-2
lines changed

4 files changed

+154
-2
lines changed

docs/index.asciidoc

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,9 @@ This plugin supports the following configuration options plus the
333333
| <<plugins-{type}s-{plugin}-ilm_policy>> |<<string,string>>|No
334334
| <<plugins-{type}s-{plugin}-ilm_rollover_alias>> |<<string,string>>|No
335335
| <<plugins-{type}s-{plugin}-index>> |<<string,string>>|No
336+
| <<plugins-{type}s-{plugin}-tls_certificate>> |a valid filesystem path|No
336337
| <<plugins-{type}s-{plugin}-keystore>> |a valid filesystem path|No
338+
| <<plugins-{type}s-{plugin}-tls_private_key>> |a valid filesystem path|No
337339
| <<plugins-{type}s-{plugin}-keystore_password>> |<<password,password>>|No
338340
| <<plugins-{type}s-{plugin}-manage_template>> |<<boolean,boolean>>|No
339341
| <<plugins-{type}s-{plugin}-parameters>> |<<hash,hash>>|No
@@ -738,6 +740,24 @@ Logstash uses
738740
http://www.joda.org/joda-time/apidocs/org/joda/time/format/DateTimeFormat.html[Joda
739741
formats] for the index pattern from event timestamp.
740742

743+
[id="plugins-{type}s-{plugin}-tls_certificate"]
744+
===== `tls_certificate`
745+
746+
* Value type is <<path,path>>
747+
* There is no default value for this setting.
748+
749+
The tls_certificate used to present a client certificate to the server.
750+
It accepts .pem formatted files
751+
752+
[id="plugins-{type}s-{plugin}-tls_private_key"]
753+
===== `tls_private_key`
754+
755+
* Value type is <<path,path>>
756+
* There is no default value for this setting.
757+
758+
The tls_private_key used to present a client private key to the server, to be used in conjunction with tls_certificate.
759+
It accepts .pkcs8 formatted files
760+
741761

742762
[id="plugins-{type}s-{plugin}-keystore"]
743763
===== `keystore`

lib/logstash/outputs/elasticsearch/http_client_builder.rb

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,21 @@ def self.setup_ssl(logger, params)
112112

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

115-
cacert, truststore, truststore_password, keystore, keystore_password =
116-
params.values_at('cacert', 'truststore', 'truststore_password', 'keystore', 'keystore_password')
115+
cacert, truststore, truststore_password, keystore, keystore_password, tls_certificate, tls_private_key =
116+
params.values_at('cacert', 'truststore', 'truststore_password', 'keystore', 'keystore_password', 'tls_certificate', 'tls_private_key')
117117

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

122+
if tls_certificate && keystore
123+
raise(LogStash::ConfigurationError, "Use either \"tls_certificate\" or \"keystore\" when configuring the client certificate")
124+
end
125+
126+
if (tls_private_key && !tls_certificate) || (tls_certificate && !tls_private_key)
127+
raise(LogStash::ConfigurationError, "Both a \"tls_private_key\" and a \"tls_certificate\" need to be present")
128+
end
129+
122130
ssl_options = {:enabled => true}
123131

124132
if cacert
@@ -131,7 +139,11 @@ def self.setup_ssl(logger, params)
131139
if keystore
132140
ssl_options[:keystore] = keystore
133141
ssl_options[:keystore_password] = keystore_password.value if keystore_password
142+
elsif tls_certificate && tls_private_key
143+
ssl_options[:client_cert] = tls_certificate
144+
ssl_options[:client_key] = tls_private_key
134145
end
146+
135147
if !params["ssl_certificate_verification"]
136148
logger.warn [
137149
"** WARNING ** Detected UNSAFE options in elasticsearch output configuration!",

lib/logstash/plugin_mixins/elasticsearch/api_configs.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ module APIConfigs
6666
# Set the keystore password
6767
:keystore_password => { :validate => :password },
6868

69+
# The certificate to present to the server. (only pem format supported)
70+
:tls_certificate => { :validate => :path },
71+
# The private key to present to the server. (only pkcs8 format supported)
72+
:tls_private_key => { :validate => :path },
73+
6974
# This setting asks Elasticsearch for the list of all cluster nodes and adds them to the hosts list.
7075
# Note: This will return ALL nodes with HTTP enabled (including master nodes!). If you use
7176
# this with master nodes, you probably want to disable HTTP on them by setting

spec/unit/outputs/elasticsearch_ssl_spec.rb

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,121 @@
7676
end.and_call_original
7777
subject.register
7878
end
79+
end
80+
81+
context "when using ssl with pem-encoded client certificates" do
82+
let(:tls_certificate) { Stud::Temporary.file.path }
83+
let(:tls_private_key) { Stud::Temporary.file.path }
84+
before do
85+
`openssl req -x509 -batch -nodes -newkey rsa:2048 -keyout #{tls_private_key} -out #{tls_certificate}`
86+
end
87+
88+
after :each do
89+
File.delete(tls_certificate)
90+
File.delete(tls_private_key)
91+
subject.close
92+
end
93+
94+
subject do
95+
settings = {
96+
"hosts" => "node01",
97+
"ssl" => true,
98+
"tls_certificate" => tls_certificate,
99+
"tls_private_key" => tls_private_key
100+
}
101+
next LogStash::Outputs::ElasticSearch.new(settings)
102+
end
103+
104+
it "should pass the pem certificate parameters to the ES client" do
105+
expect(::Manticore::Client)
106+
.to receive(:new) { |args| expect(args[:ssl]).to include(:client_cert => tls_certificate, :client_key => tls_private_key) }
107+
.and_return(manticore_double)
108+
subject.register
109+
end
79110

80111
end
112+
113+
context "when using both pem-encoded and jks-encoded client certificates" do
114+
let(:tls_certificate) { Stud::Temporary.file.path }
115+
let(:tls_private_key) { Stud::Temporary.file.path }
116+
before do
117+
`openssl req -x509 -batch -nodes -newkey rsa:2048 -keyout #{tls_private_key} -out #{tls_certificate}`
118+
end
119+
120+
after :each do
121+
File.delete(tls_private_key)
122+
File.delete(tls_certificate)
123+
subject.close
124+
end
125+
126+
subject do
127+
settings = {
128+
"hosts" => "node01",
129+
"ssl" => true,
130+
"tls_certificate" => tls_certificate,
131+
"tls_private_key" => tls_private_key,
132+
# just any file will do for this test
133+
"keystore" => tls_certificate
134+
}
135+
next LogStash::Outputs::ElasticSearch.new(settings)
136+
end
137+
138+
it "should fail to load the plugin" do
139+
expect { subject.register }.to raise_error(LogStash::ConfigurationError)
140+
end
141+
end
142+
143+
context "when configuring only tls_certificate but ommitting the private_key" do
144+
let(:tls_certificate) { Stud::Temporary.file.path }
145+
let(:tls_private_key) { Stud::Temporary.file.path }
146+
before do
147+
`openssl req -x509 -batch -nodes -newkey rsa:2048 -keyout #{tls_private_key} -out #{tls_certificate}`
148+
end
149+
150+
after :each do
151+
File.delete(tls_private_key)
152+
File.delete(tls_certificate)
153+
subject.close
154+
end
155+
156+
subject do
157+
settings = {
158+
"hosts" => "node01",
159+
"ssl" => true,
160+
"tls_certificate" => tls_certificate,
161+
}
162+
next LogStash::Outputs::ElasticSearch.new(settings)
163+
end
164+
165+
it "should fail to load the plugin" do
166+
expect { subject.register }.to raise_error(LogStash::ConfigurationError)
167+
end
168+
end
169+
170+
context "when configuring only private_key but ommitting the tls_certificate" do
171+
let(:tls_certificate) { Stud::Temporary.file.path }
172+
let(:tls_private_key) { Stud::Temporary.file.path }
173+
before do
174+
`openssl req -x509 -batch -nodes -newkey rsa:2048 -keyout #{tls_private_key} -out #{tls_certificate}`
175+
end
176+
177+
after :each do
178+
File.delete(tls_private_key)
179+
File.delete(tls_certificate)
180+
subject.close
181+
end
182+
183+
subject do
184+
settings = {
185+
"hosts" => "node01",
186+
"ssl" => true,
187+
"tls_private_key" => tls_private_key,
188+
}
189+
next LogStash::Outputs::ElasticSearch.new(settings)
190+
end
191+
192+
it "should fail to load the plugin" do
193+
expect { subject.register }.to raise_error(LogStash::ConfigurationError)
194+
end
195+
end
81196
end

0 commit comments

Comments
 (0)