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;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* running in production and all bootstrap checks must pass.
|
||||
* We enforce bootstrap checks once a node has the transport protocol bound to a non-loopback interface or if the system property {@code
|
||||
* 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 {
|
||||
|
||||
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 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 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 checks the checks to execute
|
||||
|
@ -101,13 +110,31 @@ final class BootstrapChecks {
|
|||
final List<String> errors = 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) {
|
||||
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) {
|
||||
if (check.check()) {
|
||||
if (!enforceLimits && !check.alwaysEnforce()) {
|
||||
if (!(enforceLimits || enforceBootstrapChecks) && !check.alwaysEnforce()) {
|
||||
ignoredErrors.add(check.errorMessage());
|
||||
} else {
|
||||
errors.add(check.errorMessage());
|
||||
|
@ -127,7 +154,6 @@ final class BootstrapChecks {
|
|||
errors.stream().map(IllegalStateException::new).forEach(ne::addSuppressed);
|
||||
throw ne;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void log(final Logger logger, final String error) {
|
||||
|
@ -140,9 +166,9 @@ final class BootstrapChecks {
|
|||
* @param boundTransportAddress the node network bindings
|
||||
* @return {@code true} if the checks should be enforced
|
||||
*/
|
||||
static boolean enforceLimits(BoundTransportAddress boundTransportAddress) {
|
||||
Predicate<TransportAddress> isLoopbackOrLinkLocalAddress = t -> t.address().getAddress().isLinkLocalAddress()
|
||||
|| t.address().getAddress().isLoopbackAddress();
|
||||
static boolean enforceLimits(final BoundTransportAddress boundTransportAddress) {
|
||||
Predicate<TransportAddress> isLoopbackOrLinkLocalAddress =
|
||||
t -> t.address().getAddress().isLinkLocalAddress() || t.address().getAddress().isLoopbackAddress();
|
||||
return !(Arrays.stream(boundTransportAddress.boundAddresses()).allMatch(isLoopbackOrLinkLocalAddress) &&
|
||||
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.when;
|
||||
|
||||
public class BootstrapCheckTests extends ESTestCase {
|
||||
public class BootstrapChecksTests extends ESTestCase {
|
||||
|
||||
public void testNonProductionMode() throws NodeValidationException {
|
||||
// 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
|
||||
<<modules-http,`http.host`>> and <<modules-transport,`transport.host`>>;
|
||||
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
|
||||
|
||||
|
|
|
@ -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