Skip to content

Commit 7e562ad

Browse files
UXabreArend Lapere
authored andcommitted
Support TLS certificate & key pair
1 parent aeb60e2 commit 7e562ad

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
@@ -287,7 +287,9 @@ This plugin supports the following configuration options plus the
287287
| <<plugins-{type}s-{plugin}-ilm_policy>> |<<string,string>>|No
288288
| <<plugins-{type}s-{plugin}-ilm_rollover_alias>> |<<string,string>>|No
289289
| <<plugins-{type}s-{plugin}-index>> |<<string,string>>|No
290+
| <<plugins-{type}s-{plugin}-tls_certificate>> |a valid filesystem path|No
290291
| <<plugins-{type}s-{plugin}-keystore>> |a valid filesystem path|No
292+
| <<plugins-{type}s-{plugin}-tls_private_key>> |a valid filesystem path|No
291293
| <<plugins-{type}s-{plugin}-keystore_password>> |<<password,password>>|No
292294
| <<plugins-{type}s-{plugin}-manage_template>> |<<boolean,boolean>>|No
293295
| <<plugins-{type}s-{plugin}-parameters>> |<<hash,hash>>|No
@@ -628,6 +630,24 @@ Logstash uses
628630
http://www.joda.org/joda-time/apidocs/org/joda/time/format/DateTimeFormat.html[Joda
629631
formats] for the index pattern from event timestamp.
630632

633+
[id="plugins-{type}s-{plugin}-tls_certificate"]
634+
===== `tls_certificate`
635+
636+
* Value type is <<path,path>>
637+
* There is no default value for this setting.
638+
639+
The tls_certificate used to present a client certificate to the server.
640+
It accepts .pem formatted files
641+
642+
[id="plugins-{type}s-{plugin}-tls_private_key"]
643+
===== `tls_private_key`
644+
645+
* Value type is <<path,path>>
646+
* There is no default value for this setting.
647+
648+
The tls_private_key used to present a client private key to the server, to be used in conjunction with tls_certificate.
649+
It accepts .pkcs8 formatted files
650+
631651

632652
[id="plugins-{type}s-{plugin}-keystore"]
633653
===== `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 @@ def self.included(mod)
6666
# Set the keystore password
6767
mod.config :keystore_password, :validate => :password
6868

69+
# The certificate to present to the server. (only pem format supported)
70+
mod.config :tls_certificate, :validate => :path
71+
# The private key to present to the server. (only pkcs8 format supported)
72+
mod.config :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
@@ -77,6 +77,121 @@
7777
end.and_call_original
7878
subject.register
7979
end
80+
end
81+
82+
context "when using ssl with pem-encoded client certificates" do
83+
let(:tls_certificate) { Stud::Temporary.file.path }
84+
let(:tls_private_key) { Stud::Temporary.file.path }
85+
before do
86+
`openssl req -x509 -batch -nodes -newkey rsa:2048 -keyout #{tls_private_key} -out #{tls_certificate}`
87+
end
88+
89+
after :each do
90+
File.delete(tls_certificate)
91+
File.delete(tls_private_key)
92+
subject.close
93+
end
94+
95+
subject do
96+
settings = {
97+
"hosts" => "node01",
98+
"ssl" => true,
99+
"tls_certificate" => tls_certificate,
100+
"tls_private_key" => tls_private_key
101+
}
102+
next LogStash::Outputs::ElasticSearch.new(settings)
103+
end
104+
105+
it "should pass the pem certificate parameters to the ES client" do
106+
expect(::Manticore::Client)
107+
.to receive(:new) { |args| expect(args[:ssl]).to include(:client_cert => tls_certificate, :client_key => tls_private_key) }
108+
.and_return(manticore_double)
109+
subject.register
110+
end
80111

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

0 commit comments

Comments
 (0)