diff --git a/server/src/main/java/org/elasticsearch/common/settings/KeyStoreWrapper.java b/server/src/main/java/org/elasticsearch/common/settings/KeyStoreWrapper.java index 3a8a06949b29c..eee45743ee32e 100644 --- a/server/src/main/java/org/elasticsearch/common/settings/KeyStoreWrapper.java +++ b/server/src/main/java/org/elasticsearch/common/settings/KeyStoreWrapper.java @@ -166,6 +166,13 @@ private KeyStoreWrapper(int formatVersion, boolean hasPassword, byte[] dataBytes this.dataBytes = dataBytes; } + /** + * Get the metadata format version for the keystore + **/ + public int getFormatVersion() { + return formatVersion; + } + /** Returns a path representing the ES keystore in the given config dir. */ public static Path keystorePath(Path configDir) { return configDir.resolve(KEYSTORE_FILENAME); @@ -593,8 +600,10 @@ private void ensureOpen() { @Override public synchronized void close() { this.closed = true; - for (Entry entry : entries.get().values()) { - Arrays.fill(entry.bytes, (byte)0); + if (null != entries.get() && entries.get().isEmpty() == false) { + for (Entry entry : entries.get().values()) { + Arrays.fill(entry.bytes, (byte) 0); + } } } } diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/FIPS140JKSKeystoreBootstrapCheck.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/FIPS140JKSKeystoreBootstrapCheck.java new file mode 100644 index 0000000000000..4a2c7b97195eb --- /dev/null +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/FIPS140JKSKeystoreBootstrapCheck.java @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.xpack.security; + +import org.elasticsearch.bootstrap.BootstrapCheck; +import org.elasticsearch.bootstrap.BootstrapContext; +import org.elasticsearch.common.settings.Settings; + + +public class FIPS140JKSKeystoreBootstrapCheck implements BootstrapCheck { + + private final boolean fipsModeEnabled; + + FIPS140JKSKeystoreBootstrapCheck(Settings settings) { + this.fipsModeEnabled = Security.FIPS_MODE_ENABLED.get(settings); + } + + /** + * Test if the node fails the check. + * + * @param context the bootstrap context + * @return the result of the bootstrap check + */ + @Override + public BootstrapCheckResult check(BootstrapContext context) { + + if (fipsModeEnabled) { + final Settings settings = context.settings; + Settings keystoreTypeSettings = settings.filter(k -> k.endsWith("keystore.type")) + .filter(k -> settings.get(k).equalsIgnoreCase("jks")); + if (keystoreTypeSettings.isEmpty() == false) { + return BootstrapCheckResult.failure("JKS Keystores cannot be used in a FIPS 140 compliant JVM. Please " + + "revisit [" + keystoreTypeSettings.toDelimitedString(',') + "] settings"); + } + // Default Keystore type is JKS if not explicitly set + Settings keystorePathSettings = settings.filter(k -> k.endsWith("keystore.path")) + .filter(k -> settings.hasValue(k.replace(".path", ".type")) == false); + if (keystorePathSettings.isEmpty() == false) { + return BootstrapCheckResult.failure("JKS Keystores cannot be used in a FIPS 140 compliant JVM. Please " + + "revisit [" + keystorePathSettings.toDelimitedString(',') + "] settings"); + } + + } + return BootstrapCheckResult.success(); + } + + @Override + public boolean alwaysEnforce() { + return fipsModeEnabled; + } +} diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/FIPS140PasswordHashingAlgorithmBootstrapCheck.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/FIPS140PasswordHashingAlgorithmBootstrapCheck.java new file mode 100644 index 0000000000000..7f6d799cf5a8e --- /dev/null +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/FIPS140PasswordHashingAlgorithmBootstrapCheck.java @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.xpack.security; + +import org.elasticsearch.bootstrap.BootstrapCheck; +import org.elasticsearch.bootstrap.BootstrapContext; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.xpack.core.XPackSettings; + +import java.util.Locale; + +public class FIPS140PasswordHashingAlgorithmBootstrapCheck implements BootstrapCheck { + + private final boolean fipsModeEnabled; + + FIPS140PasswordHashingAlgorithmBootstrapCheck(Settings settings) { + this.fipsModeEnabled = Security.FIPS_MODE_ENABLED.get(settings); + } + + /** + * Test if the node fails the check. + * + * @param context the bootstrap context + * @return the result of the bootstrap check + */ + @Override + public BootstrapCheckResult check(BootstrapContext context) { + final String selectedAlgorithm = XPackSettings.PASSWORD_HASHING_ALGORITHM.get(context.settings); + if (selectedAlgorithm.toLowerCase(Locale.ROOT).startsWith("pbkdf2") == false) { + return BootstrapCheckResult.failure("Only PBKDF2 is allowed for password hashing in a FIPS-140 JVM. Please set the " + + "appropriate value for [ " + XPackSettings.PASSWORD_HASHING_ALGORITHM.getKey() + " ] setting."); + } + return BootstrapCheckResult.success(); + } + + @Override + public boolean alwaysEnforce() { + return fipsModeEnabled; + } +} diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/FIPS140SecureSettingsBootstrapCheck.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/FIPS140SecureSettingsBootstrapCheck.java new file mode 100644 index 0000000000000..c766dd0ffaa2b --- /dev/null +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/FIPS140SecureSettingsBootstrapCheck.java @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.xpack.security; + +import org.elasticsearch.bootstrap.BootstrapCheck; +import org.elasticsearch.bootstrap.BootstrapContext; +import org.elasticsearch.common.settings.KeyStoreWrapper; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.env.Environment; + +import java.io.IOException; +import java.io.UncheckedIOException; + +public class FIPS140SecureSettingsBootstrapCheck implements BootstrapCheck { + + private final boolean fipsModeEnabled; + private final Environment environment; + + FIPS140SecureSettingsBootstrapCheck(Settings settings, Environment environment) { + this.fipsModeEnabled = Security.FIPS_MODE_ENABLED.get(settings); + this.environment = environment; + } + + /** + * Test if the node fails the check. + * + * @param context the bootstrap context + * @return the result of the bootstrap check + */ + @Override + public BootstrapCheckResult check(BootstrapContext context) { + if (fipsModeEnabled) { + try (KeyStoreWrapper secureSettings = KeyStoreWrapper.load(environment.configFile())) { + if (secureSettings != null && secureSettings.getFormatVersion() < 3) { + return BootstrapCheckResult.failure("Secure settings store is not of the latest version. Please use " + + "bin/elasticsearch-keystore create to generate a new secure settings store and migrate the secure settings there."); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + return BootstrapCheckResult.success(); + } + + @Override + public boolean alwaysEnforce() { + return fipsModeEnabled; + } +} diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java index e483ad235c5fa..acfe7f0927ebb 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java @@ -245,6 +245,8 @@ public class Security extends Plugin implements ActionPlugin, IngestPlugin, Netw DiscoveryPlugin, MapperPlugin, ExtensiblePlugin { private static final Logger logger = Loggers.getLogger(Security.class); + static final Setting FIPS_MODE_ENABLED = + Setting.boolSetting("xpack.security.fips_mode.enabled", false, Property.NodeScope); static final Setting> AUDIT_OUTPUTS_SETTING = Setting.listSetting(SecurityField.setting("audit.outputs"), @@ -294,6 +296,9 @@ public Security(Settings settings, final Path configPath) { new PkiRealmBootstrapCheck(getSslService()), new TLSLicenseBootstrapCheck(), new PasswordHashingAlgorithmBootstrapCheck(), + new FIPS140SecureSettingsBootstrapCheck(settings, env), + new FIPS140JKSKeystoreBootstrapCheck(settings), + new FIPS140PasswordHashingAlgorithmBootstrapCheck(settings), new KerberosRealmBootstrapCheck(env))); checks.addAll(InternalRealms.getBootstrapChecks(settings, env)); this.bootstrapChecks = Collections.unmodifiableList(checks); @@ -578,6 +583,7 @@ public static List> getSettings(boolean transportClientMode, List