Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow node to enroll to cluster on startup #77718

Merged
merged 68 commits into from
Oct 27, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
aeec13d
Allow node to enroll to cluster on startup
jkakavas Sep 14, 2021
4c17742
Merge remote-tracking branch 'origin/master' into enroll-as-a-param
jkakavas Sep 14, 2021
f9a14b8
Add packaging tests(first of many commits)
jkakavas Sep 15, 2021
226e965
supress forbidden warning for necessary use
jkakavas Sep 15, 2021
a15b6bc
close if not null
jkakavas Sep 15, 2021
99afe27
proper settings
jkakavas Sep 15, 2021
ca0836c
Shift only when there are more args
jkakavas Sep 15, 2021
068e45d
temp
jkakavas Sep 15, 2021
be92ca1
correct mock esNode ssl config to return the entire chain
jkakavas Sep 15, 2021
ba11466
Merge remote-tracking branch 'origin/master' into enroll-as-a-param
jkakavas Sep 15, 2021
5c0c1f0
fixes
jkakavas Sep 15, 2021
062cb85
more fixes
jkakavas Sep 15, 2021
2f3c337
final fixes
jkakavas Sep 16, 2021
74ef40b
(mostly) fix batch startup script
jkakavas Sep 16, 2021
140d2c4
Merge remote-tracking branch 'origin/master' into enroll-as-a-param
jkakavas Sep 16, 2021
fb8686c
update batch file
jkakavas Sep 16, 2021
b480b4e
feedback
jkakavas Sep 19, 2021
7941370
adjust test
jkakavas Sep 20, 2021
8ca65fa
Call CLI tool from batch script
jkakavas Sep 20, 2021
2d8b395
Merge remote-tracking branch 'origin/master' into enroll-as-a-param
jkakavas Sep 21, 2021
f631474
change how we populate SANs
jkakavas Sep 21, 2021
1efbc06
Merge remote-tracking branch 'origin/master' into enroll-as-a-param
jkakavas Sep 21, 2021
0c1fee9
non null hostnames
jkakavas Sep 22, 2021
345329e
Merge remote-tracking branch 'origin/master' into enroll-as-a-param
jkakavas Oct 18, 2021
36fc590
Scope down the PR to just allow users to pass the enrollment-token on…
jkakavas Oct 19, 2021
fd14936
cleanup qa
jkakavas Oct 19, 2021
6dbb3fa
remove redundant testing
jkakavas Oct 19, 2021
5dbfba3
Revert "remove redundant testing"
jkakavas Oct 19, 2021
2daabc5
remove redundant test - correctly this time
jkakavas Oct 19, 2021
caa5880
reintroduce a simpler version of packaging tests
jkakavas Oct 19, 2021
d6018d3
Merge remote-tracking branch 'origin/master' into enroll-as-a-param
jkakavas Oct 19, 2021
14b679a
windows tests
jkakavas Oct 19, 2021
881d001
use EnrollmentToken class in tests
jkakavas Oct 19, 2021
1758026
spotless
jkakavas Oct 19, 2021
026341d
spotless
jkakavas Oct 19, 2021
c6f123e
fix token param checking
jkakavas Oct 20, 2021
bfbfee0
Merge remote-tracking branch 'origin/master' into enroll-as-a-param
jkakavas Oct 20, 2021
4222f7c
Fix test
jkakavas Oct 20, 2021
a3cae59
Adjust startup scripts
jkakavas Oct 20, 2021
7331c71
cleanup
jkakavas Oct 20, 2021
2d04043
mute test on windows
jkakavas Oct 20, 2021
aebba35
Merge remote-tracking branch 'origin/master' into enroll-as-a-param
jkakavas Oct 20, 2021
3643d72
mute on windows
jkakavas Oct 20, 2021
4198cc8
change test while figuring the cause
jkakavas Oct 20, 2021
573add3
Fix argument handling
jkakavas Oct 21, 2021
38623c6
Merge remote-tracking branch 'origin/master' into enroll-as-a-param
jkakavas Oct 21, 2021
0ced955
remove code that can wait until the follow-up PR for the CLI tool
jkakavas Oct 21, 2021
9efae09
Merge remote-tracking branch 'origin/master' into enroll-as-a-param
jkakavas Oct 23, 2021
48b7569
Use enrollment token on startup in tests
jkakavas Oct 23, 2021
d0e9e6c
add failure information output in verbose mode
jkakavas Oct 24, 2021
a3c9a21
a space is not a -
jkakavas Oct 24, 2021
18ed565
account for the fact that the second node binds to 9201 in Enrollment…
jkakavas Oct 24, 2021
f09cba0
test
jkakavas Oct 24, 2021
347e4b9
Ugly hacks are ugly
jkakavas Oct 24, 2021
d8729ce
tightly scope try blocks
jkakavas Oct 25, 2021
7693119
address feedback
jkakavas Oct 25, 2021
1138134
dissallow multiple --enrollment-token paramaters
jkakavas Oct 26, 2021
ee03466
Generate a CA and sign transport certificates with this
jkakavas Oct 26, 2021
0ca60d1
Merge remote-tracking branch 'origin/master' into enroll-as-a-param
jkakavas Oct 26, 2021
e00aa95
Transport CA cert needs to be transmitted to all nodes to be used in …
jkakavas Oct 26, 2021
491dbf5
disallow multiple --enrollment-token params on windows too
jkakavas Oct 26, 2021
d7ba414
print to stderr on windows
jkakavas Oct 26, 2021
5859676
windows exit code handling is totally bonkers
jkakavas Oct 26, 2021
10fec73
stderr
jkakavas Oct 26, 2021
631d22c
fix test
jkakavas Oct 26, 2021
b635b96
fix test
jkakavas Oct 26, 2021
eaeafe8
Merge remote-tracking branch 'origin/master' into enroll-as-a-param
jkakavas Oct 27, 2021
5b796df
merge woes
jkakavas Oct 27, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 24 additions & 11 deletions distribution/src/bin/elasticsearch
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,20 @@ source "`dirname "$0"`"/elasticsearch-env

CHECK_KEYSTORE=true
DAEMONIZE=false
for option in "$@"; do
case "$option" in
-h|--help|-V|--version)
CHECK_KEYSTORE=false
;;
-d|--daemonize)
DAEMONIZE=true
;;
esac
ENROLL_TO_CLUSTER=false
# Store original arg array as we will be shifting through it below
ARG_LIST=($@)
while [ $# -gt 0 ]; do
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed the way we are iterating over the args, so that we can capture the value of the enrollment token. We need this so that we can remove it from the args array that we end up passing to elasticserach process to start in the end, as elasticsearch will not allow for unrecognized options.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious about why you iterate through $@ here (effectively), rather than $ARG_LIST.
If you just iterated through ARG_LIST you could remove the enrollment token in a single pass rather than needing to iterate again later.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm using SHIFT in this iteration so that I can recognize the named arg that comes after --enrollment-token. If Iwas to iterate over ARG_LIST , I'd be removing args from it while iterating so I wouldn't be able to use it below.

Or, I am missing your point, let me know :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not important, but you could do something like this:

for i in "${!ARG_LIST[@]}"; do
  case "${ARG_LIST[i]}" in
    --enrollment-token)
      ENROLL_TO_CLUSTER=true
      ATTEMPT_SECURITY_AUTO_CONFIG=false
      unset 'ARG_LIST[i]'
      let i++
      ENROLLMENT_TOKEN="${ARG_LIST[i]}"
      unset 'ARG_LIST[i]'
      ;;
    -h|--help|-V|--version)
      CHECK_KEYSTORE=false
      ATTEMPT_SECURITY_AUTO_CONFIG=false
      ;;
    -d|--daemonize)
      DAEMONIZE=true
      ;;
  esac
done

if [[ $1 == "--enrollment-token" ]]; then
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally, I would have stuck with a case rather than a series of if/elif

ENROLL_TO_CLUSTER=true
ENROLLMENT_TOKEN="$2"
jkakavas marked this conversation as resolved.
Show resolved Hide resolved
shift
elif [[ $1 == "-h" || $1 == "--help" || $1 == "-V" || $1 == "--version" ]]; then
CHECK_KEYSTORE=false
elif [[ $1 == "-d" || $1 == "--daemonize" ]]; then
DAEMONIZE=true
fi
shift
done

if [ -z "$ES_TMPDIR" ]; then
Expand All @@ -45,6 +50,14 @@ then
fi
fi

if [[ $ENROLL_TO_CLUSTER = true ]]; then
ES_MAIN_CLASS=org.elasticsearch.xpack.security.cli.EnrollNodeToCluster \
jkakavas marked this conversation as resolved.
Show resolved Hide resolved
ES_ADDITIONAL_SOURCES="x-pack-env;x-pack-security-env" \
ES_ADDITIONAL_CLASSPATH_DIRECTORIES=lib/tools/security-cli \
"`dirname "$0"`"/elasticsearch-cli \
mark-vieira marked this conversation as resolved.
Show resolved Hide resolved
--enrollment-token "$ENROLLMENT_TOKEN" <<<"$KEYSTORE_PASSWORD"
fi

# The JVM options parser produces the final JVM options to start Elasticsearch.
# It does this by incorporating JVM options in the following way:
# - first, system JVM options are applied (these are hardcoded options in the
Expand All @@ -67,7 +80,7 @@ if [[ $DAEMONIZE = false ]]; then
-Des.bundled_jdk="$ES_BUNDLED_JDK" \
-cp "$ES_CLASSPATH" \
org.elasticsearch.bootstrap.Elasticsearch \
"$@" <<<"$KEYSTORE_PASSWORD"
"${ARG_LIST[@]}" <<<"$KEYSTORE_PASSWORD"
else
exec \
"$JAVA" \
Expand All @@ -80,7 +93,7 @@ else
-Des.bundled_jdk="$ES_BUNDLED_JDK" \
-cp "$ES_CLASSPATH" \
org.elasticsearch.bootstrap.Elasticsearch \
"$@" \
"${ARG_LIST[@]}" \
<<<"$KEYSTORE_PASSWORD" &
retval=$?
pid=$!
Expand Down
31 changes: 23 additions & 8 deletions distribution/src/bin/elasticsearch.bat
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ setlocal enableextensions

SET params='%*'
SET checkpassword=Y
SET enrolltocluster=N

:loop
FOR /F "usebackq tokens=1* delims= " %%A IN (!params!) DO (
Expand Down Expand Up @@ -33,6 +34,12 @@ FOR /F "usebackq tokens=1* delims= " %%A IN (!params!) DO (
SET checkpassword=N
)

IF "!current!" == "--enrollment-token" (
SHIFT
SET enrolltocluster=Y
SET enrollmenttoken=%~1
)

IF "!silent!" == "Y" (
SET nopauseonerror=Y
) ELSE (
Expand Down Expand Up @@ -68,6 +75,22 @@ IF "%checkpassword%"=="Y" (
)
)

rem windows batch pipe will choke on special characters in strings
SET KEYSTORE_PASSWORD=!KEYSTORE_PASSWORD:^^=^^^^!
SET KEYSTORE_PASSWORD=!KEYSTORE_PASSWORD:^&=^^^&!
SET KEYSTORE_PASSWORD=!KEYSTORE_PASSWORD:^|=^^^|!
SET KEYSTORE_PASSWORD=!KEYSTORE_PASSWORD:^<=^^^<!
SET KEYSTORE_PASSWORD=!KEYSTORE_PASSWORD:^>=^^^>!
SET KEYSTORE_PASSWORD=!KEYSTORE_PASSWORD:^\=^^^\!

IF "%enrolltocluster%"=="Y" ()
SET ES_MAIN_CLASS=org.elasticsearch.xpack.security.cli.EnrollNodeToCluster
SET ES_ADDITIONAL_SOURCES=x-pack-env;x-pack-security-env
SET ES_ADDITIONAL_CLASSPATH_DIRECTORIES=lib/tools/security-cli
ECHO.!KEYSTORE_PASSWORD!|CALL "%~dp0elasticsearch-cli.bat" --enrollment-token %enrollmenttoken%
jkakavas marked this conversation as resolved.
Show resolved Hide resolved
|| GOTO EXIT
)

if not defined ES_TMPDIR (
for /f "tokens=* usebackq" %%a in (`CALL %JAVA% -cp "!ES_CLASSPATH!" "org.elasticsearch.tools.launchers.TempDirectory"`) do set ES_TMPDIR=%%a
)
Expand All @@ -89,14 +112,6 @@ if "%MAYBE_JVM_OPTIONS_PARSER_FAILED%" == "jvm_options_parser_failed" (
exit /b 1
)

rem windows batch pipe will choke on special characters in strings
SET KEYSTORE_PASSWORD=!KEYSTORE_PASSWORD:^^=^^^^!
SET KEYSTORE_PASSWORD=!KEYSTORE_PASSWORD:^&=^^^&!
SET KEYSTORE_PASSWORD=!KEYSTORE_PASSWORD:^|=^^^|!
SET KEYSTORE_PASSWORD=!KEYSTORE_PASSWORD:^<=^^^<!
SET KEYSTORE_PASSWORD=!KEYSTORE_PASSWORD:^>=^^^>!
SET KEYSTORE_PASSWORD=!KEYSTORE_PASSWORD:^\=^^^\!

ECHO.!KEYSTORE_PASSWORD!| %JAVA% %ES_JAVA_OPTS% -Delasticsearch ^
-Des.path.home="%ES_HOME%" -Des.path.conf="%ES_PATH_CONF%" ^
-Des.distribution.flavor="%ES_DISTRIBUTION_FLAVOR%" ^
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ public class ConfigInitialNode extends EnvironmentAwareCommand {
private static final int HTTP_CERTIFICATE_DAYS = 2 * 365;
private static final int HTTP_KEY_SIZE = 4096;
private static final String TLS_CONFIG_DIR_NAME_PREFIX = "tls_auto_config_initial_node_";
static final String AUTO_CONFIGURATION_START_MARKER = "---------------------- Security auto configuration start ---------------------";
static final String AUTO_CONFIGURATION_END_MARKER = "---------------------- Security auto configuration end ---------------------";

private final OptionSpec<Void> strictOption = parser.accepts("strict", "Error if auto config cannot be performed for any reason");

Expand Down Expand Up @@ -336,6 +338,7 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th
bw.newLine();
}
bw.newLine();
bw.write(AUTO_CONFIGURATION_START_MARKER);
bw.newLine();
bw.write("###################################################################################");
bw.newLine();
Expand Down Expand Up @@ -404,6 +407,8 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th
bw.write(HttpTransportSettings.SETTING_HTTP_HOST.getKey() + ": [_local_, _site_]");
bw.newLine();
}
bw.write(AUTO_CONFIGURATION_END_MARKER);
bw.newLine();
}
});
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
import java.security.cert.X509Certificate;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashSet;
import java.util.List;
Expand All @@ -75,6 +76,8 @@
import static org.elasticsearch.common.ssl.PemUtils.parsePKCS8PemString;
import static org.elasticsearch.discovery.SettingsBasedSeedHostsProvider.DISCOVERY_SEED_HOSTS_SETTING;
import static org.elasticsearch.xpack.core.security.CommandLineHttpClient.createURL;
import static org.elasticsearch.xpack.security.cli.ConfigInitialNode.AUTO_CONFIGURATION_END_MARKER;
import static org.elasticsearch.xpack.security.cli.ConfigInitialNode.AUTO_CONFIGURATION_START_MARKER;

/**
* Configures a node to join an existing cluster with security features enabled.
Expand All @@ -84,6 +87,10 @@ public class EnrollNodeToCluster extends KeyStoreAwareCommand {
private final OptionSpec<String> enrollmentTokenParam = parser.accepts("enrollment-token", "The enrollment token to use")
.withRequiredArg()
.required();
private final OptionSpec<Void> force = parser.acceptsAll(
jkakavas marked this conversation as resolved.
Show resolved Hide resolved
List.of("f", "force"),
"Overwrite existing configuration even when this is not a new node"
);
private final BiFunction<Environment, String, CommandLineHttpClient> clientFunction;

static final String TLS_CONFIG_DIR_NAME_PREFIX = "tls_auto_config_node_";
Expand All @@ -110,8 +117,9 @@ public static void main(String[] args) throws Exception {

@Override
protected void execute(Terminal terminal, OptionSet options, Environment env) throws Exception {
jkakavas marked this conversation as resolved.
Show resolved Hide resolved
final boolean shouldForce = options.has(force);

if (Files.isDirectory(env.dataFile()) && Files.list(env.dataFile()).findAny().isPresent()) {
if (shouldForce == false && Files.isDirectory(env.dataFile()) && Files.list(env.dataFile()).findAny().isPresent()) {
throw new UserException(
ExitCodes.CONFIG,
"Aborting enrolling to cluster. It appears that this is not the first time this node starts."
Expand Down Expand Up @@ -140,8 +148,7 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th
String.format(Locale.ROOT, "Aborting enrolling to cluster. The keystore [%s] is not a readable regular file", ymlPath)
);
}

checkExistingConfiguration(env.settings());
checkExistingConfiguration(env.settings(), shouldForce);

final ZonedDateTime autoConfigDate = ZonedDateTime.now(ZoneOffset.UTC);
final String instantAutoConfigName = TLS_CONFIG_DIR_NAME_PREFIX + autoConfigDate.toInstant().getEpochSecond();
Expand Down Expand Up @@ -169,7 +176,7 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th
}

final UserPrincipal newFileOwner = Files.getOwner(instantAutoConfigDir, LinkOption.NOFOLLOW_LINKS);
if (false == newFileOwner.equals(Files.getOwner(env.configFile(), LinkOption.NOFOLLOW_LINKS))) {
if (shouldForce == false && (false == newFileOwner.equals(Files.getOwner(env.configFile(), LinkOption.NOFOLLOW_LINKS)))) {
jkakavas marked this conversation as resolved.
Show resolved Hide resolved
Files.deleteIfExists(instantAutoConfigDir);
throw new UserException(ExitCodes.CONFIG, "Aborting enrolling to cluster. config dir ownership mismatch");
}
Expand Down Expand Up @@ -246,7 +253,7 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th
final PrivateKey transportKey = transport.v1();
final X509Certificate transportCert = transport.v2();

final X500Principal certificatePrincipal = new X500Principal("CN=Autogenerated by Elasticsearch");
final X500Principal certificatePrincipal = new X500Principal("CN="+System.getenv("HOSTNAME"));
// this does DNS resolve and could block
final GeneralNames subjectAltNames = getSubjectAltNames();

Expand All @@ -263,7 +270,7 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th
httpCaKey,
false,
HTTP_CERTIFICATE_DAYS,
null
"SHA256withRSA"
);
} catch (Exception e) {
try {
Expand All @@ -279,7 +286,7 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th
}

try {
fullyWriteFile(instantAutoConfigDir, HTTP_AUTOGENERATED_CA_NAME + ".crt", false, stream -> {
fullyWriteFile(instantAutoConfigDir, HTTP_AUTOGENERATED_CA_NAME + ".crt", shouldForce, stream -> {
try (
JcaPEMWriter pemWriter =
new JcaPEMWriter(new BufferedWriter(new OutputStreamWriter(stream, StandardCharsets.UTF_8)))) {
Expand Down Expand Up @@ -324,11 +331,12 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th
nodeKeystorePassword.set(new SecureString(terminal.readSecret("", KeyStoreWrapper.MAX_PASSPHRASE_LENGTH)));
return nodeKeystorePassword.get().clone();
})) {
// do not overwrite keystore entries
// do not overwrite keystore entries unless explicitly instructed to do so,
// instead expect the user to manually remove them themselves
if (nodeKeystore.getSettingNames().contains("xpack.security.transport.ssl.keystore.secure_password")
if (shouldForce == false &&
(nodeKeystore.getSettingNames().contains("xpack.security.transport.ssl.keystore.secure_password")
|| nodeKeystore.getSettingNames().contains("xpack.security.transport.ssl.truststore.secure_password")
|| nodeKeystore.getSettingNames().contains("xpack.security.http.ssl.keystore.secure_password")) {
|| nodeKeystore.getSettingNames().contains("xpack.security.http.ssl.keystore.secure_password"))) {
throw new UserException(
ExitCodes.CONFIG,
"Aborting enrolling to cluster. The node keystore contains TLS related settings already."
Expand All @@ -352,7 +360,7 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th
fullyWriteFile(
instantAutoConfigDir,
HTTP_AUTOGENERATED_KEYSTORE_NAME + ".p12",
false,
shouldForce,
stream -> httpKeystore.store(stream, httpKeystorePassword.getChars())
);
nodeKeystore.setString("xpack.security.http.ssl.keystore.secure_password", httpKeystorePassword.getChars());
Expand All @@ -373,7 +381,7 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th
fullyWriteFile(
instantAutoConfigDir,
TRANSPORT_AUTOGENERATED_KEYSTORE_NAME + ".p12",
false,
shouldForce,
stream -> transportKeystore.store(stream, transportKeystorePassword.getChars())
);
nodeKeystore.setString("xpack.security.transport.ssl.keystore.secure_password", transportKeystorePassword.getChars());
Expand Down Expand Up @@ -417,15 +425,24 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th

// We have everything, let's write to the config
try {
List<String> existingConfigLines = Files.readAllLines(ymlPath, StandardCharsets.UTF_8);
final List<String> existingConfigLines = Files.readAllLines(ymlPath, StandardCharsets.UTF_8);

fullyWriteFile(env.configFile(), "elasticsearch.yml", true, stream -> {
try (BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(stream, StandardCharsets.UTF_8))) {
// Remove previous auto-configuration
final List<String> existingConfigWithoutAutoconfiguration = Arrays.stream(
existingConfigLines.stream()
.collect(Collectors.joining(System.lineSeparator()))
.replace("(?s)" + AUTO_CONFIGURATION_START_MARKER + ".*?" + AUTO_CONFIGURATION_END_MARKER, "")
.split(System.lineSeparator())
).collect(Collectors.toList());
// start with the existing config lines
for (String line : existingConfigLines) {
for (String line : existingConfigWithoutAutoconfiguration) {
bw.write(line);
bw.newLine();
}
bw.newLine();
bw.write(AUTO_CONFIGURATION_START_MARKER);
bw.newLine();
bw.write("###################################################################################");
bw.newLine();
Expand Down Expand Up @@ -507,6 +524,8 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th
bw.write(HttpTransportSettings.SETTING_HTTP_HOST.getKey() + ": [_local_, _site_]");
bw.newLine();
}
bw.write(AUTO_CONFIGURATION_END_MARKER);
bw.newLine();
}
});
} catch (Exception e) {
Expand Down Expand Up @@ -634,16 +653,18 @@ private Tuple<PrivateKey, X509Certificate> parseKeyCertFromPem(String pemFormatt
}
}

void checkExistingConfiguration(Settings settings) throws UserException {
if (XPackSettings.SECURITY_ENABLED.exists(settings)) {
throw new UserException(ExitCodes.CONFIG, "Aborting enrolling to cluster. It appears that security is already configured.");
}
void checkExistingConfiguration(Settings settings, boolean shouldForce) throws UserException {
if (XPackSettings.ENROLLMENT_ENABLED.exists(settings) && false == XPackSettings.ENROLLMENT_ENABLED.get(settings)) {
throw new UserException(ExitCodes.CONFIG, "Aborting enrolling to cluster. Enrollment is explicitly disabled.");
}
if (false == settings.getByPrefix(XPackSettings.TRANSPORT_SSL_PREFIX).isEmpty() ||
false == settings.getByPrefix(XPackSettings.HTTP_SSL_PREFIX).isEmpty()) {
throw new UserException(ExitCodes.CONFIG, "Aborting enrolling to cluster. It appears that TLS is already configured.");
if (shouldForce == false) {
if (XPackSettings.SECURITY_ENABLED.exists(settings)) {
throw new UserException(ExitCodes.CONFIG, "Aborting enrolling to cluster. It appears that security is already configured.");
}
if (false == settings.getByPrefix(XPackSettings.TRANSPORT_SSL_PREFIX).isEmpty()
|| false == settings.getByPrefix(XPackSettings.HTTP_SSL_PREFIX).isEmpty()) {
throw new UserException(ExitCodes.CONFIG, "Aborting enrolling to cluster. It appears that TLS is already configured.");
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,20 @@ public void testEnrollmentExitsOnAlreadyConfiguredNode() throws Exception {
assertAutoConfigurationFilesNotCreated();
}

public void testEnrollmentDoesNotExitOnAlreadyConfiguredNodeIfForced() throws Exception {
final EnrollmentToken enrollmentToken = new EnrollmentToken(
randomAlphaOfLength(12),
randomAlphaOfLength(12),
Version.CURRENT.toString(),
List.of("127.0.0.1:9200")
);
Path dataDir = Files.createDirectory(jimfs.getPath("eshome").resolve("data"));
Files.createFile(dataDir.resolve("foo"));
settings = Settings.builder().put(settings).put("path.data", dataDir).put("xpack.security.enrollment.enabled", true).build();
execute("--enrollment-token", enrollmentToken.getEncoded(), randomFrom("-f", "--force"));
assertAutoConfigurationFilesCreated();
}

public void testEnrollmentExitsOnInvalidEnrollmentToken() throws Exception {
final EnrollmentToken enrollmentToken = new EnrollmentToken(
randomAlphaOfLength(12),
Expand Down Expand Up @@ -231,6 +245,28 @@ public void testEnrollmentExitsOnExistingSecurityConfiguration() throws Exceptio
assertAutoConfigurationFilesNotCreated();
}

public void testEnrollmentDoesNotExitOnExistingSecurityConfigurationIfForced() throws Exception {
settings = Settings.builder().put(settings).put("xpack.security.enabled", true).build();
final EnrollmentToken enrollmentToken = new EnrollmentToken(
randomAlphaOfLength(12),
randomAlphaOfLength(12),
Version.CURRENT.toString(),
List.of("127.0.0.1:9200")
);
execute("--enrollment-token", enrollmentToken.getEncoded(), randomFrom("-f", "--force"));
final Path autoConfigDir = assertAutoConfigurationFilesCreated();
assertTransportKeystore(
autoConfigDir.resolve(TRANSPORT_AUTOGENERATED_KEYSTORE_NAME + ".p12"),
Paths.get(getClass().getResource("transport.key").toURI()).toAbsolutePath().normalize(),
Paths.get(getClass().getResource("transport.crt").toURI()).toAbsolutePath().normalize()
);
assertHttpKeystore(
autoConfigDir.resolve(HTTP_AUTOGENERATED_KEYSTORE_NAME + ".p12"),
Paths.get(getClass().getResource("http_ca.key").toURI()).toAbsolutePath().normalize(),
Paths.get(getClass().getResource("http_ca.crt").toURI()).toAbsolutePath().normalize()
);
}

public void testEnrollmentExitsOnExistingTlsConfiguration() throws Exception {
settings = Settings.builder()
.put(settings)
Expand Down