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

Refactored FIPS Checks into a Single Class (For Issue #34772) #36677

Closed
wants to merge 11 commits into from
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,4 @@ html_docs
# random old stuff that we should look at the necessity of...
/tmp/
eclipse-build
/bin/
jkakavas marked this conversation as resolved.
Show resolved Hide resolved
35 changes: 35 additions & 0 deletions server/src/main/java/org/elasticsearch/bootstrap/FIPSContext.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* 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.elasticsearch.common.settings.Settings;

/**
* Context that is passed to every bootstrap check to make decisions on.
*/
public class FIPSContext {
Copy link
Member

Choose a reason for hiding this comment

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

I don't think we actually need a FIPSContext class since we only encapsulate the settings here ( as opposed to i.e. a BootstrapContext that encapsulates settings and metadata. )

/**
* The nodes settings
*/
public final Settings settings;

public FIPSContext(Settings settings) {
this.settings = settings;
}
}

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
@@ -0,0 +1,124 @@
package org.elasticsearch.xpack.security;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.EnumSet;
import java.util.Locale;

import org.elasticsearch.bootstrap.FIPSContext;
import org.elasticsearch.common.settings.KeyStoreWrapper;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.license.License;
import org.elasticsearch.xpack.core.XPackSettings;

public class FIPSChecks implements FIPSInterface {

private License.OperationMode licenseMode;

static final EnumSet<License.OperationMode> ALLOWED_LICENSE_OPERATION_MODES =
EnumSet.of(License.OperationMode.PLATINUM, License.OperationMode.TRIAL);

FIPSChecks(License.OperationMode mode){
licenseMode = mode;
}

FIPSChecks(){
licenseMode = null;
}

public License.OperationMode getLicenseOperationMode(){
return licenseMode;
}

@Override
public FIPSCheckResult check(FIPSContext context, Environment env) {
FIPSCheckResult check1 = keystoreCheck(context);
Copy link
Member

Choose a reason for hiding this comment

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

It's better to use variable names with context so for example check1 could be keystoreCheck, etc.

FIPSCheckResult check2 = licenseCheck(context);
FIPSCheckResult check3 = passwordHashingAlgorithmCheck(context);
FIPSCheckResult check4 = secureSettingsCheck(context, env);

if(check1.isSuccess() && check2.isSuccess() && check3.isSuccess() && check4.isSuccess()) {
return FIPSCheckResult.success();
}

else {
Copy link
Member

Choose a reason for hiding this comment

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

we should probably consolidate the error messages from the results so that we don't only present the first (from a seemingly arbitrary check order) error that was encountered to the user

Copy link
Author

Choose a reason for hiding this comment

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

How do you suggest we do it then?

Copy link
Member

Choose a reason for hiding this comment

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

I was thinking something similar to how we use addValidationError

if(check1.isFailure()) {
return check1;
}

if(check2.isFailure()) {
return check2;
}

if(check3.isFailure()) {
return check3;
}

if(check4.isFailure()) {
return check4;
}
}

return null;
}

public FIPSCheckResult keystoreCheck(FIPSContext context) {

if (XPackSettings.FIPS_MODE_ENABLED.get(context.settings)) {
Copy link
Member

Choose a reason for hiding this comment

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

Depending on other changes/suggestions, we should probably conditionally apply the FipsChecks if FIPS_MODE_ENABLED already in Security.java so that we don't have to check the settings value each time.

Copy link
Author

@LuisAlvelaMendes LuisAlvelaMendes Dec 17, 2018

Choose a reason for hiding this comment

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

Do you mean add an if clause like
if (XPackSettings.FIPS_MODE_ENABLED.get(context.settings)) {} surrounding it? I'm not sure what impact that would have

Copy link
Member

Choose a reason for hiding this comment

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

I meant that we can have the if clause once in Security.java and call check() on all the checks (or the one check() if we decide to implement FipsChecks in a simpler manner ) IF fips_mode is set

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 FIPSCheckResult.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 FIPSCheckResult.failure("JKS Keystores cannot be used in a FIPS 140 compliant JVM. Please " +
"revisit [" + keystorePathSettings.toDelimitedString(',') + "] settings");
}
}

return FIPSCheckResult.success();
}

public FIPSCheckResult licenseCheck(FIPSContext context) {
if (XPackSettings.FIPS_MODE_ENABLED.get(context.settings)) {
if (licenseMode != null && ALLOWED_LICENSE_OPERATION_MODES.contains(licenseMode) == false) {
return FIPSCheckResult.failure("FIPS mode is only allowed with a Platinum or Trial license");
}
}

return FIPSCheckResult.success();
}

public FIPSCheckResult passwordHashingAlgorithmCheck(FIPSContext context) {
if (XPackSettings.FIPS_MODE_ENABLED.get(context.settings)) {
final String selectedAlgorithm = XPackSettings.PASSWORD_HASHING_ALGORITHM.get(context.settings);
if (selectedAlgorithm.toLowerCase(Locale.ROOT).startsWith("pbkdf2") == false) {
return FIPSCheckResult.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 FIPSCheckResult.success();
}

public FIPSCheckResult secureSettingsCheck(FIPSContext context, Environment environment) {
if (XPackSettings.FIPS_MODE_ENABLED.get(context.settings)) {
try (KeyStoreWrapper secureSettings = KeyStoreWrapper.load(environment.configFile())) {
if (secureSettings != null && secureSettings.getFormatVersion() < 3) {
return FIPSCheckResult.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 FIPSCheckResult.success();
}
}
Loading