-
Notifications
You must be signed in to change notification settings - Fork 24.7k
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
Refactor FIPS BootstrapChecks to simple checks #47499
Changes from 1 commit
d5bf8e7
1843c70
2816d5f
57d88a3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -15,6 +15,7 @@ | |||||||||||||||||
import org.elasticsearch.action.support.ActionFilter; | ||||||||||||||||||
import org.elasticsearch.action.support.DestructiveOperations; | ||||||||||||||||||
import org.elasticsearch.bootstrap.BootstrapCheck; | ||||||||||||||||||
import org.elasticsearch.bootstrap.JavaVersion; | ||||||||||||||||||
import org.elasticsearch.client.Client; | ||||||||||||||||||
import org.elasticsearch.cluster.ClusterState; | ||||||||||||||||||
import org.elasticsearch.cluster.metadata.IndexMetaData; | ||||||||||||||||||
|
@@ -247,6 +248,7 @@ | |||||||||||||||||
import java.util.Arrays; | ||||||||||||||||||
import java.util.Collection; | ||||||||||||||||||
import java.util.Collections; | ||||||||||||||||||
import java.util.EnumSet; | ||||||||||||||||||
import java.util.HashMap; | ||||||||||||||||||
import java.util.HashSet; | ||||||||||||||||||
import java.util.List; | ||||||||||||||||||
|
@@ -273,6 +275,8 @@ public class Security extends Plugin implements ActionPlugin, IngestPlugin, Netw | |||||||||||||||||
DiscoveryPlugin, MapperPlugin, ExtensiblePlugin { | ||||||||||||||||||
|
||||||||||||||||||
private static final Logger logger = LogManager.getLogger(Security.class); | ||||||||||||||||||
static final EnumSet<License.OperationMode> FIPS_ALLOWED_LICENSE_OPERATION_MODES = | ||||||||||||||||||
EnumSet.of(License.OperationMode.PLATINUM, License.OperationMode.TRIAL); | ||||||||||||||||||
|
||||||||||||||||||
private final Settings settings; | ||||||||||||||||||
private final Environment env; | ||||||||||||||||||
|
@@ -303,19 +307,15 @@ public Security(Settings settings, final Path configPath) { | |||||||||||||||||
this.env = new Environment(settings, configPath); | ||||||||||||||||||
this.enabled = XPackSettings.SECURITY_ENABLED.get(settings); | ||||||||||||||||||
if (enabled) { | ||||||||||||||||||
runStartupChecks(settings); | ||||||||||||||||||
runStartupChecks(settings, getLicenseState()); | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is license state meaningful at this point during node start up? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see your point. Should we just remove checks for license on node startup and only leave |
||||||||||||||||||
// we load them all here otherwise we can't access secure settings since they are closed once the checks are | ||||||||||||||||||
// fetched | ||||||||||||||||||
final List<BootstrapCheck> checks = new ArrayList<>(); | ||||||||||||||||||
checks.addAll(Arrays.asList( | ||||||||||||||||||
new ApiKeySSLBootstrapCheck(), | ||||||||||||||||||
new TokenSSLBootstrapCheck(), | ||||||||||||||||||
new PkiRealmBootstrapCheck(getSslService()), | ||||||||||||||||||
new TLSLicenseBootstrapCheck(), | ||||||||||||||||||
new FIPS140SecureSettingsBootstrapCheck(settings, env), | ||||||||||||||||||
new FIPS140JKSKeystoreBootstrapCheck(), | ||||||||||||||||||
new FIPS140PasswordHashingAlgorithmBootstrapCheck(), | ||||||||||||||||||
new FIPS140LicenseBootstrapCheck())); | ||||||||||||||||||
new TLSLicenseBootstrapCheck())); | ||||||||||||||||||
checks.addAll(InternalRealms.getBootstrapChecks(settings, env)); | ||||||||||||||||||
this.bootstrapChecks = Collections.unmodifiableList(checks); | ||||||||||||||||||
Automatons.updateConfiguration(settings); | ||||||||||||||||||
|
@@ -326,8 +326,11 @@ public Security(Settings settings, final Path configPath) { | |||||||||||||||||
|
||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
private static void runStartupChecks(Settings settings) { | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this no longer static? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Leftover change from uncompleted attempt I'd guess, wasn't left on purpose |
||||||||||||||||||
private void runStartupChecks(Settings settings, XPackLicenseState licenseState) { | ||||||||||||||||||
validateRealmSettings(settings); | ||||||||||||||||||
if (XPackSettings.FIPS_MODE_ENABLED.get(settings)) { | ||||||||||||||||||
validateForFips(settings, licenseState); | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
// overridable by tests | ||||||||||||||||||
|
@@ -830,6 +833,41 @@ static void validateRealmSettings(Settings settings) { | |||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
static void validateForFips(Settings settings, XPackLicenseState licenseState) { | ||||||||||||||||||
final List<String> validationErrors = new ArrayList<>(); | ||||||||||||||||||
Settings keystoreTypeSettings = settings.filter(k -> k.endsWith("keystore.type")) | ||||||||||||||||||
.filter(k -> settings.get(k).equalsIgnoreCase("jks")); | ||||||||||||||||||
if (keystoreTypeSettings.isEmpty() == false) { | ||||||||||||||||||
validationErrors.add("JKS Keystores cannot be used in a FIPS 140 compliant JVM. Please " + | ||||||||||||||||||
"revisit [" + keystoreTypeSettings.toDelimitedString(',') + "] settings"); | ||||||||||||||||||
} | ||||||||||||||||||
Settings keystorePathSettings = settings.filter(k -> k.endsWith("keystore.path")) | ||||||||||||||||||
.filter(k -> settings.hasValue(k.replace(".path", ".type")) == false); | ||||||||||||||||||
// Default Keystore type is JKS in JDK8 and PKCS12 in > JDK9 if not explicitly set | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not the case. We explicitly set the keystore type to JKS, and don't depend on the JVM default. Lines 245 to 252 in 9945d5c
Perhaps we should call |
||||||||||||||||||
if (keystorePathSettings.isEmpty() == false && JavaVersion.current().compareTo(JavaVersion.parse("9")) < 0) { | ||||||||||||||||||
validationErrors.add("JKS Keystores cannot be used in a FIPS 140 compliant JVM. Please " + | ||||||||||||||||||
"revisit [" + keystorePathSettings.toDelimitedString(',') + "] settings"); | ||||||||||||||||||
} | ||||||||||||||||||
final String selectedAlgorithm = XPackSettings.PASSWORD_HASHING_ALGORITHM.get(settings); | ||||||||||||||||||
if (selectedAlgorithm.toLowerCase(Locale.ROOT).startsWith("pbkdf2") == false) { | ||||||||||||||||||
validationErrors.add("Only PBKDF2 is allowed for password hashing in a FIPS 140 JVM. Please set the " + | ||||||||||||||||||
"appropriate value for [ " + XPackSettings.PASSWORD_HASHING_ALGORITHM.getKey() + " ] setting."); | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
if (licenseState != null && FIPS_ALLOWED_LICENSE_OPERATION_MODES.contains(licenseState.getOperationMode()) == false) { | ||||||||||||||||||
validationErrors.add("FIPS mode is only allowed with a Platinum or Trial license"); | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The error message should be built from |
||||||||||||||||||
} | ||||||||||||||||||
if (validationErrors.isEmpty() == false) { | ||||||||||||||||||
final StringBuilder sb = new StringBuilder(); | ||||||||||||||||||
sb.append("Validation for FIPS 140 mode failed: \n"); | ||||||||||||||||||
int index = 0; | ||||||||||||||||||
for (String error : validationErrors) { | ||||||||||||||||||
sb.append(++index).append(": ").append(error).append(";\n"); | ||||||||||||||||||
} | ||||||||||||||||||
throw new IllegalArgumentException(sb.toString()); | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
@Override | ||||||||||||||||||
public List<TransportInterceptor> getTransportInterceptors(NamedWriteableRegistry namedWriteableRegistry, ThreadContext threadContext) { | ||||||||||||||||||
if (enabled == false) { // don't register anything if we are not enabled | ||||||||||||||||||
|
@@ -998,7 +1036,7 @@ public void accept(DiscoveryNode node, ClusterState state) { | |||||||||||||||||
if (inFipsMode) { | ||||||||||||||||||
License license = LicenseService.getLicense(state.metaData()); | ||||||||||||||||||
if (license != null && | ||||||||||||||||||
FIPS140LicenseBootstrapCheck.ALLOWED_LICENSE_OPERATION_MODES.contains(license.operationMode()) == false) { | ||||||||||||||||||
FIPS_ALLOWED_LICENSE_OPERATION_MODES.contains(license.operationMode()) == false) { | ||||||||||||||||||
throw new IllegalStateException("FIPS mode cannot be used with a [" + license.operationMode() + | ||||||||||||||||||
"] license. It is only allowed with a Platinum or Trial license."); | ||||||||||||||||||
|
||||||||||||||||||
|
This file was deleted.
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we move this to
XPackLicenseState
? We ought to keep licensing decisions centralised.