logstash-filter-elasticsearch 3.7.1 → 3.9.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3546d5b2082aa7d3f83c0a855c6fcebe9203c83bb6d5a7831da2237c9b5b2672
4
- data.tar.gz: fa20e8e10194ebbe86b3ceea46f582d4a0c50c66122bd75968a4df149d848648
3
+ metadata.gz: 3264c43657eea13f942de4c005f0996dfc30e845cf7643fe374ec6918d400d83
4
+ data.tar.gz: bfdb2268299b87ed893d778411c450c1758977cd2e2e3c051e0679aa9467fd37
5
5
  SHA512:
6
- metadata.gz: a4278733f509ed60089bcaae0e2d509acb66fd8b0ba3bb361d889b167991625e790a3d2422870a114ad3527f418ed2389b95fc7abee7334e8c2e7e07eb1dedc2
7
- data.tar.gz: fffc8107d0ce39d3cc3d12669a0c13340f70fa5d1ec6807ea953b0f2d1f2049594372c6360ec87e7a8c2eb4b0682e85d0b906c6028e458ba7cf350ca45d5653d
6
+ metadata.gz: 759cb7f87175ac2894adc715e108399fd6d27af1cae91fdf91bd05aa5312678e9963fde3c36a25519b6eac3b53a326ed203583a1f22a94fdd66239014ddf2433
7
+ data.tar.gz: 5dda79137366f9d40d1fddf21b6eb92da6377187eb470d3e725b1cfb338bd1e2f2296263e67fec7f10bf7b9a4d611bcca243d393235727ffef200264da5ef4c9
data/CHANGELOG.md CHANGED
@@ -1,3 +1,28 @@
1
+ ## 3.9.4
2
+ - Fix: a regression (in LS 7.14.0) where due the elasticsearch client update (from 5.0.5 to 7.5.0) the `Authorization`
3
+ header isn't passed, this leads to the plugin not being able to leverage `user`/`password` credentials set by the user.
4
+ [#148](https://github.com/logstash-plugins/logstash-filter-elasticsearch/pull/148)
5
+ - Fix: default setting for `hosts` not working (since 3.7.0) GH-147
6
+ - Fix: mutating @hosts variable which leads to issues with multiple worker threads GH-129
7
+
8
+ ## 3.9.3
9
+ - [DOC] Update links to use shared attributes [#144](https://github.com/logstash-plugins/logstash-filter-elasticsearch/pull/144)
10
+
11
+ ## 3.9.2
12
+ - [DOC] Fixed links to restructured Logstash-to-cloud docs [#142](https://github.com/logstash-plugins/logstash-filter-elasticsearch/pull/142)
13
+
14
+ ## 3.9.1
15
+ - [DOC] Document the permissions required in secured clusters [#140](https://github.com/logstash-plugins/logstash-filter-elasticsearch/pull/140)
16
+
17
+ ## 3.9.0
18
+ - Add support to define a proxy with the proxy config option [#134](https://github.com/logstash-plugins/logstash-filter-elasticsearch/pull/134)
19
+
20
+ ## 3.8.0
21
+ - Added api_key support [#132](https://github.com/logstash-plugins/logstash-filter-elasticsearch/pull/132)
22
+
23
+ ## 3.7.2
24
+ - [DOC] Removed outdated compatibility notice [#131](https://github.com/logstash-plugins/logstash-filter-elasticsearch/pull/131)
25
+
1
26
  ## 3.7.1
2
27
  - Fix: solves an issue where non-ascii unicode values in a template were not handled correctly [#128](https://github.com/logstash-plugins/logstash-filter-elasticsearch/pull/128)
3
28
 
data/CONTRIBUTORS CHANGED
@@ -9,6 +9,9 @@ Contributors:
9
9
  * Richard Pijnenburg (electrical)
10
10
  * Suyog Rao (suyograo)
11
11
  * Adrian Solom (addrians)
12
+ * Colin Surprenant (colinsurprenant)
13
+ * Andres Rodriguez (roaksoax)
14
+ * Luca Belluccini (lucabelluccini)
12
15
 
13
16
  Note: If you've sent us patches, bug reports, or otherwise contributed to
14
17
  Logstash, and you aren't on the list above and want to be, please let us know
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Logstash Plugin
2
2
 
3
- [![Travis Build Status](https://travis-ci.org/logstash-plugins/logstash-filter-elasticsearch.svg)](https://travis-ci.org/logstash-plugins/logstash-filter-elasticsearch)
3
+ [![Travis Build Status](https://travis-ci.com/logstash-plugins/logstash-filter-elasticsearch.svg)](https://travis-ci.com/logstash-plugins/logstash-filter-elasticsearch)
4
4
 
5
5
  This is a plugin for [Logstash](https://github.com/elastic/logstash).
6
6
 
data/docs/index.asciidoc CHANGED
@@ -20,21 +20,12 @@ include::{include_path}/plugin_header.asciidoc[]
20
20
 
21
21
  ==== Description
22
22
 
23
- .Compatibility Note
24
- [NOTE]
25
- ================================================================================
26
- Starting with Elasticsearch 5.3, there's an {ref}/modules-http.html[HTTP setting]
27
- called `http.content_type.required`. If this option is set to `true`, and you
28
- are using Logstash 2.4 through 5.2, you need to update the Elasticsearch filter
29
- plugin to version 3.1.1 or higher.
30
-
31
- ================================================================================
32
-
33
23
  Search Elasticsearch for a previous log event and copy some fields from it
34
- into the current event. Below are two complete examples of how this filter might
24
+ into the current event. Below are two complete examples of how this filter might
35
25
  be used.
36
26
 
37
- The first example uses the legacy 'query' parameter where the user is limited to an Elasticsearch query_string.
27
+ The first example uses the legacy 'query' parameter where the user is limited to
28
+ an Elasticsearch query_string.
38
29
  Whenever logstash receives an "end" event, it uses this elasticsearch
39
30
  filter to find the matching "start" event based on some operation identifier.
40
31
  Then it copies the `@timestamp` field from the "start" event into a new field on
@@ -111,6 +102,20 @@ Notice also that when you use `query_template`, the Logstash attributes `result_
111
102
  and `sort` will be ignored. They should be specified directly in the JSON
112
103
  template, as shown in the example above.
113
104
 
105
+ [id="plugins-{type}s-{plugin}-auth"]
106
+ ==== Authentication
107
+
108
+ Authentication to a secure Elasticsearch cluster is possible using _one_ of the following options:
109
+
110
+ * <<plugins-{type}s-{plugin}-user>> AND <<plugins-{type}s-{plugin}-password>>
111
+ * <<plugins-{type}s-{plugin}-cloud_auth>>
112
+ * <<plugins-{type}s-{plugin}-api_key>>
113
+
114
+ [id="plugins-{type}s-{plugin}-autz"]
115
+ ==== Authorization
116
+
117
+ Authorization to a secure Elasticsearch cluster requires `read` permission at index level and `monitoring` permissions at cluster level.
118
+ The `monitoring` permission at cluster level is necessary to perform periodic connectivity checks.
114
119
 
115
120
  [id="plugins-{type}s-{plugin}-options"]
116
121
  ==== Elasticsearch Filter Configuration Options
@@ -121,6 +126,7 @@ This plugin supports the following configuration options plus the <<plugins-{typ
121
126
  |=======================================================================
122
127
  |Setting |Input type|Required
123
128
  | <<plugins-{type}s-{plugin}-aggregation_fields>> |<<hash,hash>>|No
129
+ | <<plugins-{type}s-{plugin}-api_key>> |<<password,password>>|No
124
130
  | <<plugins-{type}s-{plugin}-ca_file>> |a valid filesystem path|No
125
131
  | <<plugins-{type}s-{plugin}-cloud_auth>> |<<password,password>>|No
126
132
  | <<plugins-{type}s-{plugin}-cloud_id>> |<<string,string>>|No
@@ -130,6 +136,7 @@ This plugin supports the following configuration options plus the <<plugins-{typ
130
136
  | <<plugins-{type}s-{plugin}-hosts>> |<<array,array>>|No
131
137
  | <<plugins-{type}s-{plugin}-index>> |<<string,string>>|No
132
138
  | <<plugins-{type}s-{plugin}-password>> |<<password,password>>|No
139
+ | <<plugins-{type}s-{plugin}-proxy>> |<<uri,uri>>|No
133
140
  | <<plugins-{type}s-{plugin}-query>> |<<string,string>>|No
134
141
  | <<plugins-{type}s-{plugin}-query_template>> |<<string,string>>|No
135
142
  | <<plugins-{type}s-{plugin}-result_size>> |<<number,number>>|No
@@ -162,6 +169,18 @@ Example:
162
169
  }
163
170
  }
164
171
 
172
+ [id="plugins-{type}s-{plugin}-api_key"]
173
+ ===== `api_key`
174
+
175
+ * Value type is <<password,password>>
176
+ * There is no default value for this setting.
177
+
178
+ Authenticate using Elasticsearch API key. Note that this option also requires
179
+ enabling the `ssl` option.
180
+
181
+ Format is `id:api_key` where `id` and `api_key` are as returned by the
182
+ Elasticsearch {ref}/security-api-create-api-key.html[Create API key API].
183
+
165
184
  [id="plugins-{type}s-{plugin}-ca_file"]
166
185
  ===== `ca_file`
167
186
 
@@ -170,6 +189,28 @@ Example:
170
189
 
171
190
  SSL Certificate Authority file
172
191
 
192
+ [id="plugins-{type}s-{plugin}-cloud_auth"]
193
+ ===== `cloud_auth`
194
+
195
+ * Value type is <<password,password>>
196
+ * There is no default value for this setting.
197
+
198
+ Cloud authentication string ("<username>:<password>" format) is an alternative for the `user`/`password` pair.
199
+
200
+ For more info, check out the
201
+ {logstash-ref}/connecting-to-cloud.html[Logstash-to-Cloud documentation].
202
+
203
+ [id="plugins-{type}s-{plugin}-cloud_id"]
204
+ ===== `cloud_id`
205
+
206
+ * Value type is <<string,string>>
207
+ * There is no default value for this setting.
208
+
209
+ Cloud ID, from the Elastic Cloud web console. If set `hosts` should not be used.
210
+
211
+ For more info, check out the
212
+ {logstash-ref}/connecting-to-cloud.html[Logstash-to-Cloud documentation].
213
+
173
214
  [id="plugins-{type}s-{plugin}-docinfo_fields"]
174
215
  ===== `docinfo_fields`
175
216
 
@@ -243,14 +284,26 @@ Field substitution (e.g. `index-name-%{date_field}`) is available
243
284
 
244
285
  Basic Auth - password
245
286
 
287
+ [id="plugins-{type}s-{plugin}-proxy"]
288
+ ===== `proxy`
289
+
290
+ * Value type is <<uri,uri>>
291
+ * There is no default value for this setting.
292
+
293
+ Set the address of a forward HTTP proxy.
294
+ An empty string is treated as if proxy was not set, and is useful when using
295
+ environment variables e.g. `proxy => '${LS_PROXY:}'`.
296
+
246
297
  [id="plugins-{type}s-{plugin}-query"]
247
298
  ===== `query`
248
299
 
249
300
  * Value type is <<string,string>>
250
301
  * There is no default value for this setting.
251
302
 
252
- Elasticsearch query string. Read the Elasticsearch query string documentation.
253
- for more info at: https://www.elastic.co/guide/en/elasticsearch/reference/master/query-dsl-query-string-query.html#query-string-syntax
303
+ Elasticsearch query string. More information is available in the
304
+ {ref}/query-dsl-query-string-query.html#query-string-syntax[Elasticsearch query
305
+ string documentation].
306
+
254
307
 
255
308
  [id="plugins-{type}s-{plugin}-query_template"]
256
309
  ===== `query_template`
@@ -258,8 +311,8 @@ for more info at: https://www.elastic.co/guide/en/elasticsearch/reference/master
258
311
  * Value type is <<string,string>>
259
312
  * There is no default value for this setting.
260
313
 
261
- File path to elasticsearch query in DSL format. Read the Elasticsearch query documentation
262
- for more info at: https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html
314
+ File path to elasticsearch query in DSL format. More information is available in
315
+ the {ref}/query-dsl.html[Elasticsearch query documentation].
263
316
 
264
317
  [id="plugins-{type}s-{plugin}-result_size"]
265
318
  ===== `result_size`
@@ -301,27 +354,6 @@ Tags the event on failure to look up previous log event information. This can be
301
354
 
302
355
  Basic Auth - username
303
356
 
304
- [id="plugins-{type}s-{plugin}-cloud_auth"]
305
- ===== `cloud_auth`
306
-
307
- * Value type is <<password,password>>
308
- * There is no default value for this setting.
309
-
310
- Cloud authentication string ("<username>:<password>" format) is an alternative for the `user`/`password` pair.
311
-
312
- For more info, check out the https://www.elastic.co/guide/en/logstash/current/connecting-to-cloud.html#_cloud_auth[Logstash-to-Cloud documentation]
313
-
314
- [id="plugins-{type}s-{plugin}-cloud_id"]
315
- ===== `cloud_id`
316
-
317
- * Value type is <<string,string>>
318
- * There is no default value for this setting.
319
-
320
- Cloud ID, from the Elastic Cloud web console. If set `hosts` should not be used.
321
-
322
- For more info, check out the https://www.elastic.co/guide/en/logstash/current/connecting-to-cloud.html#_cloud_id[Logstash-to-Cloud documentation]
323
-
324
-
325
357
 
326
358
  [id="plugins-{type}s-{plugin}-common-options"]
327
359
  include::{include_path}/{type}.asciidoc[]
@@ -1,24 +1,15 @@
1
1
  # encoding: utf-8
2
2
  require "logstash/filters/base"
3
3
  require "logstash/namespace"
4
- require_relative "elasticsearch/client"
5
4
  require "logstash/json"
6
- require "logstash/util/safe_uri"
7
- java_import "java.util.concurrent.ConcurrentHashMap"
8
-
5
+ require_relative "elasticsearch/client"
6
+ require_relative "elasticsearch/patches/_elasticsearch_transport_http_manticore"
9
7
 
10
8
  class LogStash::Filters::Elasticsearch < LogStash::Filters::Base
11
9
  config_name "elasticsearch"
12
10
 
13
- DEFAULT_HOST = ::LogStash::Util::SafeURI.new("//localhost:9200")
14
-
15
11
  # List of elasticsearch hosts to use for querying.
16
- config :hosts, :validate => :array, :default => [ DEFAULT_HOST ]
17
-
18
- # Cloud ID, from the Elastic Cloud web console. If set `hosts` should not be used.
19
- #
20
- # For more info, check out the https://www.elastic.co/guide/en/logstash/current/connecting-to-cloud.html#_cloud_id[Logstash-to-Cloud documentation]
21
- config :cloud_id, :validate => :string
12
+ config :hosts, :validate => :array, :default => [ 'localhost:9200' ]
22
13
 
23
14
  # Comma-delimited list of index names to search; use `_all` or empty string to perform the operation on all indices.
24
15
  # Field substitution (e.g. `index-name-%{date_field}`) is available
@@ -50,11 +41,23 @@ class LogStash::Filters::Elasticsearch < LogStash::Filters::Base
50
41
  # Basic Auth - password
51
42
  config :password, :validate => :password
52
43
 
44
+ # Cloud ID, from the Elastic Cloud web console. If set `hosts` should not be used.
45
+ #
46
+ # For more info, check out the https://www.elastic.co/guide/en/logstash/current/connecting-to-cloud.html#_cloud_id[Logstash-to-Cloud documentation]
47
+ config :cloud_id, :validate => :string
48
+
53
49
  # Cloud authentication string ("<username>:<password>" format) is an alternative for the `user`/`password` configuration.
54
50
  #
55
51
  # For more info, check out the https://www.elastic.co/guide/en/logstash/current/connecting-to-cloud.html#_cloud_auth[Logstash-to-Cloud documentation]
56
52
  config :cloud_auth, :validate => :password
57
53
 
54
+ # Authenticate using Elasticsearch API key.
55
+ # format is id:api_key (as returned by https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-create-api-key.html[Create API key])
56
+ config :api_key, :validate => :password
57
+
58
+ # Set the address of a forward HTTP proxy.
59
+ config :proxy, :validate => :uri_or_empty
60
+
58
61
  # SSL
59
62
  config :ssl, :validate => :boolean, :default => false
60
63
 
@@ -72,6 +75,23 @@ class LogStash::Filters::Elasticsearch < LogStash::Filters::Base
72
75
 
73
76
  attr_reader :clients_pool
74
77
 
78
+ ##
79
+ # @override to handle proxy => '' as if none was set
80
+ # @param value [Array<Object>]
81
+ # @param validator [nil,Array,Symbol]
82
+ # @return [Array(true,Object)]: if validation is a success, a tuple containing `true` and the coerced value
83
+ # @return [Array(false,String)]: if validation is a failure, a tuple containing `false` and the failure reason.
84
+ def self.validate_value(value, validator)
85
+ return super unless validator == :uri_or_empty
86
+
87
+ value = deep_replace(value)
88
+ value = hash_or_array(value)
89
+
90
+ return true, value.first if value.size == 1 && value.first.empty?
91
+
92
+ return super(value, :uri)
93
+ end
94
+
75
95
  def register
76
96
  @clients_pool = java.util.concurrent.ConcurrentHashMap.new
77
97
 
@@ -84,10 +104,11 @@ class LogStash::Filters::Elasticsearch < LogStash::Filters::Base
84
104
  @query_dsl = file.read
85
105
  end
86
106
 
87
- fill_hosts_from_cloud_id
107
+ validate_authentication
88
108
  fill_user_password_from_cloud_auth
109
+ fill_hosts_from_cloud_id
89
110
 
90
- @hosts = Array(@hosts).map { |host| host.to_s } # for ES client URI#to_s
111
+ @hosts = Array(@hosts).map { |host| host.to_s } # potential SafeURI#to_s
91
112
 
92
113
  test_connection!
93
114
  end # def register
@@ -95,7 +116,7 @@ class LogStash::Filters::Elasticsearch < LogStash::Filters::Base
95
116
  def filter(event)
96
117
  matched = false
97
118
  begin
98
- params = {:index => event.sprintf(@index) }
119
+ params = { :index => event.sprintf(@index) }
99
120
 
100
121
  if @query_dsl
101
122
  query = LogStash::Json.load(event.sprintf(@query_dsl))
@@ -156,19 +177,22 @@ class LogStash::Filters::Elasticsearch < LogStash::Filters::Base
156
177
  end # def filter
157
178
 
158
179
  private
180
+
159
181
  def client_options
160
182
  {
183
+ :user => @user,
184
+ :password => @password,
185
+ :api_key => @api_key,
186
+ :proxy => @proxy,
161
187
  :ssl => @ssl,
162
- :hosts => @hosts,
163
188
  :ca_file => @ca_file,
164
- :logger => @logger
165
189
  }
166
190
  end
167
191
 
168
192
  def new_client
169
193
  # NOTE: could pass cloud-id/cloud-auth to client but than we would need to be stricter on ES version requirement
170
194
  # and also LS parsing might differ from ES client's parsing so for consistency we do not pass cloud options ...
171
- LogStash::Filters::ElasticsearchClient.new(@user, @password, client_options)
195
+ LogStash::Filters::ElasticsearchClient.new(@logger, @hosts, client_options)
172
196
  end
173
197
 
174
198
  def get_client
@@ -209,30 +233,42 @@ class LogStash::Filters::Elasticsearch < LogStash::Filters::Base
209
233
  end
210
234
 
211
235
  def hosts_default?(hosts)
212
- # NOTE: would be nice if pipeline allowed us a clean way to detect a config default :
213
- hosts.is_a?(Array) && hosts.size == 1 && hosts.first.equal?(DEFAULT_HOST)
236
+ hosts.is_a?(Array) && hosts.size == 1 && !original_params.key?('hosts')
214
237
  end
215
238
 
216
- def fill_hosts_from_cloud_id
217
- return unless @cloud_id
239
+ def validate_authentication
240
+ authn_options = 0
241
+ authn_options += 1 if @cloud_auth
242
+ authn_options += 1 if (@api_key && @api_key.value)
243
+ authn_options += 1 if (@user || (@password && @password.value))
218
244
 
219
- if @hosts && !hosts_default?(@hosts)
220
- raise LogStash::ConfigurationError, 'Both cloud_id and hosts specified, please only use one of those.'
245
+ if authn_options > 1
246
+ raise LogStash::ConfigurationError, 'Multiple authentication options are specified, please only use one of user/password, cloud_auth or api_key'
247
+ end
248
+
249
+ if @api_key && @api_key.value && @ssl != true
250
+ raise(LogStash::ConfigurationError, "Using api_key authentication requires SSL/TLS secured communication using the `ssl => true` option")
221
251
  end
222
- @hosts = parse_host_uri_from_cloud_id(@cloud_id)
223
252
  end
224
253
 
225
254
  def fill_user_password_from_cloud_auth
226
255
  return unless @cloud_auth
227
256
 
228
- if @user || @password
229
- raise LogStash::ConfigurationError, 'Both cloud_auth and user/password specified, please only use one.'
230
- end
231
257
  @user, @password = parse_user_password_from_cloud_auth(@cloud_auth)
232
258
  params['user'], params['password'] = @user, @password
233
259
  end
234
260
 
261
+ def fill_hosts_from_cloud_id
262
+ return unless @cloud_id
263
+
264
+ if @hosts && !hosts_default?(@hosts)
265
+ raise LogStash::ConfigurationError, 'Both cloud_id and hosts specified, please only use one of those.'
266
+ end
267
+ @hosts = parse_host_uri_from_cloud_id(@cloud_id)
268
+ end
269
+
235
270
  def parse_host_uri_from_cloud_id(cloud_id)
271
+ require 'logstash/util/safe_uri'
236
272
  begin # might not be available on older LS
237
273
  require 'logstash/util/cloud_setting_id'
238
274
  rescue LoadError
@@ -10,23 +10,26 @@ module LogStash
10
10
 
11
11
  attr_reader :client
12
12
 
13
- def initialize(user, password, options={})
14
- ssl = options.fetch(:ssl, false)
15
- hosts = options[:hosts]
16
- @logger = options[:logger]
17
-
18
- transport_options = {}
19
- if user && password
20
- token = ::Base64.strict_encode64("#{user}:#{password.value}")
21
- transport_options[:headers] = { Authorization: "Basic #{token}" }
22
- end
23
-
24
- hosts.map! {|h| { host: h, scheme: 'https' } } if ssl
13
+ def initialize(logger, hosts, options = {})
14
+ ssl = options.fetch(:ssl, false)
15
+ user = options.fetch(:user, nil)
16
+ password = options.fetch(:password, nil)
17
+ api_key = options.fetch(:api_key, nil)
18
+ proxy = options.fetch(:proxy, nil)
19
+
20
+ transport_options = {:headers => {}}
21
+ transport_options[:headers].merge!(setup_basic_auth(user, password))
22
+ transport_options[:headers].merge!(setup_api_key(api_key))
23
+
24
+ logger.warn "Supplied proxy setting (proxy => '') has no effect" if @proxy.eql?('')
25
+ transport_options[:proxy] = proxy.to_s if proxy && !proxy.eql?('')
26
+
27
+ hosts = hosts.map { |host| { host: host, scheme: 'https' } } if ssl
25
28
  # set ca_file even if ssl isn't on, since the host can be an https url
26
29
  ssl_options = { ssl: true, ca_file: options[:ca_file] } if options[:ca_file]
27
30
  ssl_options ||= {}
28
31
 
29
- @logger.info("New ElasticSearch filter client", :hosts => hosts)
32
+ logger.info("New ElasticSearch filter client", :hosts => hosts)
30
33
  @client = ::Elasticsearch::Client.new(hosts: hosts, transport_options: transport_options, transport_class: ::Elasticsearch::Transport::Transport::HTTP::Manticore, :ssl => ssl_options)
31
34
  end
32
35
 
@@ -34,6 +37,21 @@ module LogStash
34
37
  @client.search(params)
35
38
  end
36
39
 
40
+ private
41
+
42
+ def setup_basic_auth(user, password)
43
+ return {} unless user && password && password.value
44
+
45
+ token = ::Base64.strict_encode64("#{user}:#{password.value}")
46
+ { 'Authorization' => "Basic #{token}" }
47
+ end
48
+
49
+ def setup_api_key(api_key)
50
+ return {} unless (api_key && api_key.value)
51
+
52
+ token = ::Base64.strict_encode64(api_key.value)
53
+ { 'Authorization' => "ApiKey #{token}" }
54
+ end
37
55
  end
38
56
  end
39
57
  end
@@ -0,0 +1,33 @@
1
+ # encoding: utf-8
2
+ require "elasticsearch"
3
+ require "elasticsearch/transport/transport/http/manticore"
4
+
5
+ es_client_version = Gem.loaded_specs['elasticsearch-transport'].version
6
+ if es_client_version >= Gem::Version.new('7.2') && es_client_version < Gem::Version.new('7.16')
7
+ # elasticsearch-transport 7.2.0 - 7.14.0 had a bug where setting http headers
8
+ # ES::Client.new ..., transport_options: { headers: { 'Authorization' => ... } }
9
+ # would be lost https://github.com/elastic/elasticsearch-ruby/issues/1428
10
+ #
11
+ # NOTE: needs to be idempotent as input ES plugin might apply the same patch!
12
+ #
13
+ # @private
14
+ module Elasticsearch
15
+ module Transport
16
+ module Transport
17
+ module HTTP
18
+ class Manticore
19
+
20
+ def apply_headers(request_options, options)
21
+ headers = (options && options[:headers]) || {}
22
+ headers[CONTENT_TYPE_STR] = find_value(headers, CONTENT_TYPE_REGEX) || DEFAULT_CONTENT_TYPE
23
+ headers[USER_AGENT_STR] = find_value(headers, USER_AGENT_REGEX) || user_agent_header
24
+ headers[ACCEPT_ENCODING] = GZIP if use_compression?
25
+ (request_options[:headers] ||= {}).merge!(headers) # this line was changed
26
+ end
27
+
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
 
3
3
  s.name = 'logstash-filter-elasticsearch'
4
- s.version = '3.7.1'
4
+ s.version = '3.9.4'
5
5
  s.licenses = ['Apache License (2.0)']
6
6
  s.summary = "Copies fields from previous log events in Elasticsearch to current events "
7
7
  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"
@@ -21,7 +21,7 @@ Gem::Specification.new do |s|
21
21
 
22
22
  # Gem dependencies
23
23
  s.add_runtime_dependency "logstash-core-plugin-api", ">= 1.60", "<= 2.99"
24
- s.add_runtime_dependency 'elasticsearch', ">= 5.0.3"
24
+ s.add_runtime_dependency 'elasticsearch', ">= 5.0.5" # LS >= 6.7 and < 7.14 all used version 5.0.5
25
25
  s.add_runtime_dependency 'manticore', "~> 0.6"
26
26
 
27
27
  s.add_development_dependency 'logstash-devutils'
data/spec/es_helper.rb CHANGED
@@ -7,8 +7,12 @@ module ESHelper
7
7
  end
8
8
  end
9
9
 
10
- def self.get_client
11
- Elasticsearch::Client.new(:hosts => [get_host_port])
10
+ def self.get_client(credentials)
11
+ require 'elasticsearch/transport/transport/http/faraday' # supports user/password options
12
+ host, port = get_host_port.split(':')
13
+ host_opts = credentials.inject({}) { |h, (k, v)| h[k.to_sym] = v; h } # user: _, password: _
14
+ host_opts.merge! host: host, port: port, scheme: 'http'
15
+ Elasticsearch::Client.new(hosts: [host_opts], transport_class: Elasticsearch::Transport::Transport::HTTP::Faraday)
12
16
  end
13
17
 
14
18
  def self.doc_type
@@ -313,12 +313,12 @@ describe LogStash::Filters::Elasticsearch do
313
313
  'sample:dXMtY2VudHJhbDEuZ2NwLmNsb3VkLmVzLmlvJGFjMzFlYmI5MDI0MTc3MzE1NzA0M2MzNGZkMjZmZDQ2OjkyNDMkYTRjMDYyMzBlNDhjOGZjZTdiZTg4YTA3NGEzYmIzZTA6OTI0NA=='
314
314
  end
315
315
 
316
- let(:config) { super.merge({ 'cloud_id' => valid_cloud_id }) }
316
+ let(:config) { super().merge({ 'cloud_id' => valid_cloud_id }) }
317
317
 
318
318
  it "should set host(s)" do
319
319
  plugin.register
320
320
  client = plugin.send(:get_client).client
321
- expect( client.transport.hosts ).to eql [{
321
+ expect( extract_transport(client).hosts ).to eql [{
322
322
  :scheme => "https",
323
323
  :host => "ac31ebb90241773157043c34fd26fd46.us-central1.gcp.cloud.es.io",
324
324
  :port => 9243,
@@ -328,7 +328,7 @@ describe LogStash::Filters::Elasticsearch do
328
328
  end
329
329
 
330
330
  context 'invalid' do
331
- let(:config) { super.merge({ 'cloud_id' => 'invalid:dXMtY2VudHJhbDEuZ2NwLmNsb3VkLmVzLmlv' }) }
331
+ let(:config) { super().merge({ 'cloud_id' => 'invalid:dXMtY2VudHJhbDEuZ2NwLmNsb3VkLmVzLmlv' }) }
332
332
 
333
333
  it "should fail" do
334
334
  expect { plugin.register }.to raise_error LogStash::ConfigurationError, /cloud_id.*? is invalid/
@@ -336,7 +336,7 @@ describe LogStash::Filters::Elasticsearch do
336
336
  end
337
337
 
338
338
  context 'hosts also set' do
339
- let(:config) { super.merge({ 'cloud_id' => valid_cloud_id, 'hosts' => [ 'localhost:9200' ] }) }
339
+ let(:config) { super().merge({ 'cloud_id' => valid_cloud_id, 'hosts' => [ 'localhost:9200' ] }) }
340
340
 
341
341
  it "should fail" do
342
342
  expect { plugin.register }.to raise_error LogStash::ConfigurationError, /cloud_id and hosts/
@@ -345,18 +345,18 @@ describe LogStash::Filters::Elasticsearch do
345
345
  end if LOGSTASH_VERSION > '6.0'
346
346
 
347
347
  describe "cloud.auth" do
348
- let(:config) { super.merge({ 'cloud_auth' => LogStash::Util::Password.new('elastic:my-passwd-00') }) }
348
+ let(:config) { super().merge({ 'cloud_auth' => LogStash::Util::Password.new('elastic:my-passwd-00') }) }
349
349
 
350
350
  it "should set authorization" do
351
351
  plugin.register
352
352
  client = plugin.send(:get_client).client
353
- auth_header = client.transport.options[:transport_options][:headers][:Authorization]
353
+ auth_header = extract_transport(client).options[:transport_options][:headers]['Authorization']
354
354
 
355
355
  expect( auth_header ).to eql "Basic #{Base64.encode64('elastic:my-passwd-00').rstrip}"
356
356
  end
357
357
 
358
358
  context 'invalid' do
359
- let(:config) { super.merge({ 'cloud_auth' => 'invalid-format' }) }
359
+ let(:config) { super().merge({ 'cloud_auth' => 'invalid-format' }) }
360
360
 
361
361
  it "should fail" do
362
362
  expect { plugin.register }.to raise_error LogStash::ConfigurationError, /cloud_auth.*? format/
@@ -364,13 +364,82 @@ describe LogStash::Filters::Elasticsearch do
364
364
  end
365
365
 
366
366
  context 'user also set' do
367
- let(:config) { super.merge({ 'cloud_auth' => 'elastic:my-passwd-00', 'user' => 'another' }) }
367
+ let(:config) { super().merge({ 'cloud_auth' => 'elastic:my-passwd-00', 'user' => 'another' }) }
368
368
 
369
369
  it "should fail" do
370
- expect { plugin.register }.to raise_error LogStash::ConfigurationError, /cloud_auth and user/
370
+ expect { plugin.register }.to raise_error LogStash::ConfigurationError, /Multiple authentication options are specified/
371
371
  end
372
372
  end
373
373
  end if LOGSTASH_VERSION > '6.0'
374
+
375
+ describe "api_key" do
376
+ context "without ssl" do
377
+ let(:config) { super().merge({ 'api_key' => LogStash::Util::Password.new('foo:bar') }) }
378
+
379
+ it "should fail" do
380
+ expect { plugin.register }.to raise_error LogStash::ConfigurationError, /api_key authentication requires SSL\/TLS/
381
+ end
382
+ end
383
+
384
+ context "with ssl" do
385
+ let(:config) { super().merge({ 'api_key' => LogStash::Util::Password.new('foo:bar'), "ssl" => true }) }
386
+
387
+ it "should set authorization" do
388
+ plugin.register
389
+ client = plugin.send(:get_client).client
390
+ auth_header = extract_transport(client).options[:transport_options][:headers]['Authorization']
391
+
392
+ expect( auth_header ).to eql "ApiKey #{Base64.strict_encode64('foo:bar')}"
393
+ end
394
+
395
+ context 'user also set' do
396
+ let(:config) { super().merge({ 'api_key' => 'foo:bar', 'user' => 'another' }) }
397
+
398
+ it "should fail" do
399
+ expect { plugin.register }.to raise_error LogStash::ConfigurationError, /Multiple authentication options are specified/
400
+ end
401
+ end
402
+ end
403
+ end if LOGSTASH_VERSION > '6.0'
404
+
405
+ describe "proxy" do
406
+ context 'valid' do
407
+ let(:config) { super().merge({ 'proxy' => 'http://localhost:1234' }) }
408
+
409
+ it "should set proxy" do
410
+ plugin.register
411
+ client = plugin.send(:get_client).client
412
+ proxy = extract_transport(client).options[:transport_options][:proxy]
413
+
414
+ expect( proxy ).to eql "http://localhost:1234"
415
+ end
416
+ end
417
+
418
+ context 'invalid' do
419
+ let(:config) { super().merge({ 'proxy' => '${A_MISSING_ENV_VAR:}' }) }
420
+
421
+ it "should not set proxy" do
422
+ plugin.register
423
+ client = plugin.send(:get_client).client
424
+
425
+ expect( extract_transport(client).options[:transport_options] ).to_not include(:proxy)
426
+ end
427
+ end
428
+ end
429
+ end
430
+
431
+ describe "defaults" do
432
+
433
+ let(:config) { Hash.new }
434
+ let(:plugin) { described_class.new(config) }
435
+
436
+ before { allow(plugin).to receive(:test_connection!) }
437
+
438
+ it "should set localhost:9200 as hosts" do
439
+ plugin.register
440
+ client = plugin.send(:get_client).client
441
+ expect( extract_transport(client).hosts ).to eql [{ :host => "localhost", :port => 9200, :protocol => "http"}]
442
+ end
374
443
  end
375
444
 
376
445
  describe "query template" do
@@ -398,4 +467,10 @@ describe LogStash::Filters::Elasticsearch do
398
467
  plugin.filter(LogStash::Event.new)
399
468
  end
400
469
  end
470
+
471
+ # @note can be removed once gem depends on elasticsearch >= 6.x
472
+ def extract_transport(client) # on 7.x client.transport is a ES::Transport::Client
473
+ client.transport.respond_to?(:transport) ? client.transport.transport : client.transport
474
+ end
475
+
401
476
  end
@@ -6,21 +6,31 @@ require_relative "../../../spec/es_helper"
6
6
 
7
7
  describe LogStash::Filters::Elasticsearch, :integration => true do
8
8
 
9
+ ELASTIC_SECURITY_ENABLED = ENV['ELASTIC_SECURITY_ENABLED'].eql? 'true'
9
10
 
10
- let(:config) do
11
+ let(:base_config) do
11
12
  {
12
- "index" => 'logs',
13
- "hosts" => [ESHelper.get_host_port],
14
- "query" => "response: 404",
15
- "sort" => "response",
16
- "fields" => [ ["response", "code"] ],
13
+ "index" => 'logs',
14
+ "hosts" => [ESHelper.get_host_port],
15
+ "query" => "response: 404",
16
+ "sort" => "response",
17
+ "fields" => [ ["response", "code"] ],
17
18
  }
18
19
  end
20
+
21
+ let(:credentials) do
22
+ { 'user' => 'elastic', 'password' => ENV['ELASTIC_PASSWORD'] }
23
+ end
24
+
25
+ let(:config) do
26
+ ELASTIC_SECURITY_ENABLED ? base_config.merge(credentials) : base_config
27
+ end
28
+
19
29
  let(:plugin) { described_class.new(config) }
20
30
  let(:event) { LogStash::Event.new({}) }
21
31
 
22
32
  before(:each) do
23
- @es = ESHelper.get_client
33
+ @es = ESHelper.get_client(ELASTIC_SECURITY_ENABLED ? credentials : {})
24
34
  # Delete all templates first.
25
35
  # Clean ES of data before we start.
26
36
  @es.indices.delete_template(:name => "*")
@@ -30,11 +40,10 @@ describe LogStash::Filters::Elasticsearch, :integration => true do
30
40
  ESHelper.index_doc(@es, :index => 'logs', :body => { :response => 404, :this => 'that'})
31
41
  end
32
42
  @es.indices.refresh
33
-
34
- plugin.register
35
43
  end
36
44
 
37
45
  it "should enhance the current event with new data" do
46
+ plugin.register
38
47
  plugin.filter(event)
39
48
  expect(event.get('code')).to eq(404)
40
49
  end
@@ -42,20 +51,28 @@ describe LogStash::Filters::Elasticsearch, :integration => true do
42
51
  context "when retrieving a list of elements" do
43
52
 
44
53
  let(:config) do
45
- {
46
- "index" => 'logs',
47
- "hosts" => [ESHelper.get_host_port],
48
- "query" => "response: 404",
49
- "fields" => [ ["response", "code"] ],
50
- "sort" => "response",
51
- "result_size" => 10
52
- }
54
+ super().merge("fields" => [ ["response", "code"] ], "result_size" => 10)
53
55
  end
54
56
 
57
+ before { plugin.register }
58
+
55
59
  it "should enhance the current event with new data" do
56
60
  plugin.filter(event)
57
61
  expect(event.get("code")).to eq([404]*10)
58
62
  end
59
63
 
60
64
  end
65
+
66
+ context "incorrect auth credentials" do
67
+
68
+ let(:config) do
69
+ super().reject { |key, _| key == 'password' }
70
+ end
71
+
72
+ it "should enhance the current event with new data" do
73
+ expect { plugin.register }.to raise_error Elasticsearch::Transport::Transport::Errors::Unauthorized
74
+ end
75
+
76
+ end if ELASTIC_SECURITY_ENABLED
77
+
61
78
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-filter-elasticsearch
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.7.1
4
+ version: 3.9.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elastic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-09 00:00:00.000000000 Z
11
+ date: 2021-08-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -20,8 +20,8 @@ dependencies:
20
20
  - !ruby/object:Gem::Version
21
21
  version: '2.99'
22
22
  name: logstash-core-plugin-api
23
- prerelease: false
24
23
  type: :runtime
24
+ prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
27
  - - ">="
@@ -35,15 +35,15 @@ dependencies:
35
35
  requirements:
36
36
  - - ">="
37
37
  - !ruby/object:Gem::Version
38
- version: 5.0.3
38
+ version: 5.0.5
39
39
  name: elasticsearch
40
- prerelease: false
41
40
  type: :runtime
41
+ prerelease: false
42
42
  version_requirements: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - ">="
45
45
  - !ruby/object:Gem::Version
46
- version: 5.0.3
46
+ version: 5.0.5
47
47
  - !ruby/object:Gem::Dependency
48
48
  requirement: !ruby/object:Gem::Requirement
49
49
  requirements:
@@ -51,8 +51,8 @@ dependencies:
51
51
  - !ruby/object:Gem::Version
52
52
  version: '0.6'
53
53
  name: manticore
54
- prerelease: false
55
54
  type: :runtime
55
+ prerelease: false
56
56
  version_requirements: !ruby/object:Gem::Requirement
57
57
  requirements:
58
58
  - - "~>"
@@ -65,8 +65,8 @@ dependencies:
65
65
  - !ruby/object:Gem::Version
66
66
  version: '0'
67
67
  name: logstash-devutils
68
- prerelease: false
69
68
  type: :development
69
+ prerelease: false
70
70
  version_requirements: !ruby/object:Gem::Requirement
71
71
  requirements:
72
72
  - - ">="
@@ -89,6 +89,7 @@ files:
89
89
  - docs/index.asciidoc
90
90
  - lib/logstash/filters/elasticsearch.rb
91
91
  - lib/logstash/filters/elasticsearch/client.rb
92
+ - lib/logstash/filters/elasticsearch/patches/_elasticsearch_transport_http_manticore.rb
92
93
  - logstash-filter-elasticsearch.gemspec
93
94
  - spec/es_helper.rb
94
95
  - spec/filters/elasticsearch_spec.rb
@@ -121,8 +122,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
121
122
  - !ruby/object:Gem::Version
122
123
  version: '0'
123
124
  requirements: []
124
- rubyforge_project:
125
- rubygems_version: 2.6.13
125
+ rubygems_version: 3.0.6
126
126
  signing_key:
127
127
  specification_version: 4
128
128
  summary: Copies fields from previous log events in Elasticsearch to current events