mirror of
https://github.com/elastic/logstash.git
synced 2025-04-24 14:47:19 -04:00
add support for api_key authentication in xpack management and monitoring. (#11953)
7.x backport of #11864
This commit is contained in:
parent
fa75ac2780
commit
5e62c96c19
13 changed files with 437 additions and 136 deletions
|
@ -99,3 +99,10 @@ and `xpack.management.elasticsearch.password`. If `cloud_auth` is configured,
|
|||
those settings should not be used.
|
||||
The credentials you specify here should be for a user with the `logstash_admin` role, which
|
||||
provides access to `.logstash-*` indices for managing configurations.
|
||||
|
||||
`xpack.management.elasticsearch.api_key`::
|
||||
|
||||
Authenticate using an Elasticsearch API key. Note that this option also requires using SSL.
|
||||
|
||||
The API key Format is `id:api_key` where `id` and `api_key` are as returned by the Elasticsearch
|
||||
https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-create-api-key.html[Create API key API].
|
||||
|
|
|
@ -104,3 +104,10 @@ If you're using {es} in {ecloud}, you can set your auth credentials here.
|
|||
This setting is an alternative to both `xpack.monitoring.elasticsearch.username`
|
||||
and `xpack.monitoring.elasticsearch.password`. If `cloud_auth` is configured,
|
||||
those settings should not be used.
|
||||
|
||||
`xpack.monitoring.elasticsearch.api_key`::
|
||||
|
||||
Authenticate using an Elasticsearch API key. Note that this option also requires using SSL.
|
||||
|
||||
The API key Format is `id:api_key` where `id` and `api_key` are as returned by the Elasticsearch
|
||||
https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-create-api-key.html[Create API key API].
|
||||
|
|
|
@ -35,22 +35,9 @@ module LogStash
|
|||
|
||||
def initialize(settings)
|
||||
super(settings)
|
||||
if @settings.get("xpack.management.enabled")
|
||||
if @settings.get_setting("xpack.management.elasticsearch.cloud_id").set?
|
||||
if !@settings.get_setting("xpack.management.elasticsearch.cloud_auth").set?
|
||||
raise ArgumentError.new("You must set credentials using \"xpack.management.elasticsearch.cloud_auth\", " +
|
||||
"when using \"xpack.management.elasticsearch.cloud_id\" in logstash.yml")
|
||||
end
|
||||
else
|
||||
if !@settings.get_setting("xpack.management.elasticsearch.password").set?
|
||||
raise ArgumentError.new("You must set the password using \"xpack.management.elasticsearch.password\" in logstash.yml")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@es_options = es_options_from_settings('management', settings)
|
||||
|
||||
if enabled?
|
||||
@es_options = es_options_from_settings('management', settings)
|
||||
setup_license_checker(FEATURE_INTERNAL)
|
||||
license_check(true)
|
||||
end
|
||||
|
|
|
@ -29,6 +29,7 @@ module LogStash
|
|||
settings.register(LogStash::Setting::ArrayCoercible.new("xpack.management.elasticsearch.hosts", String, [ "https://localhost:9200" ] ))
|
||||
settings.register(LogStash::Setting::NullableString.new("xpack.management.elasticsearch.cloud_id"))
|
||||
settings.register(LogStash::Setting::NullableString.new("xpack.management.elasticsearch.cloud_auth"))
|
||||
settings.register(LogStash::Setting::NullableString.new("xpack.management.elasticsearch.api_key"))
|
||||
settings.register(LogStash::Setting::NullableString.new("xpack.management.elasticsearch.proxy"))
|
||||
settings.register(LogStash::Setting::NullableString.new("xpack.management.elasticsearch.ssl.certificate_authority"))
|
||||
settings.register(LogStash::Setting::NullableString.new("xpack.management.elasticsearch.ssl.truststore.path"))
|
||||
|
|
|
@ -6,17 +6,34 @@ module LogStash module Helpers
|
|||
module ElasticsearchOptions
|
||||
extend self
|
||||
|
||||
ES_SETTINGS =%w(
|
||||
ssl.certificate_authority
|
||||
ssl.truststore.path
|
||||
ssl.keystore.path
|
||||
hosts
|
||||
username
|
||||
password
|
||||
cloud_id
|
||||
cloud_auth
|
||||
proxy
|
||||
)
|
||||
ES_SETTINGS = %w(
|
||||
ssl.certificate_authority
|
||||
ssl.truststore.path
|
||||
ssl.keystore.path
|
||||
hosts
|
||||
username
|
||||
password
|
||||
cloud_id
|
||||
cloud_auth
|
||||
api_key
|
||||
proxy
|
||||
)
|
||||
|
||||
# xpack setting to ES output setting
|
||||
SETTINGS_MAPPINGS = {
|
||||
"cloud_id" => "cloud_id",
|
||||
"cloud_auth" => "cloud_auth",
|
||||
"username" => "user",
|
||||
"password" => "password",
|
||||
"api_key" => "api_key",
|
||||
"proxy" => "proxy",
|
||||
"sniffing" => "sniffing",
|
||||
"ssl.certificate_authority" => "cacert",
|
||||
"ssl.truststore.path" => "truststore",
|
||||
"ssl.truststore.password" => "truststore_password",
|
||||
"ssl.keystore.path" => "keystore",
|
||||
"ssl.keystore.password" => "keystore_password",
|
||||
}
|
||||
|
||||
# Retrieve elasticsearch options from either specific settings, or modules if the setting is not there and the
|
||||
# feature supports falling back to modules if the feature is not specified in logstash.yml
|
||||
|
@ -27,53 +44,53 @@ module LogStash module Helpers
|
|||
# Populate the Elasticsearch options from LogStashSettings file, based on the feature that is being used.
|
||||
# @return Hash
|
||||
def es_options_from_settings(feature, settings)
|
||||
prefix = if feature == "monitoring" &&
|
||||
LogStash::MonitoringExtension.use_direct_shipping?(settings)
|
||||
""
|
||||
else
|
||||
"xpack."
|
||||
end
|
||||
prefix = (feature == "monitoring" && LogStash::MonitoringExtension.use_direct_shipping?(settings)) ? "" : "xpack."
|
||||
opts = {}
|
||||
|
||||
if cloud_id = settings.get("#{prefix}#{feature}.elasticsearch.cloud_id")
|
||||
opts['cloud_id'] = cloud_id
|
||||
check_cloud_id_configuration!(feature, settings, prefix)
|
||||
else
|
||||
validate_authentication!(feature, settings, prefix)
|
||||
|
||||
# transpose all directly mappable settings
|
||||
SETTINGS_MAPPINGS.each do |xpack_setting, es_setting|
|
||||
v = settings.get("#{prefix}#{feature}.elasticsearch.#{xpack_setting}")
|
||||
opts[es_setting] = v unless v.nil?
|
||||
end
|
||||
|
||||
# process remaining settings
|
||||
|
||||
unless settings.get("#{prefix}#{feature}.elasticsearch.cloud_id")
|
||||
opts['hosts'] = settings.get("#{prefix}#{feature}.elasticsearch.hosts")
|
||||
end
|
||||
if cloud_auth = settings.get("#{prefix}#{feature}.elasticsearch.cloud_auth")
|
||||
opts['cloud_auth'] = cloud_auth
|
||||
check_cloud_auth_configuration!(feature, settings, prefix)
|
||||
else
|
||||
opts['user'] = settings.get("#{prefix}#{feature}.elasticsearch.username")
|
||||
opts['password'] = settings.get("#{prefix}#{feature}.elasticsearch.password")
|
||||
end
|
||||
if proxysetting = settings.get("#{prefix}#{feature}.elasticsearch.proxy")
|
||||
opts['proxy'] = proxysetting
|
||||
end
|
||||
|
||||
opts['sniffing'] = settings.get("#{prefix}#{feature}.elasticsearch.sniffing")
|
||||
opts['ssl_certificate_verification'] = settings.get("#{prefix}#{feature}.elasticsearch.ssl.verification_mode") == 'certificate'
|
||||
|
||||
if cacert = settings.get("#{prefix}#{feature}.elasticsearch.ssl.certificate_authority")
|
||||
opts['cacert'] = cacert
|
||||
# if all hosts are using https or any of the ssl related settings are set
|
||||
if ssl?(feature, settings, prefix)
|
||||
opts['ssl'] = true
|
||||
end
|
||||
|
||||
if truststore = settings.get("#{prefix}#{feature}.elasticsearch.ssl.truststore.path")
|
||||
opts['truststore'] = truststore
|
||||
opts['truststore_password'] = settings.get("#{prefix}#{feature}.elasticsearch.ssl.truststore.password")
|
||||
opts['ssl'] = true
|
||||
# the username setting has a default value and should not be included when using another authentication
|
||||
# it is safe to silently remove here since all authentication verifications have been validated at this point.
|
||||
if settings.set?("#{prefix}#{feature}.elasticsearch.cloud_auth") || settings.set?("#{prefix}#{feature}.elasticsearch.api_key")
|
||||
opts.delete('user')
|
||||
end
|
||||
|
||||
if keystore = settings.get("#{prefix}#{feature}.elasticsearch.ssl.keystore.path")
|
||||
opts['keystore'] = keystore
|
||||
opts['keystore_password']= settings.get("#{prefix}#{feature}.elasticsearch.ssl.keystore.password")
|
||||
opts['ssl'] = true
|
||||
end
|
||||
opts
|
||||
end
|
||||
|
||||
def ssl?(feature, settings, prefix)
|
||||
return true if verify_https_scheme(feature, settings, prefix)
|
||||
return true if settings.set?("#{prefix}#{feature}.elasticsearch.cloud_id") # cloud_id always resolves to https hosts
|
||||
return true if settings.set?("#{prefix}#{feature}.elasticsearch.ssl.certificate_authority")
|
||||
return true if settings.set?("#{prefix}#{feature}.elasticsearch.ssl.truststore.path") && settings.set?("#{prefix}#{feature}.elasticsearch.ssl.truststore.password")
|
||||
return true if settings.set?("#{prefix}#{feature}.elasticsearch.ssl.keystore.path") && settings.set?("#{prefix}#{feature}.elasticsearch.ssl.keystore.password")
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
HTTPS_SCHEME = /^https:\/\/.+/
|
||||
def verify_https_scheme(feature, settings, prefix)
|
||||
hosts = Array(settings.get("#{prefix}#{feature}.elasticsearch.hosts"))
|
||||
hosts.all? {|host| host.match?(HTTPS_SCHEME)}
|
||||
end
|
||||
|
||||
# Elasticsearch settings can be extracted from the modules settings inside the configuration.
|
||||
# Few options will be supported, however - the modules security configuration is
|
||||
|
@ -113,9 +130,6 @@ module LogStash module Helpers
|
|||
end
|
||||
|
||||
# If no settings are configured, then assume that the feature has not been configured.
|
||||
# The assumption is that with security setup, at least one setting (password or certificates)
|
||||
# should be configured. If security is not setup, and defaults 'just work' for monitoring, then
|
||||
# this will need to be reconsidered.
|
||||
def feature_configured?(feature, settings)
|
||||
ES_SETTINGS.each do |option|
|
||||
return true if settings.set?("xpack.#{feature}.elasticsearch.#{option}")
|
||||
|
@ -146,20 +160,62 @@ module LogStash module Helpers
|
|||
|
||||
private
|
||||
|
||||
def check_cloud_id_configuration!(feature, settings, prefix)
|
||||
return if !settings.set?("#{prefix}#{feature}.elasticsearch.hosts")
|
||||
def validate_authentication!(feature, settings, prefix)
|
||||
provided_cloud_id = settings.set?("#{prefix}#{feature}.elasticsearch.cloud_id")
|
||||
provided_hosts = settings.set?("#{prefix}#{feature}.elasticsearch.hosts")
|
||||
provided_cloud_auth = settings.set?("#{prefix}#{feature}.elasticsearch.cloud_auth")
|
||||
provided_api_key = settings.set?("#{prefix}#{feature}.elasticsearch.api_key")
|
||||
provided_username = settings.set?("#{prefix}#{feature}.elasticsearch.username")
|
||||
provided_password = settings.set?("#{prefix}#{feature}.elasticsearch.password")
|
||||
|
||||
raise ArgumentError.new("Both \"#{prefix}#{feature}.elasticsearch.cloud_id\" and " +
|
||||
"\"#{prefix}#{feature}.elasticsearch.hosts\" specified, please only use one of those.")
|
||||
# note that the username setting has a default value and in the verifications below
|
||||
# we can test on the password option being set as a proxy to using basic auth because
|
||||
# if the username is not explicitly set it will use its default value.
|
||||
|
||||
if provided_cloud_auth && (provided_username || provided_password)
|
||||
raise ArgumentError.new(
|
||||
"Both #{prefix}#{feature}.elasticsearch.cloud_auth and " +
|
||||
"#{prefix}#{feature}.elasticsearch.username/password " +
|
||||
"specified, please only use one of those"
|
||||
)
|
||||
end
|
||||
|
||||
if provided_username && !provided_password
|
||||
raise(ArgumentError,
|
||||
"When using #{prefix}#{feature}.elasticsearch.username, " +
|
||||
"#{prefix}#{feature}.elasticsearch.password must also be set"
|
||||
)
|
||||
end
|
||||
|
||||
if provided_cloud_id
|
||||
if provided_hosts
|
||||
raise(ArgumentError,
|
||||
"Both #{prefix}#{feature}.elasticsearch.cloud_id and " +
|
||||
"#{prefix}#{feature}.elasticsearch.hosts specified, please only use one of those"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
authentication_count = 0
|
||||
authentication_count += 1 if provided_cloud_auth
|
||||
authentication_count += 1 if provided_password
|
||||
authentication_count += 1 if provided_api_key
|
||||
|
||||
if authentication_count == 0
|
||||
# when no explicit authentication is set it is relying on default username
|
||||
# but without and explicit password set
|
||||
raise(ArgumentError,
|
||||
"With the default #{prefix}#{feature}.elasticsearch.username, " +
|
||||
"#{prefix}#{feature}.elasticsearch.password must be set"
|
||||
)
|
||||
end
|
||||
|
||||
if authentication_count > 1
|
||||
raise(ArgumentError, "Multiple authentication options are specified, please only use one of #{prefix}#{feature}.elasticsearch.username/password, #{prefix}#{feature}.elasticsearch.cloud_auth or #{prefix}#{feature}.elasticsearch.api_key")
|
||||
end
|
||||
|
||||
if provided_api_key && !ssl?(feature, settings, prefix)
|
||||
raise(ArgumentError, "Using api_key authentication requires SSL/TLS secured communication")
|
||||
end
|
||||
end
|
||||
|
||||
def check_cloud_auth_configuration!(feature, settings, prefix)
|
||||
return if !settings.set?("#{prefix}#{feature}.elasticsearch.username") &&
|
||||
!settings.set?("#{prefix}#{feature}.elasticsearch.password")
|
||||
|
||||
raise ArgumentError.new("Both \"#{prefix}#{feature}.elasticsearch.cloud_auth\" and " +
|
||||
"\"#{prefix}#{feature}.elasticsearch.username\"/\"#{prefix}#{feature}.elasticsearch.password\" " +
|
||||
"specified, please only use one of those.")
|
||||
end
|
||||
|
||||
end end end
|
||||
|
|
|
@ -13,10 +13,10 @@ module LogStash module Monitoring
|
|||
include LogStash::Util::Loggable
|
||||
FEATURE = 'monitoring'
|
||||
|
||||
def initialize(pipeline_config, agent)
|
||||
def initialize(pipeline_config, agent, settings)
|
||||
super(pipeline_config.settings)
|
||||
@pipeline_config = pipeline_config
|
||||
@settings = LogStash::SETTINGS.clone
|
||||
@settings = settings
|
||||
@agent = agent
|
||||
@es_options = es_options_from_settings_or_modules(FEATURE, @settings)
|
||||
setup_license_checker(FEATURE)
|
||||
|
|
|
@ -32,7 +32,9 @@ module LogStash
|
|||
@password = es_settings['password']
|
||||
@cloud_id = es_settings['cloud_id']
|
||||
@cloud_auth = es_settings['cloud_auth']
|
||||
@api_key = es_settings['api_key']
|
||||
@proxy = es_settings['proxy']
|
||||
@ssl = es_settings['ssl']
|
||||
@ca_path = es_settings['cacert']
|
||||
@truststore_path = es_settings['truststore']
|
||||
@truststore_password = es_settings['truststore_password']
|
||||
|
@ -42,8 +44,8 @@ module LogStash
|
|||
@ssl_certificate_verification = (es_settings['verification_mode'] == 'certificate')
|
||||
end
|
||||
|
||||
attr_accessor :system_api_version, :es_hosts, :user, :password, :node_uuid, :cloud_id, :cloud_auth, :proxy
|
||||
attr_accessor :ca_path, :truststore_path, :truststore_password
|
||||
attr_accessor :system_api_version, :es_hosts, :user, :password, :node_uuid, :cloud_id, :cloud_auth, :api_key
|
||||
attr_accessor :proxy, :ssl, :ca_path, :truststore_path, :truststore_password
|
||||
attr_accessor :keystore_path, :keystore_password, :sniffing, :ssl_certificate_verification
|
||||
|
||||
def collection_interval
|
||||
|
@ -70,8 +72,12 @@ module LogStash
|
|||
user && password
|
||||
end
|
||||
|
||||
def api_key?
|
||||
api_key
|
||||
end
|
||||
|
||||
def ssl?
|
||||
ca_path || (truststore_path && truststore_password) || (keystore_path && keystore_password)
|
||||
ssl || ca_path || (truststore_path && truststore_password) || (keystore_path && keystore_password)
|
||||
end
|
||||
|
||||
def truststore?
|
||||
|
@ -133,7 +139,7 @@ module LogStash
|
|||
|
||||
logger.trace("registering the metrics pipeline")
|
||||
LogStash::SETTINGS.set("node.uuid", runner.agent.id)
|
||||
internal_pipeline_source = LogStash::Monitoring::InternalPipelineSource.new(setup_metrics_pipeline, runner.agent)
|
||||
internal_pipeline_source = LogStash::Monitoring::InternalPipelineSource.new(setup_metrics_pipeline, runner.agent, LogStash::SETTINGS.clone)
|
||||
runner.source_loader.add_source(internal_pipeline_source)
|
||||
rescue => e
|
||||
logger.error("Failed to set up the metrics pipeline", :message => e.message, :backtrace => e.backtrace)
|
||||
|
@ -260,6 +266,7 @@ module LogStash
|
|||
settings.register(LogStash::Setting::NullableString.new("#{prefix}monitoring.elasticsearch.proxy"))
|
||||
settings.register(LogStash::Setting::NullableString.new("#{prefix}monitoring.elasticsearch.cloud_id"))
|
||||
settings.register(LogStash::Setting::NullableString.new("#{prefix}monitoring.elasticsearch.cloud_auth"))
|
||||
settings.register(LogStash::Setting::NullableString.new("#{prefix}monitoring.elasticsearch.api_key"))
|
||||
settings.register(LogStash::Setting::NullableString.new("#{prefix}monitoring.elasticsearch.ssl.certificate_authority"))
|
||||
settings.register(LogStash::Setting::NullableString.new("#{prefix}monitoring.elasticsearch.ssl.truststore.path"))
|
||||
settings.register(LogStash::Setting::NullableString.new("#{prefix}monitoring.elasticsearch.ssl.truststore.password"))
|
||||
|
|
|
@ -12,13 +12,20 @@ input {
|
|||
}
|
||||
output {
|
||||
elasticsearch_monitoring {
|
||||
<% if auth? %>
|
||||
user => "<%= user %>"
|
||||
password => "<%= password %>"
|
||||
<% end %>
|
||||
<% if api_key? %>
|
||||
api_key => "<%= api_key %>"
|
||||
<% end %>
|
||||
<% if cloud_id? %>
|
||||
cloud_id => "<%= cloud_id %>"
|
||||
<% if cloud_auth %>
|
||||
cloud_auth => "<%= cloud_auth %>"
|
||||
<% end %>
|
||||
<% else %>
|
||||
hosts => <%= es_hosts %>
|
||||
<% end %>
|
||||
<% if cloud_auth %>
|
||||
cloud_auth => "<%= cloud_auth %>"
|
||||
<% end %>
|
||||
bulk_path => "<%= monitoring_endpoint %>"
|
||||
manage_template => false
|
||||
|
@ -28,10 +35,6 @@ output {
|
|||
<% if proxy? %>
|
||||
proxy => "<%= proxy %>"
|
||||
<% end %>
|
||||
<% if auth? && !cloud_auth? %>
|
||||
user => "<%= user %>"
|
||||
password => "<%= password %>"
|
||||
<% end %>
|
||||
<% if ssl? %>
|
||||
ssl => true
|
||||
<% if ca_path %>
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
require "spec_helper"
|
||||
require "logstash/json"
|
||||
require "logstash/runner"
|
||||
require "config_management/elasticsearch_source"
|
||||
require "config_management/extension"
|
||||
require "license_checker/license_manager"
|
||||
|
@ -164,9 +165,12 @@ describe LogStash::ConfigManagement::ElasticsearchSource do
|
|||
end
|
||||
|
||||
let(:pipeline_id) { "foobar" }
|
||||
let(:settings) { { "xpack.management.pipeline.id" => pipeline_id,
|
||||
"xpack.management.elasticsearch.password" => "testpassword"
|
||||
} }
|
||||
let(:settings) do
|
||||
{
|
||||
"xpack.management.pipeline.id" => pipeline_id,
|
||||
"xpack.management.elasticsearch.password" => "testpassword"
|
||||
}
|
||||
end
|
||||
|
||||
it "generates the path to get the configuration" do
|
||||
expect(subject.config_path).to eq("#{described_class::PIPELINE_INDEX}/_mget")
|
||||
|
@ -182,10 +186,13 @@ describe LogStash::ConfigManagement::ElasticsearchSource do
|
|||
end
|
||||
|
||||
context "when enabled" do
|
||||
let(:settings) { {
|
||||
"xpack.management.enabled" => true,
|
||||
"xpack.management.elasticsearch.password" => "testpassword"
|
||||
} }
|
||||
let(:settings) do
|
||||
{
|
||||
"xpack.management.enabled" => true,
|
||||
"xpack.management.elasticsearch.username" => "testuser",
|
||||
"xpack.management.elasticsearch.password" => "testpassword"
|
||||
}
|
||||
end
|
||||
|
||||
it "returns true" do
|
||||
expect(subject.match?).to be_truthy
|
||||
|
@ -357,7 +364,8 @@ describe LogStash::ConfigManagement::ElasticsearchSource do
|
|||
context 'when security is enabled in Elasticsearch' do
|
||||
let(:security_enabled) { true }
|
||||
it 'should not raise an error' do
|
||||
expect { subject.pipeline_configs }.not_to raise_error(LogStash::LicenseChecker::LicenseError)
|
||||
expect_any_instance_of(described_class).to receive(:build_client).and_return(mock_client)
|
||||
expect { subject.pipeline_configs }.not_to raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
require "spec_helper"
|
||||
require "logstash/json"
|
||||
require "logstash/runner"
|
||||
require 'helpers/elasticsearch_options'
|
||||
require "license_checker/license_manager"
|
||||
require 'monitoring/monitoring'
|
||||
|
@ -97,65 +98,267 @@ describe LogStash::Helpers::ElasticsearchOptions do
|
|||
end
|
||||
|
||||
describe "es_options_from_settings" do
|
||||
let(:settings) do
|
||||
{
|
||||
|
||||
context "with implicit username" do
|
||||
let(:settings) do
|
||||
{
|
||||
"xpack.monitoring.enabled" => true,
|
||||
"xpack.monitoring.elasticsearch.hosts" => elasticsearch_url,
|
||||
}
|
||||
end
|
||||
|
||||
it "fails without password" do
|
||||
expect {
|
||||
test_class.es_options_from_settings_or_modules('monitoring', system_settings)
|
||||
}.to raise_error(ArgumentError, /password must be set/)
|
||||
end
|
||||
|
||||
context "with cloud_auth" do
|
||||
let(:cloud_username) { 'elastic' }
|
||||
let(:cloud_password) { 'passw0rd'}
|
||||
let(:cloud_auth) { "#{cloud_username}:#{cloud_password}" }
|
||||
|
||||
let(:settings) do
|
||||
super.merge(
|
||||
"xpack.monitoring.elasticsearch.cloud_auth" => cloud_auth,
|
||||
)
|
||||
end
|
||||
|
||||
it "silently ignores the default username" do
|
||||
es_options = test_class.es_options_from_settings_or_modules('monitoring', system_settings)
|
||||
expect(es_options).to include("cloud_auth")
|
||||
expect(es_options).to_not include("user")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
context "with api_key" do
|
||||
let(:settings) do
|
||||
super.merge(
|
||||
"xpack.monitoring.elasticsearch.api_key" => 'foo:bar'
|
||||
)
|
||||
end
|
||||
|
||||
it "silently ignores the default username" do
|
||||
es_options = test_class.es_options_from_settings_or_modules('monitoring', system_settings)
|
||||
expect(es_options).to include("api_key")
|
||||
expect(es_options).to_not include("user")
|
||||
end
|
||||
|
||||
context "and explicit password" do
|
||||
let(:settings) do
|
||||
super.merge(
|
||||
"xpack.monitoring.elasticsearch.password" => elasticsearch_password
|
||||
)
|
||||
end
|
||||
|
||||
it "fails for multiple authentications" do
|
||||
expect {
|
||||
test_class.es_options_from_settings_or_modules('monitoring', system_settings)
|
||||
}.to raise_error(ArgumentError, /Multiple authentication options are specified/)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with explicit username" do
|
||||
let(:settings) do
|
||||
{
|
||||
"xpack.monitoring.enabled" => true,
|
||||
"xpack.monitoring.elasticsearch.hosts" => elasticsearch_url,
|
||||
"xpack.monitoring.elasticsearch.username" => "foo",
|
||||
}
|
||||
end
|
||||
|
||||
it "fails without password" do
|
||||
expect {
|
||||
test_class.es_options_from_settings_or_modules('monitoring', system_settings)
|
||||
}.to raise_error(ArgumentError, /password must also be set/)
|
||||
end
|
||||
|
||||
context "with cloud_auth" do
|
||||
let(:settings) do
|
||||
super.merge(
|
||||
"xpack.monitoring.elasticsearch.password" => "bar",
|
||||
"xpack.monitoring.elasticsearch.cloud_auth" => "foo:bar",
|
||||
)
|
||||
end
|
||||
|
||||
it "fails for multiple authentications" do
|
||||
expect {
|
||||
test_class.es_options_from_settings_or_modules('monitoring', system_settings)
|
||||
}.to raise_error(ArgumentError, /Both.*?cloud_auth.*?and.*?username.*?specified/)
|
||||
end
|
||||
end
|
||||
|
||||
context "with api_key" do
|
||||
let(:settings) do
|
||||
super.merge(
|
||||
"xpack.monitoring.elasticsearch.password" => "bar",
|
||||
"xpack.monitoring.elasticsearch.api_key" => 'foo:bar'
|
||||
)
|
||||
end
|
||||
|
||||
it "fails for multiple authentications" do
|
||||
expect {
|
||||
test_class.es_options_from_settings_or_modules('monitoring', system_settings)
|
||||
}.to raise_error(ArgumentError, /Multiple authentication options are specified/)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with username and password" do
|
||||
let(:settings) do
|
||||
{
|
||||
"xpack.monitoring.enabled" => true,
|
||||
"xpack.monitoring.elasticsearch.hosts" => elasticsearch_url,
|
||||
"xpack.monitoring.elasticsearch.username" => elasticsearch_username,
|
||||
"xpack.monitoring.elasticsearch.password" => elasticsearch_password,
|
||||
}
|
||||
end
|
||||
|
||||
it_behaves_like 'elasticsearch options hash is populated without security'
|
||||
it_behaves_like 'elasticsearch options hash is populated with secure options'
|
||||
|
||||
context 'when cloud id and auth are set' do
|
||||
let(:cloud_name) { 'thebigone'}
|
||||
let(:cloud_domain) { 'elastic.co'}
|
||||
let(:cloud_id) { "monitoring:#{Base64.urlsafe_encode64("#{cloud_domain}$#{cloud_name}$ignored")}" }
|
||||
let(:cloud_username) { 'elastic' }
|
||||
let(:cloud_password) { 'passw0rd'}
|
||||
let(:cloud_auth) { "#{cloud_username}:#{cloud_password}" }
|
||||
let(:expected_url) { ["https://#{cloud_name}.#{cloud_domain}:443"] }
|
||||
let(:settings) do
|
||||
{
|
||||
"xpack.monitoring.enabled" => true,
|
||||
"xpack.monitoring.elasticsearch.cloud_id" => cloud_id,
|
||||
"xpack.monitoring.elasticsearch.cloud_auth" => cloud_auth,
|
||||
}
|
||||
end
|
||||
|
||||
it "creates the elasticsearch output options hash" do
|
||||
es_options = test_class.es_options_from_settings_or_modules('monitoring', system_settings)
|
||||
expect(es_options).to include("cloud_id" => cloud_id, "cloud_auth" => cloud_auth)
|
||||
expect(es_options.keys).to_not include("hosts")
|
||||
expect(es_options.keys).to_not include("username")
|
||||
expect(es_options.keys).to_not include("password")
|
||||
it_behaves_like 'elasticsearch options hash is populated without security'
|
||||
it_behaves_like 'elasticsearch options hash is populated with secure options'
|
||||
end
|
||||
|
||||
|
||||
context 'when cloud_id' do
|
||||
let(:cloud_name) { 'thebigone'}
|
||||
let(:cloud_domain) { 'elastic.co'}
|
||||
let(:cloud_id) { "monitoring:#{Base64.urlsafe_encode64("#{cloud_domain}$#{cloud_name}$ignored")}" }
|
||||
let(:expected_url) { ["https://#{cloud_name}.#{cloud_domain}:443"] }
|
||||
let(:settings) do
|
||||
{
|
||||
"xpack.monitoring.enabled" => true,
|
||||
"xpack.monitoring.elasticsearch.cloud_id" => cloud_id,
|
||||
}
|
||||
end
|
||||
|
||||
context 'hosts also set' do
|
||||
let(:settings) do
|
||||
super.merge(
|
||||
"xpack.monitoring.elasticsearch.hosts" => 'https://localhost:9200'
|
||||
"xpack.monitoring.elasticsearch.hosts" => 'https://localhost:9200'
|
||||
)
|
||||
end
|
||||
|
||||
it "raises due invalid configuration" do
|
||||
expect { test_class.es_options_from_settings_or_modules('monitoring', system_settings) }.
|
||||
to raise_error(ArgumentError, /Both.*?cloud_id.*?and.*?hosts.*?specified/)
|
||||
expect {
|
||||
test_class.es_options_from_settings_or_modules('monitoring', system_settings)
|
||||
}.to raise_error(ArgumentError, /Both.*?cloud_id.*?and.*?hosts.*?specified/)
|
||||
end
|
||||
end
|
||||
|
||||
context 'username also set' do
|
||||
context "when cloud_auth is set" do
|
||||
let(:cloud_username) { 'elastic' }
|
||||
let(:cloud_password) { 'passw0rd'}
|
||||
let(:cloud_auth) { "#{cloud_username}:#{cloud_password}" }
|
||||
let(:settings) do
|
||||
super.merge(
|
||||
"xpack.monitoring.elasticsearch.username" => 'elastic'
|
||||
"xpack.monitoring.elasticsearch.cloud_auth" => cloud_auth,
|
||||
)
|
||||
end
|
||||
|
||||
it "raises due invalid configuration" do
|
||||
expect { test_class.es_options_from_settings_or_modules('monitoring', system_settings) }.
|
||||
to raise_error(ArgumentError, /Both.*?cloud_auth.*?and.*?username.*?specified/)
|
||||
it "creates the elasticsearch output options hash" do
|
||||
es_options = test_class.es_options_from_settings_or_modules('monitoring', system_settings)
|
||||
expect(es_options).to include("cloud_id" => cloud_id, "cloud_auth" => cloud_auth)
|
||||
expect(es_options.keys).to_not include("hosts")
|
||||
expect(es_options.keys).to_not include("username")
|
||||
expect(es_options.keys).to_not include("password")
|
||||
end
|
||||
|
||||
context 'username also set' do
|
||||
let(:settings) do
|
||||
super.merge(
|
||||
"xpack.monitoring.elasticsearch.username" => 'elastic'
|
||||
)
|
||||
end
|
||||
|
||||
it "raises for invalid configuration" do
|
||||
expect {
|
||||
test_class.es_options_from_settings_or_modules('monitoring', system_settings)
|
||||
}.to raise_error(ArgumentError, /Both.*?cloud_auth.*?and.*?username.*?specified/)
|
||||
end
|
||||
end
|
||||
|
||||
context 'api_key also set' do
|
||||
let(:settings) do
|
||||
super.merge(
|
||||
"xpack.monitoring.elasticsearch.api_key" => 'foo:bar',
|
||||
)
|
||||
end
|
||||
|
||||
it "raises for invalid configuration" do
|
||||
expect {
|
||||
test_class.es_options_from_settings_or_modules('monitoring', system_settings)
|
||||
}.to raise_error(ArgumentError, /Multiple authentication options are specified/)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when cloud_auth is not set" do
|
||||
|
||||
it "raises for invalid configuration" do
|
||||
# if not other authn is provided it will assume basic auth using the default username
|
||||
# but the password is missing.
|
||||
expect {
|
||||
test_class.es_options_from_settings_or_modules('monitoring', system_settings)
|
||||
}.to raise_error(ArgumentError, /With the default.*?username,.*?password must be set/)
|
||||
end
|
||||
|
||||
context 'username and password set' do
|
||||
let(:settings) do
|
||||
super.merge(
|
||||
"xpack.monitoring.elasticsearch.username" => 'foo',
|
||||
"xpack.monitoring.elasticsearch.password" => 'bar'
|
||||
)
|
||||
end
|
||||
|
||||
it "creates the elasticsearch output options hash" do
|
||||
es_options = test_class.es_options_from_settings_or_modules('monitoring', system_settings)
|
||||
expect(es_options).to include("cloud_id", "user", "password")
|
||||
expect(es_options.keys).to_not include("hosts")
|
||||
end
|
||||
end
|
||||
|
||||
context 'api_key set' do
|
||||
let(:settings) do
|
||||
super.merge(
|
||||
"xpack.monitoring.elasticsearch.api_key" => 'foo:bar'
|
||||
)
|
||||
end
|
||||
|
||||
it "creates the elasticsearch output options hash" do
|
||||
es_options = test_class.es_options_from_settings_or_modules('monitoring', system_settings)
|
||||
expect(es_options).to include("cloud_id", "api_key")
|
||||
expect(es_options.keys).to_not include("hosts")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when api_key is set' do
|
||||
let(:api_key) { 'foo:bar'}
|
||||
let(:settings) do
|
||||
{
|
||||
"xpack.monitoring.enabled" => true,
|
||||
"xpack.monitoring.elasticsearch.hosts" => elasticsearch_url,
|
||||
"xpack.monitoring.elasticsearch.api_key" => api_key,
|
||||
}
|
||||
end
|
||||
|
||||
it "creates the elasticsearch output options hash" do
|
||||
es_options = test_class.es_options_from_settings_or_modules('monitoring', system_settings)
|
||||
expect(es_options).to include("api_key" => api_key)
|
||||
expect(es_options.keys).to include("hosts")
|
||||
end
|
||||
|
||||
context "with a non https host" do
|
||||
let(:elasticsearch_url) { ["https://host1", "http://host2"] }
|
||||
|
||||
it "fails at options validation" do
|
||||
expect {
|
||||
test_class.es_options_from_settings_or_modules('monitoring', system_settings)
|
||||
}.to raise_error(ArgumentError, /api_key authentication requires SSL\/TLS/)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,6 +7,7 @@ require 'support/helpers'
|
|||
require "license_checker/license_reader"
|
||||
require "helpers/elasticsearch_options"
|
||||
require "monitoring/monitoring"
|
||||
require "logstash/runner"
|
||||
|
||||
describe LogStash::LicenseChecker::LicenseReader do
|
||||
let(:elasticsearch_url) { "https://localhost:9898" }
|
||||
|
@ -23,7 +24,7 @@ describe LogStash::LicenseChecker::LicenseReader do
|
|||
let(:settings) do
|
||||
{
|
||||
"xpack.monitoring.enabled" => true,
|
||||
"xpack.monitoring.elasticsearch.hosts" => [ elasticsearch_url ],
|
||||
"xpack.monitoring.elasticsearch.hosts" => [ elasticsearch_url],
|
||||
"xpack.monitoring.elasticsearch.username" => elasticsearch_username,
|
||||
"xpack.monitoring.elasticsearch.password" => elasticsearch_password,
|
||||
}
|
||||
|
@ -128,4 +129,19 @@ describe LogStash::LicenseChecker::LicenseReader do
|
|||
expect( subject.client.options ).to include(:user => 'elastic', :password => 'LnWMLeK3EQPTf3G3F1IBdFvO')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with api_key' do
|
||||
let(:api_key) { "foo:bar" }
|
||||
let(:settings) do
|
||||
{
|
||||
"xpack.monitoring.enabled" => true,
|
||||
"xpack.monitoring.elasticsearch.hosts" => [elasticsearch_url],
|
||||
"xpack.monitoring.elasticsearch.api_key" => api_key,
|
||||
}
|
||||
end
|
||||
|
||||
it "builds ES client" do
|
||||
expect( subject.client.options[:client_settings][:headers] ).to include("Authorization" => "ApiKey Zm9vOmJhcg==")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
require "logstash-core"
|
||||
require "logstash/runner"
|
||||
require "logstash/agent"
|
||||
require "monitoring/inputs/metrics"
|
||||
require "rspec/wait"
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
require "logstash-core"
|
||||
require "logstash/agent"
|
||||
require "logstash/agent"
|
||||
require "logstash/runner"
|
||||
require "monitoring/inputs/metrics"
|
||||
require "logstash/config/pipeline_config"
|
||||
require "logstash/config/source/local"
|
||||
|
@ -23,11 +23,17 @@ describe LogStash::Monitoring::InternalPipelineSource do
|
|||
let(:options) { { "collection_interval" => xpack_monitoring_interval,
|
||||
"collection_timeout_interval" => 600 } }
|
||||
|
||||
subject { described_class.new(pipeline_config, mock_agent) }
|
||||
subject { described_class.new(pipeline_config, mock_agent, system_settings) }
|
||||
let(:mock_agent) { double("agent")}
|
||||
let(:mock_license_client) { double("es_client")}
|
||||
let(:license_reader) { LogStash::LicenseChecker::LicenseReader.new(system_settings, 'monitoring', es_options)}
|
||||
let(:system_settings) { LogStash::Runner::SYSTEM_SETTINGS.clone }
|
||||
let(:extension) { LogStash::MonitoringExtension.new }
|
||||
let(:system_settings) do
|
||||
LogStash::Runner::SYSTEM_SETTINGS.clone.tap do |system_settings|
|
||||
extension.additionals_settings(system_settings) # register defaults from extension
|
||||
apply_settings(settings, system_settings) # apply `settings`
|
||||
end
|
||||
end
|
||||
let(:license_status) { 'active'}
|
||||
let(:license_type) { 'trial' }
|
||||
let(:license_expiry_date) { Time.now + (60 * 60 * 24)}
|
||||
|
@ -70,7 +76,6 @@ describe LogStash::Monitoring::InternalPipelineSource do
|
|||
end
|
||||
|
||||
before :each do
|
||||
allow(subject).to receive(:es_options_from_settings_or_modules).and_return(es_options)
|
||||
allow(subject).to receive(:license_reader).and_return(license_reader)
|
||||
allow(license_reader).to receive(:build_client).and_return(mock_license_client)
|
||||
end
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue