logstash-integration-elastic_enterprise_search 2.2.1 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2f0c1374a08bc723fd7452e115e7372c8e0bb416c6e7e2e6d10769b15b571b7c
4
- data.tar.gz: 53debfd033dae11b5d5c80809c51c8a6db5997c80f5eeb3901414932c0d48e17
3
+ metadata.gz: b4473449751c6ce422faa5cdbbcec12e78677a54d40209e397c987d2e2e2d05f
4
+ data.tar.gz: d1774ea9414e64b243017fa4faac4a53c830b66098208762bb601d9e3a9ac834
5
5
  SHA512:
6
- metadata.gz: dcb96b16bafc1b85a0e4e375925b8d6ad5e6f3366044e1ea13b4318ab0c5b75b84d342fe76077ddcc30f4b0423f0e47028b474d2012e34570357a07ad9ff94e8
7
- data.tar.gz: fab4c000386cca49902de6f11aa392d95032b4fd7897826fc7515dac40012315c2356d1c624485b4689faf229b7455176845ac40cdf3c298e596ad517a545a93
6
+ metadata.gz: 8ed463da5b8975c4765a4688e684e2596a42161128a6bee3f087eff49a6404180051967e6c96f7893581bd7322ff13bdb2d8251f967b8b35496e4b85503e1f83
7
+ data.tar.gz: e0fcd1f80a3cd350508d9d78922a67e26fc31e01d0433f2fa764033fc2778925b0a8ebebd48e95802718047162630e2d65124395cbd7c7989a7182ce9482e429
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## 3.0.1
2
+ - Add deprecation log for App Search and Workplace Search. Both products are removed from Elastic Stack in version 9 [#22](https://github.com/logstash-plugins/logstash-integration-elastic_enterprise_search/pull/22)
3
+
4
+ ## 3.0.0
5
+ - Bumped Enterprise Search clients to version `>= 7.16`, `< 9` [#18](https://github.com/logstash-plugins/logstash-integration-elastic_enterprise_search/pull/18)
6
+ - Added support to SSL configurations (`ssl_certificate_authorities`, `ssl_truststore_path`, `ssl_truststore_password`, `ssl_truststore_type`, `ssl_verification_mode`, `ssl_supported_protocols` and `ssl_cipher_suites`)
7
+ - [BREAKING] Swiftype endpoints are no longer supported for both plugins App Search and Workplace Search
8
+ - The App Search deprecated options `host` and `path` were removed
9
+ - Fixed the sprintf format support for the Workplace Search `source` configuration
10
+
1
11
  ## 2.2.1
2
12
  - Fix, change implementation of connectivity check method to be compatible with version `v8.0+` of Workplace Search [#16](https://github.com/logstash-plugins/logstash-integration-elastic_enterprise_search/pull/16)
3
13
 
data/README.md CHANGED
@@ -88,6 +88,16 @@ bin/plugin install --no-verify
88
88
  ```
89
89
  - Start Logstash and proceed to test the plugin
90
90
 
91
+ ### Thoubleshooting integration test failures
92
+ Integration tests uses some certificates fixtures. These security artifacts has 1 year expiration, so time to time
93
+ they would trigger errors due to bad certificate.
94
+ To regenerate use:
95
+ ```sh
96
+ > cd spec/fixture/certificates
97
+ > ./generate.sh
98
+ ```
99
+ Re-run locally the integration tests (with Docker scripts) and if it's green again create a PR to update.
100
+
91
101
  ## Contributing
92
102
 
93
103
  All contributions are welcome: ideas, patches, documentation, bug reports, complaints, and even something you drew up on a napkin.
@@ -1,13 +1,15 @@
1
1
  # encoding: utf-8
2
- require "logstash/outputs/base"
3
- require "elastic-app-search"
4
- require "elastic-enterprise-search"
2
+ require 'logstash/outputs/base'
5
3
  require 'logstash/plugin_mixins/deprecation_logger_support'
4
+ require 'logstash/plugin_mixins/enterprise_search/ssl_configs'
5
+ require 'logstash/plugin_mixins/enterprise_search/client'
6
6
 
7
7
  class LogStash::Outputs::ElasticAppSearch < LogStash::Outputs::Base
8
+
9
+ include LogStash::PluginMixins::EnterpriseSearch::SSLConfigs
8
10
  include LogStash::PluginMixins::DeprecationLoggerSupport
9
11
 
10
- config_name "elastic_app_search"
12
+ config_name 'elastic_app_search'
11
13
 
12
14
  # The name of the search engine you created in App Search, an information
13
15
  # repository that includes the indexed document records.
@@ -21,16 +23,11 @@ class LogStash::Outputs::ElasticAppSearch < LogStash::Outputs::Base
21
23
  # or, if the field is missing from the event and cannot be resolved at all.
22
24
  config :engine, :validate => :string, :required => true
23
25
 
24
- # The hostname of the App Search API that is associated with your App Search account.
25
- # Set this when using the https://www.elastic.co/cloud/app-search-service
26
- config :host, :validate => :string
27
-
28
- # The value of the API endpoint in the form of a URL. Note: The value of the of the `path` setting will be will be appended to this URL.
29
- # Set this when using the https://www.elastic.co/downloads/app-search
30
- config :url, :validate => :string
26
+ # The value of the API endpoint in the form of a URL.
27
+ config :url, :validate => :string, :required => true, :default => Elastic::EnterpriseSearch::Utils::DEFAULT_HOST
31
28
 
32
- # The private API Key with write permissions. Visit the https://app.swiftype.com/as/credentials
33
- # in the App Search dashboard to find the key associated with your account.
29
+ # The private API Key with write permissions.
30
+ # https://www.elastic.co/guide/en/app-search/current/authentication.html#authentication-api-keys
34
31
  config :api_key, :validate => :password, :required => true
35
32
 
36
33
  # Where to move the value from the `@timestamp` field.
@@ -46,57 +43,48 @@ class LogStash::Outputs::ElasticAppSearch < LogStash::Outputs::Base
46
43
  # like `myapp-%{sequence_id}`. Reusing ids will cause documents to be rewritten.
47
44
  config :document_id, :validate => :string
48
45
 
49
- # The path that is appended to the `url` parameter when connecting to a https://www.elastic.co/downloads/app-search
50
- config :path, :validate => :string, :default => "/api/as/v1/"
51
-
52
- ENGINE_WITH_SPRINTF_REGEX = /^.*%\{.+\}.*$/
46
+ ENGINE_WITH_SPRINTF_REGEX = /^.*%\{.+\}.*$/.freeze
53
47
 
54
- public
55
48
  def register
56
- @use_old_client = false
57
- if @host.nil? && @url.nil?
58
- raise ::LogStash::ConfigurationError.new("Please specify either \"url\" (for self-managed) or \"host\" (for SaaS).")
59
- elsif @host && @url
60
- raise ::LogStash::ConfigurationError.new("Both \"url\" or \"host\" can't be set simultaneously. Please specify either \"url\" (for self-managed ot Elastic Enterprise Search) or \"host\" (for SaaS).")
61
- elsif @host && path_is_set? # because path has a default value we need extra work to if the user set it
62
- raise ::LogStash::ConfigurationError.new("The setting \"path\" is not compatible with \"host\". Use \"path\" only with \"url\".")
63
- elsif @host
64
- @deprecation_logger.deprecated("Deprecated service usage, the `host` setting will be removed when Swiftype AppSearch service is shutdown")
65
- @use_old_client = true
66
- @client = Elastic::AppSearch::Client.new(:host_identifier => @host, :api_key => @api_key.value)
67
- elsif @url
68
- if path_is_set?
69
- @deprecation_logger.deprecated("Deprecated service usage, the `path` setting will be removed when Swiftype AppSearch service is shutdown")
70
- @use_old_client = true
71
- @client = Elastic::AppSearch::Client.new(:api_endpoint => @url + @path, :api_key => @api_key.value)
72
- else
73
- @client = Elastic::EnterpriseSearch::AppSearch::Client.new(:host => @url, :http_auth => @api_key.value, :external_url => @url)
74
- end
75
- end
76
- check_connection! unless @engine =~ ENGINE_WITH_SPRINTF_REGEX
49
+ log_message = "The App Search product is deprecated and not supported from version 9 of the Elastic Stack. " +
50
+ "The Elastic App Search output plugin is deprecated and will only receive security updates and critical bug fixes. " +
51
+ "Please migrate to the Elastic Connector for continued support. " +
52
+ "For more details, please visit https://www.elastic.co/guide/en/search-ui/current/tutorials-elasticsearch.html"
53
+ deprecation_logger.deprecated log_message
54
+
55
+ @retry_disabled = false
56
+ @client = LogStash::PluginMixins::EnterpriseSearch::AppSearch::Client.new(client_options, params: params)
57
+ check_connection!
77
58
  rescue => e
78
59
  if e.message =~ /401/
79
- raise ::LogStash::ConfigurationError.new("Failed to connect to App Search. Error: 401. Please check your credentials")
60
+ raise ::LogStash::ConfigurationError, "Failed to connect to App Search. Please check your credentials. Error: #{e.message}"
80
61
  elsif e.message =~ /404/
81
- raise ::LogStash::ConfigurationError.new("Failed to connect to App Search. Error: 404. Please check if host '#{@host}' is correct and you've created an engine with name '#{@engine}'")
62
+ raise ::LogStash::ConfigurationError, "Failed to connect to App Search. Please check if url '#{@url}' is correct and you've created an engine with name '#{@engine}'. Error: #{e.message}"
82
63
  else
83
- raise ::LogStash::ConfigurationError.new("Failed to connect to App Search. #{e.message}")
64
+ raise ::LogStash::ConfigurationError, "Failed to connect to App Search. Error: #{e.message}"
84
65
  end
85
66
  end
86
67
 
87
- public
88
68
  def multi_receive(events)
89
69
  # because App Search has a limit of 100 documents per bulk
90
70
  events.each_slice(100) do |events|
91
71
  batch = format_batch(events)
92
72
  if @logger.trace?
93
- @logger.trace("Sending bulk to App Search", :size => batch.size, :data => batch.inspect)
73
+ @logger.trace('Sending bulk to App Search', :size => batch.size, :data => batch.inspect)
94
74
  end
95
75
  index(batch)
96
76
  end
97
77
  end
98
78
 
99
79
  private
80
+
81
+ def client_options
82
+ options = { :host => @url, :http_auth => @api_key.value }
83
+ options[:logger] = @logger if @logger.debug?
84
+ options[:tracer] = @logger if @logger.trace?
85
+ options
86
+ end
87
+
100
88
  def format_batch(events)
101
89
  docs_for_engine = {}
102
90
  events.each do |event|
@@ -104,18 +92,18 @@ class LogStash::Outputs::ElasticAppSearch < LogStash::Outputs::Base
104
92
  # we need to remove default fields that start with "@"
105
93
  # since Elastic App Search doesn't accept them
106
94
  if @timestamp_destination
107
- doc[@timestamp_destination] = doc.delete("@timestamp")
95
+ doc[@timestamp_destination] = doc.delete('@timestamp')
108
96
  else # delete it
109
- doc.delete("@timestamp")
97
+ doc.delete('@timestamp')
110
98
  end
111
99
  if @document_id
112
- doc["id"] = event.sprintf(@document_id)
100
+ doc['id'] = event.sprintf(@document_id)
113
101
  end
114
- doc.delete("@version")
102
+ doc.delete('@version')
115
103
  resolved_engine = event.sprintf(@engine)
116
104
  unless docs_for_engine[resolved_engine]
117
105
  if @logger.debug?
118
- @logger.debug("Creating new engine segment in batch to send", :resolved_engine => resolved_engine)
106
+ @logger.debug('Creating new engine segment in batch to send', :resolved_engine => resolved_engine)
119
107
  end
120
108
  docs_for_engine[resolved_engine] = []
121
109
  end
@@ -124,21 +112,21 @@ class LogStash::Outputs::ElasticAppSearch < LogStash::Outputs::Base
124
112
  docs_for_engine
125
113
  end
126
114
 
127
- def index(batch)
128
- batch.each do |resolved_engine, documents|
115
+ def index(docs_partitioned_by_engine)
116
+ docs_partitioned_by_engine.each do |resolved_engine, documents|
129
117
  begin
130
118
  if resolved_engine =~ ENGINE_WITH_SPRINTF_REGEX || resolved_engine =~ /^\s*$/
131
119
  raise "Cannot resolve engine field name #{@engine} from event"
132
120
  end
133
- if connected_to_swiftype?
134
- response = @client.index_documents(resolved_engine, documents)
135
- else
136
- response = @client.index_documents(resolved_engine, {:documents => documents})
137
- end
121
+
122
+ response = @client.index_documents(resolved_engine, { :documents => documents })
138
123
  report(documents, response)
139
124
  rescue => e
140
- @logger.error("Failed to execute index operation. Retrying..", :exception => e.class, :reason => e.message,
141
- :resolved_engine => resolved_engine, :backtrace => e.backtrace)
125
+ @logger.error('Failed to execute index operation.', :exception => e.class, :reason => e.message,
126
+ :resolved_engine => resolved_engine, :backtrace => e.backtrace, :retry => !@retry_disabled)
127
+
128
+ raise e if @retry_disabled
129
+
142
130
  sleep(1)
143
131
  retry
144
132
  end
@@ -147,33 +135,17 @@ class LogStash::Outputs::ElasticAppSearch < LogStash::Outputs::Base
147
135
 
148
136
  def report(documents, response)
149
137
  documents.each_with_index do |document, i|
150
- if connected_to_swiftype?
151
- errors = response[i]["errors"]
152
- else
153
- errors = response.body[i]["errors"]
154
- end
138
+ errors = response.body[i]['errors']
155
139
  if errors.empty?
156
- @logger.trace? && @logger.trace("Document was indexed with no errors", :document => document)
140
+ @logger.trace? && @logger.trace('Document was indexed with no errors', :document => document)
157
141
  else
158
- @logger.warn("Document failed to index. Dropping..", :document => document, :errors => errors.to_a)
142
+ @logger.warn('Document failed to index. Dropping..', :document => document, :errors => errors.to_a)
159
143
  end
160
144
  end
161
145
  end
162
146
 
163
147
  def check_connection!
164
- if connected_to_swiftype?
165
- @client.get_engine(@engine)
166
- else
167
- res = @client.list_engines({:page_size => 1})
168
- raise "Received HTTP error code #{res.status}" unless res.status == 200
169
- end
170
- end
171
-
172
- def path_is_set?
173
- original_params.key?("path")
174
- end
175
-
176
- def connected_to_swiftype?
177
- @use_old_client
148
+ res = @client.list_engines({ 'page[size]': 1 })
149
+ raise "Received HTTP error code #{res.status}" unless res.status == 200
178
150
  end
179
151
  end
@@ -1,12 +1,18 @@
1
1
  # encoding: utf-8
2
- require "logstash/outputs/base"
3
- require "elastic-workplace-search"
2
+ require 'logstash/outputs/base'
3
+ require 'logstash/plugin_mixins/deprecation_logger_support'
4
+ require 'logstash/plugin_mixins/enterprise_search/client'
5
+ require 'logstash/plugin_mixins/enterprise_search/ssl_configs'
4
6
 
5
7
  class LogStash::Outputs::ElasticWorkplaceSearch < LogStash::Outputs::Base
6
- config_name "elastic_workplace_search"
8
+
9
+ include LogStash::PluginMixins::EnterpriseSearch::SSLConfigs
10
+ include LogStash::PluginMixins::DeprecationLoggerSupport
11
+
12
+ config_name 'elastic_workplace_search'
7
13
 
8
14
  # The value of the API endpoint in the form of a URL.
9
- config :url, :validate => :string, :required => true
15
+ config :url, :validate => :string, :required => true, :default => Elastic::EnterpriseSearch::Utils::DEFAULT_HOST
10
16
 
11
17
  # The ID of the source you created in Workplace Search.
12
18
  # The `source` field supports
@@ -36,72 +42,104 @@ class LogStash::Outputs::ElasticWorkplaceSearch < LogStash::Outputs::Base
36
42
  # like `myapp-%{sequence_id}`. Reusing ids will cause documents to be rewritten.
37
43
  config :document_id, :validate => :string
38
44
 
39
- public
45
+ SOURCE_WITH_SPRINTF_REGEX = /^.*%\{.+\}.*$/.freeze
46
+
40
47
  def register
41
- Elastic::WorkplaceSearch.endpoint = "#{@url.chomp('/')}/api/ws/v1/"
42
- @client = Elastic::WorkplaceSearch::Client.new(:access_token => @access_token.value)
43
- check_connection!
44
- rescue Elastic::WorkplaceSearch::InvalidCredentials
45
- raise ::LogStash::ConfigurationError.new("Failed to connect to Workplace Search. Error: 401. Please check your credentials")
46
- rescue Elastic::WorkplaceSearch::NonExistentRecord
47
- raise ::LogStash::ConfigurationError.new("Failed to connect to Workplace Search. Error: 404. Please check if url '#{@url}' is correct and you've created a source with ID '#{@source}'")
48
- rescue => e
49
- raise ::LogStash::ConfigurationError.new("Failed to connect to Workplace Search. #{e.message}")
48
+ log_message = "The Workplace Search product is deprecated and not supported from version 9 of the Elastic Stack. " +
49
+ "The Elastic Workplace Search output plugin is deprecated and will only receive security updates and critical bug fixes. " +
50
+ "Please migrate to the Elastic Connector for continued support. " +
51
+ "For more details, please visit https://www.elastic.co/guide/en/search-ui/current/tutorials-elasticsearch.html"
52
+ deprecation_logger.deprecated log_message
53
+
54
+ @retry_disabled = false
55
+ @client = LogStash::PluginMixins::EnterpriseSearch::WorkplaceSearch::Client.new(client_options, params: params)
56
+ begin
57
+ check_connection!
58
+ rescue => e
59
+ raise ::LogStash::ConfigurationError, "Failed to connect to Workplace Search. Error: #{e.message}"
60
+ end
50
61
  end
51
62
 
52
- public
53
63
  def multi_receive(events)
54
64
  # because Workplace Search has a limit of 100 documents per bulk
55
65
  events.each_slice(100) do |events|
56
66
  batch = format_batch(events)
57
- if @logger.trace?
58
- @logger.trace("Sending bulk to Workplace Search", :size => batch.size, :data => batch.inspect)
59
- end
67
+ @logger.trace('Sending bulk to Workplace Search', :size => batch.size, :data => batch.inspect) if @logger.trace?
60
68
  index(batch)
61
69
  end
62
70
  end
63
71
 
64
72
  private
73
+
74
+ def client_options
75
+ options = { :host => @url, :http_auth => @access_token.value }
76
+ options[:logger] = @logger if @logger.debug?
77
+ options[:tracer] = @logger if @logger.trace?
78
+ options
79
+ end
80
+
65
81
  def format_batch(events)
82
+ docs_per_source = {}
66
83
  events.map do |event|
67
84
  doc = event.to_hash
68
85
  # we need to remove default fields that start with "@"
69
86
  # since Elastic Workplace Search doesn't accept them
70
87
  if @timestamp_destination
71
- doc[@timestamp_destination] = doc.delete("@timestamp")
88
+ doc[@timestamp_destination] = doc.delete('@timestamp')
72
89
  else # delete it
73
- doc.delete("@timestamp")
90
+ doc.delete('@timestamp')
74
91
  end
75
- if @document_id
76
- doc["id"] = event.sprintf(@document_id)
92
+
93
+ doc['id'] = event.sprintf(@document_id) if @document_id
94
+ doc.delete('@version')
95
+
96
+ resolved_source = event.sprintf(@source)
97
+ unless docs_per_source[resolved_source]
98
+ @logger.debug('Creating new source segment in batch to send', resolved_source: resolved_source) if @logger.debug?
99
+ docs_per_source[resolved_source] = []
100
+ end
101
+ docs_per_source[resolved_source] << doc
102
+ end
103
+
104
+ docs_per_source
105
+ end
106
+
107
+ def index(docs_partitioned_by_source)
108
+ docs_partitioned_by_source.each do |resolved_source, documents|
109
+ begin
110
+ raise "Cannot resolve source field name #{@source} from event" unless resolved?(resolved_source)
111
+
112
+ response = @client.index_documents(resolved_source, { :documents => documents })
113
+ report(documents, response)
114
+ rescue => e
115
+ @logger.error('Failed to execute index operation.', :exception => e.class, :reason => e.message,
116
+ :resolved_source => resolved_source, :backtrace => e.backtrace, :retry => !@retry_disabled)
117
+
118
+ raise e if @retry_disabled
119
+
120
+ sleep(1)
121
+ retry
77
122
  end
78
- doc.delete("@version")
79
- doc
80
123
  end
81
124
  end
82
125
 
83
- def index(documents)
84
- response = @client.index_documents(@source, documents)
85
- report(documents, response)
86
- rescue => e
87
- @logger.error("Failed to execute index operation. Retrying..", :exception => e.class, :reason => e.message)
88
- sleep(1)
89
- retry
126
+ def resolved?(source)
127
+ !(source =~ SOURCE_WITH_SPRINTF_REGEX) && !(source =~ /^\s*$/)
90
128
  end
91
129
 
92
130
  def report(documents, response)
93
131
  documents.each_with_index do |document, i|
94
- errors = response["results"][i]["errors"]
95
- if errors.empty?
96
- @logger.trace? && @logger.trace("Document was indexed with no errors", :document => document)
132
+ errors = response.body&.dig('results', i, 'errors')
133
+ if errors&.empty?
134
+ @logger.trace? && @logger.trace('Document was indexed with no errors', :document => document)
97
135
  else
98
- @logger.warn("Document failed to index. Dropping..", :document => document, :errors => errors.to_a)
136
+ @logger.warn('Document failed to index. Dropping..', :document => document, :errors => errors.to_a)
99
137
  end
100
138
  end
101
139
  end
102
140
 
103
141
  def check_connection!
104
- # This is the preferred way to check compatibility across different versions of Workplace Search service.
105
- @client.index_documents(@source, {:documents => []})
142
+ res = @client.list_content_sources({ 'page[size]': 1 })
143
+ raise "Received HTTP error code #{res.status}" unless res.status == 200
106
144
  end
107
145
  end
@@ -0,0 +1,35 @@
1
+ require 'elastic-enterprise-search'
2
+ require 'logstash/plugin_mixins/enterprise_search/manticore_transport'
3
+
4
+ module LogStash::PluginMixins::EnterpriseSearch
5
+
6
+ module AppSearch
7
+ # App Search client for Enterprise Search.
8
+ # This client extends Elastic::EnterpriseSearch::AppSearch::Client but overrides #transport to use Manticore.
9
+ class Client < Elastic::EnterpriseSearch::AppSearch::Client
10
+ attr_reader :params
11
+
12
+ include LogStash::PluginMixins::EnterpriseSearch::ManticoreTransport
13
+
14
+ def initialize(options, params: {})
15
+ @params = params
16
+ super options
17
+ end
18
+ end
19
+ end
20
+
21
+ module WorkplaceSearch
22
+ # Workplace Search client for Enterprise Search.
23
+ # This client extends Elastic::EnterpriseSearch::WorkplaceSearch::Client but overrides #transport to use Manticore.
24
+ class Client < Elastic::EnterpriseSearch::WorkplaceSearch::Client
25
+ attr_reader :params
26
+
27
+ include LogStash::PluginMixins::EnterpriseSearch::ManticoreTransport
28
+
29
+ def initialize(options, params: {})
30
+ @params = params
31
+ super options
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,84 @@
1
+ module LogStash::PluginMixins::EnterpriseSearch
2
+ # This module is meant to be included in EnterpriseSearch::Clients subclasses and overrides
3
+ # the default #transport method, changing the HTTP client to Manticore.
4
+ # It also provides common Manticore configurations such as :ssl.
5
+ module ManticoreTransport
6
+ def self.included(base)
7
+ if eps_version_7?
8
+ require 'elasticsearch/transport/transport/http/manticore'
9
+ else
10
+ require 'elastic/transport/transport/http/manticore'
11
+ end
12
+
13
+ raise ArgumentError, "`#{base}` must inherit Elastic::EnterpriseSearch::Client" unless base < Elastic::EnterpriseSearch::Client
14
+ raise ArgumentError, "`#{base}` must respond to :params" unless base.instance_methods.include? :params
15
+ end
16
+
17
+ # overrides Elastic::EnterpriseSearch::Client#transport
18
+ def transport
19
+ @options[:transport] || transport_klass.new(
20
+ host: host,
21
+ log: log,
22
+ logger: logger,
23
+ request_timeout: overall_timeout,
24
+ adapter: @options[:adapter],
25
+ transport_options: {
26
+ request: { open_timeout: open_timeout }
27
+ },
28
+ transport_class: manticore_transport_klass,
29
+ ssl: build_ssl_config,
30
+ enable_meta_header: @options[:enable_meta_header] || true,
31
+ trace: !@options[:tracer].nil?,
32
+ tracer: @options[:tracer]
33
+ )
34
+ end
35
+
36
+ def self.eps_version_7?
37
+ Elastic::EnterpriseSearch::VERSION.start_with?('7')
38
+ end
39
+
40
+ private
41
+
42
+ def manticore_transport_klass
43
+ ManticoreTransport.eps_version_7? ? Elasticsearch::Transport::Transport::HTTP::Manticore : Elastic::Transport::Transport::HTTP::Manticore
44
+ end
45
+
46
+ def transport_klass
47
+ case Elasticsearch::Transport::VERSION
48
+ when /^7\.1[123]/
49
+ Elasticsearch::Client
50
+ else
51
+ Elasticsearch::Transport::Client
52
+ end
53
+ end
54
+
55
+ def build_transport_options
56
+ { request: { open_timeout: open_timeout } }
57
+ end
58
+
59
+ def build_ssl_config
60
+ ssl_certificate_authorities, ssl_truststore_path = params.values_at('ssl_certificate_authorities', 'ssl_truststore_path')
61
+ if ssl_certificate_authorities && ssl_truststore_path
62
+ raise ::LogStash::ConfigurationError, 'Use either "ssl_certificate_authorities" or "ssl_truststore_path" when configuring the CA certificate'
63
+ end
64
+
65
+ ssl_options = {}
66
+ if ssl_certificate_authorities&.any?
67
+ raise ::LogStash::ConfigurationError, 'Multiple values on "ssl_certificate_authorities" are not supported by this plugin' if ssl_certificate_authorities.size > 1
68
+
69
+ ssl_options[:ca_file] = ssl_certificate_authorities.first
70
+ end
71
+
72
+ if ssl_truststore_path
73
+ ssl_options[:truststore] = ssl_truststore_path
74
+ ssl_options[:truststore_type] = params['ssl_truststore_type'] if params.include?('ssl_truststore_type')
75
+ ssl_options[:truststore_password] = params['ssl_truststore_password'].value if params.include?('ssl_truststore_password')
76
+ end
77
+
78
+ ssl_options[:verify] = params['ssl_verification_mode'] == 'full' ? :strict : :disable
79
+ ssl_options[:cipher_suites] = params['ssl_cipher_suites'] if params.include?('ssl_cipher_suites')
80
+ ssl_options[:protocols] = params['ssl_supported_protocols'] if params['ssl_supported_protocols']&.any?
81
+ ssl_options
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,32 @@
1
+ module LogStash::PluginMixins::EnterpriseSearch
2
+ # This module defines common SSL options that can be reused by the app and workplace search plugins.
3
+ module SSLConfigs
4
+ def self.included(base)
5
+ # SSL Certificate Authority files in PEM encoded format, must also include any chain certificates as necessary
6
+ base.config :ssl_certificate_authorities, :validate => :path, :list => true
7
+
8
+ # The JKS truststore to validate the server's certificate.
9
+ # Use either `:ssl_truststore_path` or `:ssl_certificate_authorities`
10
+ base.config :ssl_truststore_path, :validate => :path
11
+
12
+ # Set the truststore password
13
+ base.config :ssl_truststore_password, :validate => :password
14
+
15
+ # The format of the truststore file. It must be either jks or pkcs12
16
+ base.config :ssl_truststore_type, :validate => %w[pkcs12 jks]
17
+
18
+ # Options to verify the server's certificate.
19
+ # "full": validates that the provided certificate has an issue date that’s within the not_before and not_after dates;
20
+ # chains to a trusted Certificate Authority (CA); has a hostname or IP address that matches the names within the certificate.
21
+ # "none": performs no certificate validation. Disabling this severely compromises security (https://www.cs.utexas.edu/~shmat/shmat_ccs12.pdf)
22
+ base.config :ssl_verification_mode, :validate => %w[full none], :default => 'full'
23
+
24
+ # Supported protocols with versions.
25
+ base.config :ssl_supported_protocols, :validate => %w[TLSv1.1 TLSv1.2 TLSv1.3], :default => [], :list => true
26
+
27
+ # The list of cipher suites to use, listed by priorities.
28
+ # Supported cipher suites vary depending on which version of Java is used.
29
+ base.config :ssl_cipher_suites, :validate => :string, :list => true
30
+ end
31
+ end
32
+ end
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'logstash-integration-elastic_enterprise_search'
3
- s.version = '2.2.1'
3
+ s.version = '3.0.1'
4
4
  s.licenses = ['Apache-2.0']
5
5
  s.summary = "Integration with Elastic Enterprise Search - output plugins"
6
6
  s.description = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline "+
@@ -24,11 +24,10 @@ Gem::Specification.new do |s|
24
24
  }
25
25
 
26
26
  # Gem dependencies
27
+ s.add_runtime_dependency "manticore", '~> 0.8'
27
28
  s.add_runtime_dependency "logstash-core-plugin-api", "~> 2.0"
28
29
  s.add_runtime_dependency "logstash-codec-plain"
29
- s.add_runtime_dependency "elastic-app-search", '~>7.8.0'
30
- s.add_runtime_dependency "elastic-enterprise-search", '~>7.16.0'
31
- s.add_runtime_dependency "elastic-workplace-search", '~>0.4.1'
30
+ s.add_runtime_dependency 'elastic-enterprise-search', '>= 7.16', '< 9'
32
31
  s.add_runtime_dependency "logstash-mixin-deprecation_logger_support", '~>1.0'
33
32
  s.add_development_dependency "logstash-devutils"
34
33
  end
@@ -0,0 +1,10 @@
1
+ # Warning: do not use the certificates produced by this tool in production. This is for testing purposes only
2
+
3
+ openssl genrsa 4096 | openssl pkcs8 -topk8 -nocrypt -out root_ca.key
4
+ openssl req -sha256 -x509 -newkey rsa:4096 -nodes -key root_ca.key -sha256 -days 365 -out root_ca.crt -subj "/C=ES/ST=The Internet/L=The Internet/O=Logstash CA/OU=Logstash/CN=enterprise_search"
5
+ openssl req -sha256 -x509 -newkey rsa:4096 -nodes -key root_ca.key -sha256 -days 365 -out root_untrusted_ca.crt -subj "/C=ES/ST=The Darknet/L=The Darknet/O=Logstash CA/OU=Logstash/CN=127.0.0.1"
6
+ openssl pkcs12 -export -in root_ca.crt -inkey root_ca.key -out root_keystore.p12 -password pass:changeme -name ent-search
7
+ keytool -importkeystore -srckeystore root_keystore.p12 -destkeystore root_keystore.jks -srcstoretype PKCS12 -deststoretype jks -srcstorepass changeme -deststorepass changeme -srcalias ent-search -destalias ent-search -srckeypass changeme -destkeypass changeme
8
+
9
+ rm -rf root_keystore.p12
10
+ rm -rf *.csr
@@ -0,0 +1,34 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ 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
33
+ qk9INcfDhzlxwVIqYAoLXTV/ru3mTZI=
34
+ -----END CERTIFICATE-----