Enable explicitly enforcing bootstrap checks
This commit adds a system property that enables end-users to explicitly enforce the bootstrap checks, independently of the binding of the transport protocol. This can be useful for single-node production systems that do not bind the transport protocol (and thus the bootstrap checks would not be enforced). Relates #23585
This commit is contained in:
parent
326d6456fe
commit
f7b8128f92
|
@ -48,16 +48,21 @@ import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We enforce bootstrap checks once a node has the transport protocol bound to a non-loopback interface. In this case we assume the node is
|
* We enforce bootstrap checks once a node has the transport protocol bound to a non-loopback interface or if the system property {@code
|
||||||
* running in production and all bootstrap checks must pass.
|
* es.enforce.bootstrap.checks} is set to {@true}. In this case we assume the node is running in production and all bootstrap checks must
|
||||||
|
* pass.
|
||||||
*/
|
*/
|
||||||
final class BootstrapChecks {
|
final class BootstrapChecks {
|
||||||
|
|
||||||
private BootstrapChecks() {
|
private BootstrapChecks() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static final String ES_ENFORCE_BOOTSTRAP_CHECKS = "es.enforce.bootstrap.checks";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes the bootstrap checks if the node has the transport protocol bound to a non-loopback interface.
|
* Executes the bootstrap checks if the node has the transport protocol bound to a non-loopback interface. If the system property
|
||||||
|
* {@code es.enforce.bootstrap.checks} is set to {@code true} then the bootstrap checks will be enforced regardless of whether or not
|
||||||
|
* the transport protocol is bound to a non-loopback interface.
|
||||||
*
|
*
|
||||||
* @param settings the current node settings
|
* @param settings the current node settings
|
||||||
* @param boundTransportAddress the node network bindings
|
* @param boundTransportAddress the node network bindings
|
||||||
|
@ -74,7 +79,9 @@ final class BootstrapChecks {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes the provided checks and fails the node if {@code enforceLimits} is {@code true}, otherwise logs warnings.
|
* Executes the provided checks and fails the node if {@code enforceLimits} is {@code true}, otherwise logs warnings. If the system
|
||||||
|
* property {@code es.enforce.bootstrap.checks} is set to {@code true} then the bootstrap checks will be enforced regardless of whether
|
||||||
|
* or not the transport protocol is bound to a non-loopback interface.
|
||||||
*
|
*
|
||||||
* @param enforceLimits {@code true} if the checks should be enforced or otherwise warned
|
* @param enforceLimits {@code true} if the checks should be enforced or otherwise warned
|
||||||
* @param checks the checks to execute
|
* @param checks the checks to execute
|
||||||
|
@ -88,7 +95,9 @@ final class BootstrapChecks {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes the provided checks and fails the node if {@code enforceLimits} is {@code true}, otherwise logs warnings.
|
* Executes the provided checks and fails the node if {@code enforceLimits} is {@code true}, otherwise logs warnings. If the system
|
||||||
|
* property {@code es.enforce.bootstrap.checks }is set to {@code true} then the bootstrap checks will be enforced regardless of whether
|
||||||
|
* or not the transport protocol is bound to a non-loopback interface.
|
||||||
*
|
*
|
||||||
* @param enforceLimits {@code true} if the checks should be enforced or otherwise warned
|
* @param enforceLimits {@code true} if the checks should be enforced or otherwise warned
|
||||||
* @param checks the checks to execute
|
* @param checks the checks to execute
|
||||||
|
@ -101,13 +110,31 @@ final class BootstrapChecks {
|
||||||
final List<String> errors = new ArrayList<>();
|
final List<String> errors = new ArrayList<>();
|
||||||
final List<String> ignoredErrors = new ArrayList<>();
|
final List<String> ignoredErrors = new ArrayList<>();
|
||||||
|
|
||||||
|
final String esEnforceBootstrapChecks = System.getProperty(ES_ENFORCE_BOOTSTRAP_CHECKS);
|
||||||
|
final boolean enforceBootstrapChecks;
|
||||||
|
if (esEnforceBootstrapChecks == null) {
|
||||||
|
enforceBootstrapChecks = false;
|
||||||
|
} else if (Boolean.TRUE.toString().equals(esEnforceBootstrapChecks)) {
|
||||||
|
enforceBootstrapChecks = true;
|
||||||
|
} else {
|
||||||
|
final String message =
|
||||||
|
String.format(
|
||||||
|
Locale.ROOT,
|
||||||
|
"[%s] must be [true] but was [%s]",
|
||||||
|
ES_ENFORCE_BOOTSTRAP_CHECKS,
|
||||||
|
esEnforceBootstrapChecks);
|
||||||
|
throw new IllegalArgumentException(message);
|
||||||
|
}
|
||||||
|
|
||||||
if (enforceLimits) {
|
if (enforceLimits) {
|
||||||
logger.info("bound or publishing to a non-loopback or non-link-local address, enforcing bootstrap checks");
|
logger.info("bound or publishing to a non-loopback or non-link-local address, enforcing bootstrap checks");
|
||||||
|
} else if (enforceBootstrapChecks) {
|
||||||
|
logger.info("explicitly enforcing bootstrap checks");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final BootstrapCheck check : checks) {
|
for (final BootstrapCheck check : checks) {
|
||||||
if (check.check()) {
|
if (check.check()) {
|
||||||
if (!enforceLimits && !check.alwaysEnforce()) {
|
if (!(enforceLimits || enforceBootstrapChecks) && !check.alwaysEnforce()) {
|
||||||
ignoredErrors.add(check.errorMessage());
|
ignoredErrors.add(check.errorMessage());
|
||||||
} else {
|
} else {
|
||||||
errors.add(check.errorMessage());
|
errors.add(check.errorMessage());
|
||||||
|
@ -127,7 +154,6 @@ final class BootstrapChecks {
|
||||||
errors.stream().map(IllegalStateException::new).forEach(ne::addSuppressed);
|
errors.stream().map(IllegalStateException::new).forEach(ne::addSuppressed);
|
||||||
throw ne;
|
throw ne;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void log(final Logger logger, final String error) {
|
static void log(final Logger logger, final String error) {
|
||||||
|
@ -140,9 +166,9 @@ final class BootstrapChecks {
|
||||||
* @param boundTransportAddress the node network bindings
|
* @param boundTransportAddress the node network bindings
|
||||||
* @return {@code true} if the checks should be enforced
|
* @return {@code true} if the checks should be enforced
|
||||||
*/
|
*/
|
||||||
static boolean enforceLimits(BoundTransportAddress boundTransportAddress) {
|
static boolean enforceLimits(final BoundTransportAddress boundTransportAddress) {
|
||||||
Predicate<TransportAddress> isLoopbackOrLinkLocalAddress = t -> t.address().getAddress().isLinkLocalAddress()
|
Predicate<TransportAddress> isLoopbackOrLinkLocalAddress =
|
||||||
|| t.address().getAddress().isLoopbackAddress();
|
t -> t.address().getAddress().isLinkLocalAddress() || t.address().getAddress().isLoopbackAddress();
|
||||||
return !(Arrays.stream(boundTransportAddress.boundAddresses()).allMatch(isLoopbackOrLinkLocalAddress) &&
|
return !(Arrays.stream(boundTransportAddress.boundAddresses()).allMatch(isLoopbackOrLinkLocalAddress) &&
|
||||||
isLoopbackOrLinkLocalAddress.test(boundTransportAddress.publishAddress()));
|
isLoopbackOrLinkLocalAddress.test(boundTransportAddress.publishAddress()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,7 @@ import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
public class BootstrapCheckTests extends ESTestCase {
|
public class BootstrapChecksTests extends ESTestCase {
|
||||||
|
|
||||||
public void testNonProductionMode() throws NodeValidationException {
|
public void testNonProductionMode() throws NodeValidationException {
|
||||||
// nothing should happen since we are in non-production mode
|
// nothing should happen since we are in non-production mode
|
|
@ -35,7 +35,12 @@ production mode if it does bind transport to an external interface. Note
|
||||||
that HTTP can be configured independently of transport via
|
that HTTP can be configured independently of transport via
|
||||||
<<modules-http,`http.host`>> and <<modules-transport,`transport.host`>>;
|
<<modules-http,`http.host`>> and <<modules-transport,`transport.host`>>;
|
||||||
this can be useful for configuring a single instance to be reachable via
|
this can be useful for configuring a single instance to be reachable via
|
||||||
HTTP for testing purposes without triggering production mode.
|
HTTP for testing purposes without triggering production mode. If you do
|
||||||
|
want to force enforcement of the bootstrap checks independent of the
|
||||||
|
binding of the transport protocal, you can set the system property
|
||||||
|
`es.enforce.bootstrap.checks` to `true` (this can be useful on a
|
||||||
|
single-node production system that does not bind transport to an external
|
||||||
|
interface).
|
||||||
|
|
||||||
=== Heap size check
|
=== Heap size check
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.elasticsearch.bootstrap;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.elasticsearch.common.SuppressForbidden;
|
||||||
|
import org.elasticsearch.node.NodeValidationException;
|
||||||
|
import org.elasticsearch.test.ESTestCase;
|
||||||
|
import org.hamcrest.Matcher;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static java.util.Collections.emptyList;
|
||||||
|
import static org.elasticsearch.bootstrap.BootstrapChecks.ES_ENFORCE_BOOTSTRAP_CHECKS;
|
||||||
|
import static org.hamcrest.Matchers.allOf;
|
||||||
|
import static org.hamcrest.Matchers.containsString;
|
||||||
|
import static org.hamcrest.Matchers.hasToString;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||||
|
|
||||||
|
public class EvilBootstrapChecksTests extends ESTestCase {
|
||||||
|
|
||||||
|
private String esEnforceBootstrapChecks = System.getProperty(ES_ENFORCE_BOOTSTRAP_CHECKS);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@After
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
setEsEnforceBootstrapChecks(esEnforceBootstrapChecks);
|
||||||
|
super.tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testEnforceBootstrapChecks() throws NodeValidationException {
|
||||||
|
setEsEnforceBootstrapChecks("true");
|
||||||
|
final List<BootstrapCheck> checks = Collections.singletonList(
|
||||||
|
new BootstrapCheck() {
|
||||||
|
@Override
|
||||||
|
public boolean check() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String errorMessage() {
|
||||||
|
return "error";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
final Logger logger = mock(Logger.class);
|
||||||
|
|
||||||
|
final NodeValidationException e = expectThrows(
|
||||||
|
NodeValidationException.class,
|
||||||
|
() -> BootstrapChecks.check(false, checks, logger));
|
||||||
|
final Matcher<String> allOf =
|
||||||
|
allOf(containsString("bootstrap checks failed"), containsString("error"));
|
||||||
|
assertThat(e, hasToString(allOf));
|
||||||
|
verify(logger).info("explicitly enforcing bootstrap checks");
|
||||||
|
verifyNoMoreInteractions(logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testNonEnforcedBootstrapChecks() throws NodeValidationException {
|
||||||
|
setEsEnforceBootstrapChecks(null);
|
||||||
|
final Logger logger = mock(Logger.class);
|
||||||
|
// nothing should happen
|
||||||
|
BootstrapChecks.check(false, emptyList(), logger);
|
||||||
|
verifyNoMoreInteractions(logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testInvalidValue() {
|
||||||
|
final String value = randomAsciiOfLength(8);
|
||||||
|
setEsEnforceBootstrapChecks(value);
|
||||||
|
final boolean enforceLimits = randomBoolean();
|
||||||
|
final IllegalArgumentException e = expectThrows(
|
||||||
|
IllegalArgumentException.class,
|
||||||
|
() -> BootstrapChecks.check(enforceLimits, emptyList(), "testInvalidValue"));
|
||||||
|
final Matcher<String> matcher = containsString(
|
||||||
|
"[es.enforce.bootstrap.checks] must be [true] but was [" + value + "]");
|
||||||
|
assertThat(e, hasToString(matcher));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressForbidden(reason = "set or clear system property es.enforce.bootstrap.checks")
|
||||||
|
public void setEsEnforceBootstrapChecks(final String value) {
|
||||||
|
if (value == null) {
|
||||||
|
System.clearProperty(ES_ENFORCE_BOOTSTRAP_CHECKS);
|
||||||
|
} else {
|
||||||
|
System.setProperty(ES_ENFORCE_BOOTSTRAP_CHECKS, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue