Skip to content

Commit 3764083

Browse files
committed
Refactor api_key handling into resolve_api_key and validate the format
Move the api_key logic into a single resolve_api_key method: an already base64-encoded key or an Elastic Cloud API key is used as-is, a raw id:api_key pair is base64-encoded, and any other value is rejected at startup instead of failing later with a 401. Reword the api_key doc line.
1 parent 2f4317a commit 3764083

4 files changed

Lines changed: 39 additions & 11 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
## 12.1.5
2-
- Support Elastic Cloud API keys (`essu_` prefixed) in the `api_key` option by passing them verbatim instead of base64 re-encoding them [#1274](https://github.com/logstash-plugins/logstash-output-elasticsearch/pull/1274)
2+
- Support Elastic Cloud API keys in the `api_key` option, which now accepts an `id:api_key` pair, its base64-encoded form, or an `essu_` Cloud API key, and rejects an unrecognized format at startup [#1274](https://github.com/logstash-plugins/logstash-output-elasticsearch/pull/1274)
33

44
## 12.1.4
55
- [Doc] Add note for index option [#1269](https://github.com/logstash-plugins/logstash-output-elasticsearch/pull/1269)

lib/logstash/outputs/elasticsearch/http_client_builder.rb

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -190,10 +190,7 @@ def self.setup_api_key(logger, params)
190190

191191
return {} unless (api_key&.value)
192192

193-
# Elastic Cloud API keys (`essu_` prefixed) and already base64-encoded
194-
# keys are passed verbatim; a raw `id:api_key` pair is encoded here.
195-
value = (cloud_api_key?(api_key.value) || is_base64?(api_key.value)) ? api_key.value : Base64.strict_encode64(api_key.value)
196-
193+
value = resolve_api_key(api_key.value)
197194
{ "Authorization" => "ApiKey #{value}" }
198195
end
199196

@@ -214,12 +211,26 @@ def query_param_separator(url)
214211
url.match?(/\?[^\s#]+/) ? '&' : '?'
215212
end
216213

217-
# Elastic Cloud API keys scoped for Elasticsearch (such as the unified
218-
# Serverless keys) are opaque `essu_` prefixed tokens that Elasticsearch
219-
# accepts verbatim in the `Authorization: ApiKey` header, with no base64
220-
# encoding.
214+
# Resolves the `api_key` value into the credential used in the
215+
# `Authorization: ApiKey` header. An already base64-encoded key and an
216+
# Elastic Cloud API key are used as-is; a raw `id:api_key` pair is
217+
# base64-encoded. An unrecognized value is rejected so a malformed key
218+
# surfaces at startup rather than as a later authentication failure.
219+
def resolve_api_key(key_value)
220+
if is_base64?(key_value) || cloud_api_key?(key_value)
221+
key_value
222+
elsif key_value.match?(/\A[^:]+:[^:]+\z/)
223+
Base64.strict_encode64(key_value)
224+
else
225+
raise LogStash::ConfigurationError, "Invalid api_key format. Expected a base64-encoded key, an 'id:api_key' pair, or a Cloud API key (essu_ prefix)."
226+
end
227+
end
228+
229+
# Elastic Cloud API keys (such as the unified Serverless keys) are opaque
230+
# tokens prefixed with `essu_` that Elasticsearch accepts verbatim in the
231+
# `Authorization: ApiKey` header, with no base64 encoding.
221232
def cloud_api_key?(string)
222-
string.start_with?("essu_")
233+
string.match?(/\Aessu_.+/)
223234
end
224235

225236
def is_base64?(string)

lib/logstash/plugin_mixins/elasticsearch/api_configs.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ module APIConfigs
1616
:password => { :validate => :password },
1717

1818
# Authenticate using Elasticsearch API key.
19-
# Either the `id:api_key` pair (as returned by https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-create-api-key.html[Create API key]),
19+
# Format is either the `id:api_key` pair (as returned by https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-create-api-key.html[Create API key]),
2020
# its base64-encoded form, or an https://www.elastic.co/docs/deploy-manage/api-keys/elastic-cloud-api-keys[Elastic Cloud API key] (prefixed with `essu_`) can be used.
2121
:api_key => { :validate => :password },
2222

spec/unit/http_client_builder_spec.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@
6464
end
6565

6666
context "when api-key is an Elastic Cloud API key (essu_ prefix)" do
67+
# The suffix is intentionally not canonical base64: a Cloud key is opaque
68+
# and must be forwarded verbatim regardless of its payload encoding.
6769
let(:api_key) { "essu_VFZGblZreFhTekJ4ZDB4M2NHUnZRMEU2YzNWd1pYSnpaV055WlhRPQ==AAAAAAAA" }
6870
let(:api_key_secured) do
6971
secured = double("api_key")
@@ -79,6 +81,21 @@
7981
expect(api_key_header["Authorization"]).to eql(expected)
8082
end
8183
end
84+
85+
context "when api-key has an unrecognized format" do
86+
let(:api_key) { "not-a-valid-key" }
87+
let(:api_key_secured) do
88+
secured = double("api_key")
89+
allow(secured).to receive(:value).and_return(api_key)
90+
secured
91+
end
92+
let(:options) { { "api_key" => api_key_secured } }
93+
let(:logger) { double("logger") }
94+
95+
it "raises a configuration error" do
96+
expect { klass.setup_api_key(logger, options) }.to raise_error(LogStash::ConfigurationError, /Invalid api_key format/)
97+
end
98+
end
8299
end
83100

84101
describe "customizing action paths" do

0 commit comments

Comments
 (0)