Add validation for all `transport.profile.*` settings (elastic/x-pack-elasticsearch#1909)

Follow-up from elasticsearch/elastic#25508

Original commit: elastic/x-pack-elasticsearch@fe08e74ccc
This commit is contained in:
Simon Willnauer 2017-07-07 09:41:34 +02:00 committed by GitHub
parent c5012ac6e8
commit c87d9278a6
10 changed files with 295 additions and 88 deletions

View File

@ -154,6 +154,7 @@ import org.elasticsearch.xpack.security.transport.netty4.SecurityNetty4HttpServe
import org.elasticsearch.xpack.security.transport.netty4.SecurityNetty4Transport;
import org.elasticsearch.xpack.security.user.AnonymousUser;
import org.elasticsearch.xpack.ssl.SSLBootstrapCheck;
import org.elasticsearch.xpack.ssl.SSLConfigurationSettings;
import org.elasticsearch.xpack.ssl.SSLService;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
@ -179,6 +180,7 @@ import java.util.stream.Collectors;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.elasticsearch.common.settings.Setting.groupSetting;
import static org.elasticsearch.xpack.XPackSettings.HTTP_SSL_ENABLED;
public class Security implements ActionPlugin, IngestPlugin, NetworkPlugin {
@ -463,6 +465,8 @@ public class Security implements ActionPlugin, IngestPlugin, NetworkPlugin {
settingsList.add(TokenService.TOKEN_PASSPHRASE);
settingsList.add(TokenService.DELETE_INTERVAL);
settingsList.add(TokenService.DELETE_TIMEOUT);
settingsList.add(SecurityServerTransportInterceptor.TRANSPORT_TYPE_PROFILE_SETTING);
settingsList.addAll(SSLConfigurationSettings.getProfileSettings());
// hide settings
settingsList.add(Setting.listSetting(setting("hide_settings"), Collections.emptyList(), Function.identity(),

View File

@ -11,6 +11,7 @@ import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.support.DestructiveOperations;
import org.elasticsearch.common.CheckedConsumer;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.AbstractRunnable;
import org.elasticsearch.common.util.concurrent.ThreadContext;
@ -44,12 +45,24 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.function.Function;
import static org.elasticsearch.xpack.security.Security.setting;
public class SecurityServerTransportInterceptor extends AbstractComponent implements TransportInterceptor {
private static final String SETTING_NAME = "xpack.security.type";
private static final Function<String, Setting<String>> TRANSPORT_TYPE_SETTING_TEMPLATE = (key) -> new Setting<>(key,
"node", v
-> {
if (v.equals("node") || v.equals("client")) {
return v;
}
throw new IllegalArgumentException("type must be one of [client, node]");
}, Setting.Property.NodeScope);
private static final String TRANSPORT_TYPE_SETTING_KEY = "xpack.security.type";
public static final Setting<String> TRANSPORT_TYPE_PROFILE_SETTING = Setting.affixKeySetting("transport.profiles.",
TRANSPORT_TYPE_SETTING_KEY, TRANSPORT_TYPE_SETTING_TEMPLATE);
private final AuthenticationService authcService;
private final AuthorizationService authzService;
@ -154,17 +167,20 @@ public class SecurityServerTransportInterceptor extends AbstractComponent implem
Settings profileSettings = entry.getValue();
final Settings profileSslSettings = SecurityNetty4Transport.profileSslSettings(profileSettings);
final boolean extractClientCert = sslService.isSSLClientAuthEnabled(profileSslSettings, transportSSLSettings);
String type = entry.getValue().get(SETTING_NAME, "node");
String type = TRANSPORT_TYPE_SETTING_TEMPLATE.apply(TRANSPORT_TYPE_SETTING_KEY).get(entry.getValue());
switch (type) {
case "client":
profileFilters.put(entry.getKey(), new ServerTransportFilter.ClientProfile(authcService, authzService,
threadPool.getThreadContext(), extractClientCert, destructiveOperations, reservedRealmEnabled,
securityContext));
break;
default:
case "node":
profileFilters.put(entry.getKey(), new ServerTransportFilter.NodeProfile(authcService, authzService,
threadPool.getThreadContext(), extractClientCert, destructiveOperations, reservedRealmEnabled,
securityContext));
break;
default:
throw new IllegalStateException("unknown profile type: " + type);
}
}

View File

@ -23,12 +23,13 @@ import org.elasticsearch.xpack.security.audit.AuditTrailService;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import static java.util.Collections.unmodifiableMap;
import static org.elasticsearch.xpack.security.Security.setting;
@ -58,6 +59,13 @@ public class IPFilter {
public static final Setting<List<String>> TRANSPORT_FILTER_DENY_SETTING = Setting.listSetting(setting("transport.filter.deny"),
Collections.emptyList(), Function.identity(), Property.Dynamic, Property.NodeScope);
public static final Setting.AffixSetting<List<String>> PROFILE_FILTER_DENY_SETTING = Setting.affixKeySetting("transport.profiles.",
"xpack.security.filter.deny", key -> Setting.listSetting(key, Collections.emptyList(), Function.identity(),
Property.Dynamic, Property.NodeScope));
public static final Setting.AffixSetting<List<String>> PROFILE_FILTER_ALLOW_SETTING = Setting.affixKeySetting("transport.profiles.",
"xpack.security.filter.allow", key -> Setting.listSetting(key, Collections.emptyList(), Function.identity(),
Property.Dynamic, Property.NodeScope));
private static final Setting<List<String>> HTTP_FILTER_ALLOW_FALLBACK =
Setting.listSetting("transport.profiles.default.xpack.security.filter.allow", TRANSPORT_FILTER_ALLOW_SETTING, s -> s,
Property.NodeScope);
@ -96,7 +104,7 @@ public class IPFilter {
private volatile Map<String, SecurityIpFilterRule[]> rules = Collections.emptyMap();
private volatile boolean isIpFilterEnabled;
private volatile boolean isHttpFilterEnabled;
private volatile Map<String, Settings> transportGroups;
private final Set<String> profiles;
private volatile List<String> transportAllowFilter;
private volatile List<String> transportDenyFilter;
private volatile List<String> httpAllowFilter;
@ -104,6 +112,8 @@ public class IPFilter {
private final SetOnce<BoundTransportAddress> boundTransportAddress = new SetOnce<>();
private final SetOnce<BoundTransportAddress> boundHttpTransportAddress = new SetOnce<>();
private final SetOnce<Map<String, BoundTransportAddress>> profileBoundAddress = new SetOnce<>();
private final Map<String, List<String>> profileAllowRules = Collections.synchronizedMap(new HashMap<>());
private final Map<String, List<String>> profileDenyRules = Collections.synchronizedMap(new HashMap<>());
public IPFilter(final Settings settings, AuditTrailService auditTrail, ClusterSettings clusterSettings,
XPackLicenseState licenseState) {
@ -118,15 +128,22 @@ public class IPFilter {
isHttpFilterEnabled = IP_FILTER_ENABLED_HTTP_SETTING.get(settings);
isIpFilterEnabled = IP_FILTER_ENABLED_SETTING.get(settings);
this.transportGroups = TcpTransport.TRANSPORT_PROFILES_SETTING.get(settings).getAsGroups(); // this is pretty crazy that we
// allow this to be updateable!!! - we have to fix this very soon
this.profiles = settings.getGroups("transport.profiles.",true).keySet().stream().filter(k -> TcpTransport
.DEFAULT_PROFILE.equals(k) == false).collect(Collectors.toSet()); // exclude default profile -- it's handled differently
for (String profile : profiles) {
Setting<List<String>> allowSetting = PROFILE_FILTER_ALLOW_SETTING.getConcreteSettingForNamespace(profile);
profileAllowRules.put(profile, allowSetting.get(settings));
Setting<List<String>> denySetting = PROFILE_FILTER_DENY_SETTING.getConcreteSettingForNamespace(profile);
profileDenyRules.put(profile, denySetting.get(settings));
}
clusterSettings.addSettingsUpdateConsumer(IP_FILTER_ENABLED_HTTP_SETTING, this::setHttpFiltering);
clusterSettings.addSettingsUpdateConsumer(IP_FILTER_ENABLED_SETTING, this::setTransportFiltering);
clusterSettings.addSettingsUpdateConsumer(TRANSPORT_FILTER_ALLOW_SETTING, this::setTransportAllowFilter);
clusterSettings.addSettingsUpdateConsumer(TRANSPORT_FILTER_DENY_SETTING, this::setTransportDenyFilter);
clusterSettings.addSettingsUpdateConsumer(HTTP_FILTER_ALLOW_SETTING, this::setHttpAllowFilter);
clusterSettings.addSettingsUpdateConsumer(HTTP_FILTER_DENY_SETTING, this::setHttpDenyFilter);
clusterSettings.addSettingsUpdateConsumer(TcpTransport.TRANSPORT_PROFILES_SETTING, this::setTransportProfiles);
clusterSettings.addAffixUpdateConsumer(PROFILE_FILTER_ALLOW_SETTING, this::setProfileAllowRules, (a,b) -> {});
clusterSettings.addAffixUpdateConsumer(PROFILE_FILTER_DENY_SETTING, this::setProfileDenyRules, (a,b) -> {});
updateRules();
}
@ -140,8 +157,13 @@ public class IPFilter {
return map;
}
private void setTransportProfiles(Settings settings) {
transportGroups = settings.getAsGroups();
private void setProfileAllowRules(String profile, List<String> rules) {
profileAllowRules.put(profile, rules);
updateRules();
}
private void setProfileDenyRules(String profile, List<String> rules) {
profileDenyRules.put(profile, rules);
updateRules();
}
@ -215,18 +237,17 @@ public class IPFilter {
if (isIpFilterEnabled && boundTransportAddress.get() != null) {
TransportAddress[] localAddresses = boundTransportAddress.get().boundAddresses();
profileRules.put("default", createRules(transportAllowFilter, transportDenyFilter, localAddresses));
for (Map.Entry<String, Settings> entry : transportGroups.entrySet()) {
String profile = entry.getKey();
profileRules.put(TcpTransport.DEFAULT_PROFILE, createRules(transportAllowFilter, transportDenyFilter, localAddresses));
for (String profile : profiles) {
BoundTransportAddress profileBoundTransportAddress = profileBoundAddress.get().get(profile);
if (profileBoundTransportAddress == null) {
// this could happen if a user updates the settings dynamically with a new profile
logger.warn("skipping ip filter rules for profile [{}] since the profile is not bound to any addresses", profile);
continue;
}
Settings profileSettings = entry.getValue().getByPrefix(setting("filter."));
profileRules.put(profile, createRules(Arrays.asList(profileSettings.getAsArray("allow")),
Arrays.asList(profileSettings.getAsArray("deny")), profileBoundTransportAddress.boundAddresses()));
final List<String> allowRules = this.profileAllowRules.getOrDefault(profile, Collections.emptyList());
final List<String> denyRules = this.profileDenyRules.getOrDefault(profile, Collections.emptyList());
profileRules.put(profile, createRules(allowRules, denyRules, profileBoundTransportAddress.boundAddresses()));
}
}
@ -277,5 +298,7 @@ public class IPFilter {
settings.add(HTTP_FILTER_DENY_SETTING);
settings.add(TRANSPORT_FILTER_ALLOW_SETTING);
settings.add(TRANSPORT_FILTER_DENY_SETTING);
settings.add(PROFILE_FILTER_ALLOW_SETTING);
settings.add(PROFILE_FILTER_DENY_SETTING);
}
}

View File

@ -82,12 +82,12 @@ public class SecurityNetty4Transport extends Netty4Transport {
}
@Override
protected ChannelHandler getServerChannelInitializer(String name, Settings settings) {
protected ChannelHandler getServerChannelInitializer(String name) {
SSLConfiguration configuration = profileConfiguration.get(name);
if (configuration == null) {
throw new IllegalStateException("unknown profile: " + name);
}
return new SecurityServerChannelInitializer(settings, name, configuration);
return new SecurityServerChannelInitializer(name, configuration);
}
@Override
@ -130,8 +130,8 @@ public class SecurityNetty4Transport extends Netty4Transport {
class SecurityServerChannelInitializer extends ServerChannelInitializer {
private final SSLConfiguration configuration;
SecurityServerChannelInitializer(Settings settings, String name, SSLConfiguration configuration) {
super(name, settings);
SecurityServerChannelInitializer(String name, SSLConfiguration configuration) {
super(name);
this.configuration = configuration;
}

View File

@ -7,8 +7,9 @@ package org.elasticsearch.xpack.ssl;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
@ -50,6 +51,108 @@ public class SSLConfigurationSettings {
private final List<Setting<?>> allSettings;
private static final Function<String, Setting<List<String>>> CIPHERS_SETTING_TEMPLATE = key -> Setting.listSetting(key, Collections
.emptyList(), Function.identity(), Property.NodeScope, Property.Filtered);
public static final Setting<List<String>> CIPHERS_SETTING_PROFILES = Setting.affixKeySetting("transport.profiles.",
"xpack.security.ssl.cipher_suites", CIPHERS_SETTING_TEMPLATE);
private static final Function<String,Setting<List<String>>> SUPPORTED_PROTOCOLS_TEMPLATE = key -> Setting.listSetting(key,
Collections.emptyList(), Function.identity(), Property.NodeScope, Property.Filtered);
public static final Setting<List<String>> SUPPORTED_PROTOCOLS_PROFILES = Setting.affixKeySetting("transport.profiles.",
"xpack.security.ssl.supported_protocols", SUPPORTED_PROTOCOLS_TEMPLATE) ;
private static final Function<String, Setting<Optional<String>>> KEYSTORE_PATH_TEMPLATE = key -> new Setting<>(key, s -> null,
Optional::ofNullable, Property.NodeScope, Property.Filtered);
public static final Setting<Optional<String>> KEYSTORE_PATH_PROFILES = Setting.affixKeySetting("transport.profiles.",
"xpack.security.ssl.keystore.path", KEYSTORE_PATH_TEMPLATE);
private static final Function<String, Setting<SecureString>> LEGACY_KEYSTORE_PASSWORD_TEMPLATE = key -> new Setting<>(key, "",
SecureString::new, Property.Deprecated, Property.Filtered, Property.NodeScope);
public static final Setting<SecureString> LEGACY_KEYSTORE_PASSWORD_PROFILES = Setting.affixKeySetting("transport.profiles.",
"xpack.security.ssl.keystore.password", LEGACY_KEYSTORE_PASSWORD_TEMPLATE);
private static final Function<String, Setting<SecureString>> KEYSTORE_PASSWORD_TEMPLATE = key -> SecureSetting.secureString(key,
LEGACY_KEYSTORE_PASSWORD_TEMPLATE.apply(key.replace("keystore.secure_password", "keystore.password")));
public static final Setting<SecureString> KEYSTORE_PASSWORD_PROFILES = Setting.affixKeySetting("transport.profiles.",
"xpack.security.ssl.keystore.secure_password", KEYSTORE_PASSWORD_TEMPLATE);
private static final Function<String, Setting<SecureString>> LEGACY_KEYSTORE_KEY_PASSWORD_TEMPLATE = key -> new Setting<>(key, "",
SecureString::new, Property.Deprecated, Property.Filtered, Property.NodeScope);
public static final Setting<SecureString> LEGACY_KEYSTORE_KEY_PASSWORD_PROFILES = Setting.affixKeySetting("transport.profiles.",
"xpack.security.ssl.keystore.key_password", LEGACY_KEYSTORE_KEY_PASSWORD_TEMPLATE);
private static final Function<String, Setting<SecureString>> KEYSTORE_KEY_PASSWORD_TEMPLATE = key ->
SecureSetting.secureString(key, LEGACY_KEYSTORE_KEY_PASSWORD_TEMPLATE.apply(key.replace("keystore.secure_key_password",
"keystore.key_password")));
public static final Setting<SecureString> KEYSTORE_KEY_PASSWORD_PROFILES = Setting.affixKeySetting("transport.profiles.",
"xpack.security.ssl.keystore.secure_key_password", KEYSTORE_KEY_PASSWORD_TEMPLATE);
private static final Function<String, Setting<Optional<String>>> TRUST_STORE_PATH_TEMPLATE = key -> new Setting<>(key, s -> null,
Optional::ofNullable, Property.NodeScope, Property.Filtered);
public static final Setting<Optional<String>> TRUST_STORE_PATH_PROFILES = Setting.affixKeySetting("transport.profiles.",
"xpack.security.ssl.truststore.path", TRUST_STORE_PATH_TEMPLATE);
private static final Function<String, Setting<Optional<String>>> KEY_PATH_TEMPLATE = key -> new Setting<>(key, s -> null,
Optional::ofNullable, Property.NodeScope, Property.Filtered);
public static final Setting<Optional<String>> KEY_PATH_PROFILES = Setting.affixKeySetting("transport.profiles.",
"xpack.security.ssl.key", KEY_PATH_TEMPLATE);
private static final Function<String, Setting<SecureString>> LEGACY_TRUSTSTORE_PASSWORD_TEMPLATE = key ->
new Setting<>(key, "", SecureString::new, Property.Deprecated, Property.Filtered, Property.NodeScope);
public static final Setting<SecureString> LEGACY_TRUSTSTORE_PASSWORD_PROFILES = Setting.affixKeySetting("transport.profiles.",
"xpack.security.ssl.truststore.password", LEGACY_TRUSTSTORE_PASSWORD_TEMPLATE);
private static final Function<String, Setting<SecureString>> TRUSTSTORE_PASSWORD_TEMPLATE = key ->
SecureSetting.secureString(key, LEGACY_TRUSTSTORE_PASSWORD_TEMPLATE.apply(key.replace("truststore.secure_password",
"truststore.password")));
public static final Setting<SecureString> TRUSTSTORE_PASSWORD_PROFILES = Setting.affixKeySetting("transport.profiles.",
"xpack.security.ssl.truststore.secure_password", TRUSTSTORE_PASSWORD_TEMPLATE);
private static final Function<String, Setting<String>> KEY_STORE_ALGORITHM_TEMPLATE = key ->
new Setting<>(key, s -> KeyManagerFactory.getDefaultAlgorithm(),
Function.identity(), Property.NodeScope, Property.Filtered);
public static final Setting<String> KEY_STORE_ALGORITHM_PROFILES = Setting.affixKeySetting("transport.profiles.",
"xpack.security.ssl.keystore.algorithm", KEY_STORE_ALGORITHM_TEMPLATE);
private static final Function<String, Setting<String>> TRUST_STORE_ALGORITHM_TEMPLATE = key ->
new Setting<>(key, s -> TrustManagerFactory.getDefaultAlgorithm(),
Function.identity(), Property.NodeScope, Property.Filtered);
public static final Setting<String> TRUST_STORE_ALGORITHM_PROFILES = Setting.affixKeySetting("transport.profiles.",
"xpack.security.ssl.truststore.algorithm", TRUST_STORE_ALGORITHM_TEMPLATE);
private static final Function<String, Setting<SecureString>> LEGACY_KEY_PASSWORD_TEMPLATE = key -> new Setting<>(key, "",
SecureString::new, Property.Deprecated, Property.Filtered, Property.NodeScope);
public static final Setting<SecureString> LEGACY_KEY_PASSWORD_PROFILES = Setting.affixKeySetting("transport.profiles.",
"xpack.security.ssl.key_passphrase", LEGACY_KEY_PASSWORD_TEMPLATE);
private static final Function<String, Setting<SecureString>> KEY_PASSWORD_TEMPLATE = key ->
SecureSetting.secureString(key, LEGACY_KEY_PASSWORD_TEMPLATE.apply(key.replace("secure_key_passphrase",
"key_passphrase")));
public static final Setting<SecureString> KEY_PASSWORD_PROFILES = Setting.affixKeySetting("transport.profiles.",
"xpack.security.ssl.secure_key_passphrase", KEY_PASSWORD_TEMPLATE);
private static final Function<String, Setting<Optional<String>>> CERT_TEMPLATE = key -> new Setting<>(key, s -> null,
Optional::ofNullable, Property.NodeScope, Property.Filtered);
public static final Setting<Optional<String>> CERT_PROFILES = Setting.affixKeySetting("transport.profiles.",
"xpack.security.ssl.certificate", CERT_TEMPLATE);
private static final Function<String, Setting<List<String>>> CAPATH_SETTING_TEMPLATE = key -> Setting.listSetting(key, Collections
.emptyList(), Function.identity(), Property.NodeScope, Property.Filtered);
public static final Setting<List<String>> CAPATH_SETTING_PROFILES = Setting.affixKeySetting("transport.profiles.",
"xpack.security.ssl.certificate_authorities", CAPATH_SETTING_TEMPLATE);
private static final Function<String, Setting<Optional<SSLClientAuth>>> CLIENT_AUTH_SETTING_TEMPLATE =
key -> new Setting<>(key, (String) null, s -> s == null ? Optional.empty() : Optional.of(SSLClientAuth.parse(s)),
Property.NodeScope, Property.Filtered);
public static final Setting<Optional<SSLClientAuth>> CLIENT_AUTH_SETTING_PROFILES = Setting.affixKeySetting("transport.profiles.",
"xpack.security.ssl.client_authentication", CLIENT_AUTH_SETTING_TEMPLATE);
private static final Function<String, Setting<Optional<VerificationMode>>> VERIFICATION_MODE_SETTING_TEMPLATE =
key -> new Setting<>(key, (String) null, s -> s == null ? Optional.empty() : Optional.of(VerificationMode.parse(s)),
Property.NodeScope, Property.Filtered);
public static final Setting<Optional<VerificationMode>> VERIFICATION_MODE_SETTING_PROFILES = Setting.affixKeySetting(
"transport.profiles.", "xpack.security.ssl.verification_mode", VERIFICATION_MODE_SETTING_TEMPLATE);
/**
* @see #withoutPrefix
* @see #withPrefix
@ -58,38 +161,25 @@ public class SSLConfigurationSettings {
*/
private SSLConfigurationSettings(String prefix) {
assert prefix != null : "Prefix cannot be null (but can be blank)";
ciphers = Setting.listSetting(prefix + "cipher_suites", Collections.emptyList(), Function.identity(),
Property.NodeScope, Property.Filtered);
supportedProtocols = Setting.listSetting(prefix + "supported_protocols", Collections.emptyList(), Function.identity(),
Property.NodeScope, Property.Filtered);
keystorePath = new Setting<>(prefix + "keystore.path", s -> null, Optional::ofNullable,
Property.NodeScope, Property.Filtered);
legacyKeystorePassword = new Setting<>(prefix + "keystore.password", "", SecureString::new,
Property.Deprecated, Property.Filtered, Property.NodeScope);
keystorePassword = SecureSetting.secureString(prefix + "keystore.secure_password", legacyKeystorePassword);
legacyKeystoreKeyPassword = new Setting<>(prefix + "keystore.key_password", "",
SecureString::new, Property.Deprecated, Property.Filtered, Property.NodeScope);
keystoreKeyPassword = SecureSetting.secureString(prefix + "keystore.secure_key_password", legacyKeystoreKeyPassword);
truststorePath = new Setting<>(prefix + "truststore.path", s -> null, Optional::ofNullable, Property.NodeScope, Property.Filtered);
legacyTruststorePassword = new Setting<>(prefix + "truststore.password", "", SecureString::new,
Property.Deprecated, Property.Filtered, Property.NodeScope);
truststorePassword = SecureSetting.secureString(prefix + "truststore.secure_password", legacyTruststorePassword);
keystoreAlgorithm = new Setting<>(prefix + "keystore.algorithm", s -> KeyManagerFactory.getDefaultAlgorithm(),
Function.identity(), Property.NodeScope, Property.Filtered);
truststoreAlgorithm = new Setting<>(prefix + "truststore.algorithm", s -> TrustManagerFactory.getDefaultAlgorithm(),
Function.identity(), Property.NodeScope, Property.Filtered);
keyPath = new Setting<>(prefix + "key", s -> null, Optional::ofNullable, Setting.Property.NodeScope, Setting.Property.Filtered);
legacyKeyPassword = new Setting<>(prefix + "key_passphrase", "", SecureString::new,
Property.Deprecated, Property.Filtered, Property.NodeScope);
keyPassword = SecureSetting.secureString(prefix + "secure_key_passphrase", legacyKeyPassword);
cert =new Setting<>(prefix + "certificate", s -> null, Optional::ofNullable, Property.NodeScope, Property.Filtered);
caPaths = Setting.listSetting(prefix + "certificate_authorities", Collections.emptyList(), Function.identity(),
Property.NodeScope, Property.Filtered);
clientAuth = new Setting<>(prefix + "client_authentication", (String) null,
s -> s == null ? Optional.empty() : Optional.of(SSLClientAuth.parse(s)), Property.NodeScope, Property.Filtered);
verificationMode = new Setting<>(prefix + "verification_mode", (String) null,
s -> s == null ? Optional.empty() : Optional.of(VerificationMode.parse(s)), Property.NodeScope, Property.Filtered);
ciphers = CIPHERS_SETTING_TEMPLATE.apply(prefix + "cipher_suites");
supportedProtocols = SUPPORTED_PROTOCOLS_TEMPLATE.apply(prefix + "supported_protocols");
keystorePath = KEYSTORE_PATH_TEMPLATE.apply(prefix + "keystore.path");
legacyKeystorePassword = LEGACY_KEYSTORE_PASSWORD_TEMPLATE.apply(prefix + "keystore.password");
keystorePassword = KEYSTORE_PASSWORD_TEMPLATE.apply(prefix + "keystore.secure_password");
legacyKeystoreKeyPassword = LEGACY_KEYSTORE_KEY_PASSWORD_TEMPLATE.apply(prefix + "keystore.key_password");
keystoreKeyPassword = KEYSTORE_KEY_PASSWORD_TEMPLATE.apply(prefix + "keystore.secure_key_password");
truststorePath = TRUST_STORE_PATH_TEMPLATE.apply(prefix + "truststore.path");
legacyTruststorePassword = LEGACY_TRUSTSTORE_PASSWORD_TEMPLATE.apply(prefix + "truststore.password");
truststorePassword = TRUSTSTORE_PASSWORD_TEMPLATE.apply(prefix + "truststore.secure_password");
keystoreAlgorithm = KEY_STORE_ALGORITHM_TEMPLATE.apply(prefix + "keystore.algorithm");
truststoreAlgorithm = TRUST_STORE_ALGORITHM_TEMPLATE.apply(prefix + "truststore.algorithm");
keyPath = KEY_PATH_TEMPLATE.apply(prefix + "key");
legacyKeyPassword = LEGACY_KEY_PASSWORD_TEMPLATE.apply(prefix + "key_passphrase");
keyPassword = KEY_PASSWORD_TEMPLATE.apply(prefix + "secure_key_passphrase");
cert = CERT_TEMPLATE.apply(prefix + "certificate");
caPaths = CAPATH_SETTING_TEMPLATE.apply(prefix + "certificate_authorities");
clientAuth = CLIENT_AUTH_SETTING_TEMPLATE.apply(prefix + "client_authentication");
verificationMode = VERIFICATION_MODE_SETTING_TEMPLATE.apply(prefix + "verification_mode");
this.allSettings = Arrays.asList(ciphers, supportedProtocols, keystorePath, keystorePassword, keystoreAlgorithm,
keystoreKeyPassword, truststorePath, truststorePassword, truststoreAlgorithm, keyPath, keyPassword, cert, caPaths,
@ -117,4 +207,14 @@ public class SSLConfigurationSettings {
assert prefix.endsWith("ssl.") : "The ssl config prefix (" + prefix + ") should end in 'ssl.'";
return new SSLConfigurationSettings(prefix);
}
public static Collection<Setting<?>> getProfileSettings() {
return Arrays.asList(CIPHERS_SETTING_PROFILES, SUPPORTED_PROTOCOLS_PROFILES, KEYSTORE_PATH_PROFILES,
LEGACY_KEYSTORE_PASSWORD_PROFILES, KEYSTORE_PASSWORD_PROFILES, LEGACY_KEYSTORE_KEY_PASSWORD_PROFILES,
KEYSTORE_KEY_PASSWORD_PROFILES, TRUST_STORE_PATH_PROFILES, LEGACY_TRUSTSTORE_PASSWORD_PROFILES,
TRUSTSTORE_PASSWORD_PROFILES, KEY_STORE_ALGORITHM_PROFILES, TRUST_STORE_ALGORITHM_PROFILES,KEY_PATH_PROFILES,
LEGACY_KEY_PASSWORD_PROFILES, KEY_PASSWORD_PROFILES,CERT_PROFILES,CAPATH_SETTING_PROFILES,
CLIENT_AUTH_SETTING_PROFILES, VERIFICATION_MODE_SETTING_PROFILES);
}
}

View File

@ -16,7 +16,6 @@ import org.elasticsearch.common.Strings;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.transport.TcpTransport;
import org.elasticsearch.xpack.XPackSettings;
import org.elasticsearch.xpack.common.socket.SocketAccess;
import org.elasticsearch.xpack.security.Security;
@ -852,7 +851,7 @@ public class SSLService extends AbstractComponent {
private static List<Settings> getTransportProfileSSLSettings(Settings settings) {
List<Settings> sslSettings = new ArrayList<>();
Map<String, Settings> profiles = TcpTransport.TRANSPORT_PROFILES_SETTING.get(settings).getAsGroups(true);
Map<String, Settings> profiles = settings.getGroups("transport.profiles.", true);
for (Entry<String, Settings> entry : profiles.entrySet()) {
Settings profileSettings = entry.getValue().getByPrefix("xpack.security.ssl.");
if (profileSettings.isEmpty() == false) {

View File

@ -5,6 +5,10 @@
*/
package org.elasticsearch.xpack.security.transport;
import org.elasticsearch.ElasticsearchSecurityException;
import org.elasticsearch.Version;
import org.elasticsearch.cluster.action.index.NodeMappingRefreshAction;
import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.common.network.NetworkAddress;
import org.elasticsearch.common.network.NetworkModule;
import org.elasticsearch.common.settings.Settings;
@ -17,7 +21,14 @@ import org.elasticsearch.node.NodeValidationException;
import org.elasticsearch.test.SecurityIntegTestCase;
import org.elasticsearch.test.SecuritySettingsSource;
import org.elasticsearch.test.discovery.TestZenDiscovery;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.ConnectionProfile;
import org.elasticsearch.transport.Transport;
import org.elasticsearch.transport.TransportException;
import org.elasticsearch.transport.TransportRequestOptions;
import org.elasticsearch.transport.TransportResponse;
import org.elasticsearch.transport.TransportResponseHandler;
import org.elasticsearch.transport.TransportService;
import org.elasticsearch.xpack.XPackPlugin;
import org.elasticsearch.xpack.XPackSettings;
import org.elasticsearch.xpack.security.Security;
@ -29,10 +40,13 @@ import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import static java.util.Collections.singletonMap;
import static org.elasticsearch.test.SecuritySettingsSource.addSSLSettingsForStore;
import static org.elasticsearch.xpack.security.test.SecurityTestUtils.writeFile;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
public class ServerTransportFilterIntegrationTests extends SecurityIntegTestCase {
@ -65,7 +79,6 @@ public class ServerTransportFilterIntegrationTests extends SecurityIntegTestCase
settingsBuilder.put(super.nodeSettings(nodeOrdinal))
.put("transport.profiles.client.xpack.security.ssl.truststore.path", store) // settings for client truststore
.put("xpack.ssl.client_authentication", SSLClientAuth.REQUIRED)
.put("transport.profiles.default.type", "node")
.put("transport.profiles.client.xpack.security.type", "client")
.put("transport.profiles.client.port", randomClientPortRange)
// make sure this is "localhost", no matter if ipv4 or ipv6, but be consistent
@ -73,6 +86,9 @@ public class ServerTransportFilterIntegrationTests extends SecurityIntegTestCase
.put("xpack.security.audit.enabled", false)
.put(XPackSettings.WATCHER_ENABLED.getKey(), false)
.put(TestZenDiscovery.USE_MOCK_PINGS.getKey(), false);
if (randomBoolean()) {
settingsBuilder.put("transport.profiles.default.xpack.security.type", "node"); // this is default lets set it randomly
}
SecuritySettingsSource.addSecureSettings(settingsBuilder, secureSettings ->
secureSettings.setString("transport.profiles.client.xpack.security.ssl.truststore.secure_password", "testnode"));
@ -111,7 +127,7 @@ public class ServerTransportFilterIntegrationTests extends SecurityIntegTestCase
}
}
public void testThatConnectionToClientTypeConnectionIsRejected() throws IOException, NodeValidationException {
public void testThatConnectionToClientTypeConnectionIsRejected() throws IOException, NodeValidationException, InterruptedException {
Path home = createTempDir();
Path xpackConf = home.resolve("config").resolve(XPackPlugin.NAME);
Files.createDirectories(xpackConf);
@ -144,23 +160,49 @@ public class ServerTransportFilterIntegrationTests extends SecurityIntegTestCase
addSSLSettingsForStore(nodeSettings, "/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks", "testnode");
try (Node node = new MockNode(nodeSettings.build(), Arrays.asList(XPackPlugin.class, TestZenDiscovery.TestPlugin.class))) {
node.start();
TransportService instance = node.injector().getInstance(TransportService.class);
try (Transport.Connection connection = instance.openConnection(new DiscoveryNode("theNode", transportAddress, Version.CURRENT),
ConnectionProfile.buildSingleChannelProfile(TransportRequestOptions.Type.REG, null, null))) {
// handshake should be ok
final DiscoveryNode handshake = instance.handshake(connection, 10000);
assertEquals(transport.boundAddress().publishAddress(), handshake.getAddress());
CountDownLatch latch = new CountDownLatch(1);
instance.sendRequest(connection, NodeMappingRefreshAction.ACTION_NAME,
new NodeMappingRefreshAction.NodeMappingRefreshRequest("foo", "bar", "baz"),
TransportRequestOptions.EMPTY,
new TransportResponseHandler<TransportResponse>() {
@Override
public TransportResponse newInstance() {
fail("never get that far");
return null;
}
// assert that node is not connected by waiting for the timeout
@Override
public void handleResponse(TransportResponse response) {
try {
// updating cluster settings requires a master. since the node should not be able to
// connect to the cluster, there should be no master, and therefore this
// operation should fail. we can't use cluster health/stats here to and
// wait for a timeout, because as long as the node is not connected to the cluster
// the license is disabled and therefore blocking health & stats calls.
node.client().admin().cluster().prepareUpdateSettings()
.setTransientSettings(singletonMap("logger.org.elasticsearch.xpack.security", "DEBUG"))
.setMasterNodeTimeout(TimeValue.timeValueMillis(100))
.get();
fail("Expected to fail update settings as the node should not be able to connect to the cluster, cause there should be " +
"no master");
} catch (MasterNotDiscoveredException e) {
// expected
logger.error("expected exception", e);
fail("never get that far");
} finally {
latch.countDown();
}
}
@Override
public void handleException(TransportException exp) {
try {
assertThat(exp.getCause(), instanceOf(ElasticsearchSecurityException.class));
assertThat(exp.getCause().getMessage(),
equalTo("executing internal/shard actions is considered malicious and forbidden"));
} finally {
latch.countDown();
}
}
@Override
public String executor() {
return ThreadPool.Names.SAME;
}
});
latch.await();
}
}
}

View File

@ -18,7 +18,6 @@ import org.elasticsearch.node.MockNode;
import org.elasticsearch.node.Node;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.junit.annotations.Network;
import org.elasticsearch.transport.TcpTransport;
import org.elasticsearch.transport.Transport;
import org.elasticsearch.xpack.XPackPlugin;
import org.elasticsearch.xpack.security.audit.AuditTrailService;
@ -62,7 +61,8 @@ public class IPFilterTests extends ESTestCase {
IPFilter.IP_FILTER_ENABLED_SETTING,
IPFilter.TRANSPORT_FILTER_ALLOW_SETTING,
IPFilter.TRANSPORT_FILTER_DENY_SETTING,
TcpTransport.TRANSPORT_PROFILES_SETTING)));
IPFilter.PROFILE_FILTER_ALLOW_SETTING,
IPFilter.PROFILE_FILTER_DENY_SETTING)));
httpTransport = mock(HttpServerTransport.class);
TransportAddress httpAddress = new TransportAddress(InetAddress.getLoopbackAddress(), 9200);
@ -145,6 +145,27 @@ public class IPFilterTests extends ESTestCase {
assertAddressIsDeniedForProfile("client", "192.168.0.2");
}
public void testThatProfilesAreUpdateable() throws Exception {
Settings settings = Settings.builder()
.put("xpack.security.transport.filter.allow", "localhost")
.put("xpack.security.transport.filter.deny", "_all")
.put("transport.profiles.client.xpack.security.filter.allow", "192.168.0.1")
.put("transport.profiles.client.xpack.security.filter.deny", "_all")
.build();
ipFilter = new IPFilter(settings, auditTrail, clusterSettings, licenseState);
ipFilter.setBoundTransportAddress(transport.boundAddress(), transport.profileBoundAddresses());
Settings newSettings = Settings.builder().putArray("transport.profiles.client.xpack.security.filter.allow", "192.168.0.1",
"192.168.0.2")
.put("transport.profiles.client.xpack.security.filter.deny", "192.168.0.3").build();
Settings.Builder updatedSettingsBuilder = Settings.builder();
clusterSettings.updateDynamicSettings(newSettings, updatedSettingsBuilder, Settings.builder(), "test");
clusterSettings.applySettings(updatedSettingsBuilder.build());
assertAddressIsAllowed("127.0.0.1");
assertAddressIsDenied("192.168.0.1");
assertAddressIsAllowedForProfile("client", "192.168.0.1", "192.168.0.2");
assertAddressIsDeniedForProfile("client", "192.168.0.3");
}
public void testThatAllowWinsOverDeny() throws Exception {
Settings settings = Settings.builder()
.put("xpack.security.transport.filter.allow", "10.0.0.1")

View File

@ -17,6 +17,7 @@ import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.transport.TcpTransport;
import org.elasticsearch.transport.Transport;
import org.elasticsearch.xpack.security.Security;
import org.elasticsearch.xpack.security.audit.AuditTrailService;
import org.elasticsearch.xpack.security.transport.filter.IPFilter;
import org.junit.Before;
@ -54,7 +55,8 @@ public class IpFilterRemoteAddressFilterTests extends ESTestCase {
IPFilter.IP_FILTER_ENABLED_SETTING,
IPFilter.TRANSPORT_FILTER_ALLOW_SETTING,
IPFilter.TRANSPORT_FILTER_DENY_SETTING,
TcpTransport.TRANSPORT_PROFILES_SETTING)));
IPFilter.PROFILE_FILTER_ALLOW_SETTING,
IPFilter.PROFILE_FILTER_DENY_SETTING)));
XPackLicenseState licenseState = mock(XPackLicenseState.class);
when(licenseState.isIpFilteringAllowed()).thenReturn(true);
AuditTrailService auditTrailService = new AuditTrailService(settings, Collections.emptyList(), licenseState);

View File

@ -73,7 +73,7 @@ public class SecurityNetty4TransportTests extends ESTestCase {
public void testThatProfileTakesDefaultSSLSetting() throws Exception {
SecurityNetty4Transport transport = createTransport();
Netty4MockUtil.setOpenChannelsHandlerToMock(transport);
ChannelHandler handler = transport.getServerChannelInitializer("default", Settings.EMPTY);
ChannelHandler handler = transport.getServerChannelInitializer("default");
final EmbeddedChannel ch = new EmbeddedChannel(handler);
assertThat(ch.pipeline().get(SslHandler.class).engine(), notNullValue());
}
@ -81,7 +81,7 @@ public class SecurityNetty4TransportTests extends ESTestCase {
public void testDefaultClientAuth() throws Exception {
SecurityNetty4Transport transport = createTransport();
Netty4MockUtil.setOpenChannelsHandlerToMock(transport);
ChannelHandler handler = transport.getServerChannelInitializer("default", Settings.EMPTY);
ChannelHandler handler = transport.getServerChannelInitializer("default");
final EmbeddedChannel ch = new EmbeddedChannel(handler);
assertThat(ch.pipeline().get(SslHandler.class).engine().getNeedClientAuth(), is(true));
assertThat(ch.pipeline().get(SslHandler.class).engine().getWantClientAuth(), is(false));
@ -96,7 +96,7 @@ public class SecurityNetty4TransportTests extends ESTestCase {
sslService = new SSLService(settings, env);
SecurityNetty4Transport transport = createTransport(settings);
Netty4MockUtil.setOpenChannelsHandlerToMock(transport);
ChannelHandler handler = transport.getServerChannelInitializer("default", Settings.EMPTY);
ChannelHandler handler = transport.getServerChannelInitializer("default");
final EmbeddedChannel ch = new EmbeddedChannel(handler);
assertThat(ch.pipeline().get(SslHandler.class).engine().getNeedClientAuth(), is(true));
assertThat(ch.pipeline().get(SslHandler.class).engine().getWantClientAuth(), is(false));
@ -111,7 +111,7 @@ public class SecurityNetty4TransportTests extends ESTestCase {
sslService = new SSLService(settings, env);
SecurityNetty4Transport transport = createTransport(settings);
Netty4MockUtil.setOpenChannelsHandlerToMock(transport);
ChannelHandler handler = transport.getServerChannelInitializer("default", Settings.EMPTY);
ChannelHandler handler = transport.getServerChannelInitializer("default");
final EmbeddedChannel ch = new EmbeddedChannel(handler);
assertThat(ch.pipeline().get(SslHandler.class).engine().getNeedClientAuth(), is(false));
assertThat(ch.pipeline().get(SslHandler.class).engine().getWantClientAuth(), is(false));
@ -126,7 +126,7 @@ public class SecurityNetty4TransportTests extends ESTestCase {
sslService = new SSLService(settings, env);
SecurityNetty4Transport transport = createTransport(settings);
Netty4MockUtil.setOpenChannelsHandlerToMock(transport);
ChannelHandler handler = transport.getServerChannelInitializer("default", Settings.EMPTY);
ChannelHandler handler = transport.getServerChannelInitializer("default");
final EmbeddedChannel ch = new EmbeddedChannel(handler);
assertThat(ch.pipeline().get(SslHandler.class).engine().getNeedClientAuth(), is(false));
assertThat(ch.pipeline().get(SslHandler.class).engine().getWantClientAuth(), is(true));
@ -136,13 +136,13 @@ public class SecurityNetty4TransportTests extends ESTestCase {
String value = randomFrom(SSLClientAuth.REQUIRED.name(), SSLClientAuth.REQUIRED.name().toLowerCase(Locale.ROOT));
Settings settings = Settings.builder()
.put(env.settings())
.put("transport.profiles.client.port", "8000-9000")
.put("transport.profiles.client.xpack.security.ssl.client_authentication", value)
.build();
sslService = new SSLService(settings, env);
SecurityNetty4Transport transport = createTransport(settings);
Netty4MockUtil.setOpenChannelsHandlerToMock(transport);
ChannelHandler handler = transport.getServerChannelInitializer("client",
Settings.builder().put("xpack.security.ssl.client_authentication", value).build());
ChannelHandler handler = transport.getServerChannelInitializer("client");
final EmbeddedChannel ch = new EmbeddedChannel(handler);
assertThat(ch.pipeline().get(SslHandler.class).engine().getNeedClientAuth(), is(true));
assertThat(ch.pipeline().get(SslHandler.class).engine().getWantClientAuth(), is(false));
@ -152,13 +152,13 @@ public class SecurityNetty4TransportTests extends ESTestCase {
String value = randomFrom(SSLClientAuth.NONE.name(), SSLClientAuth.NONE.name().toLowerCase(Locale.ROOT));
Settings settings = Settings.builder()
.put(env.settings())
.put("transport.profiles.client.port", "8000-9000")
.put("transport.profiles.client.xpack.security.ssl.client_authentication", value)
.build();
sslService = new SSLService(settings, env);
SecurityNetty4Transport transport = createTransport(settings);
Netty4MockUtil.setOpenChannelsHandlerToMock(transport);
ChannelHandler handler = transport.getServerChannelInitializer("client",
Settings.builder().put("xpack.security.ssl.client_authentication", value).build());
ChannelHandler handler = transport.getServerChannelInitializer("client");
final EmbeddedChannel ch = new EmbeddedChannel(handler);
assertThat(ch.pipeline().get(SslHandler.class).engine().getNeedClientAuth(), is(false));
assertThat(ch.pipeline().get(SslHandler.class).engine().getWantClientAuth(), is(false));
@ -168,13 +168,13 @@ public class SecurityNetty4TransportTests extends ESTestCase {
String value = randomFrom(SSLClientAuth.OPTIONAL.name(), SSLClientAuth.OPTIONAL.name().toLowerCase(Locale.ROOT));
Settings settings = Settings.builder()
.put(env.settings())
.put("transport.profiles.client.port", "8000-9000")
.put("transport.profiles.client.xpack.security.ssl.client_authentication", value)
.build();
sslService = new SSLService(settings, env);
SecurityNetty4Transport transport = createTransport(settings);
Netty4MockUtil.setOpenChannelsHandlerToMock(transport);
final ChannelHandler handler = transport.getServerChannelInitializer("client",
Settings.builder().put("xpack.security.ssl.client_authentication", value).build());
final ChannelHandler handler = transport.getServerChannelInitializer("client");
final EmbeddedChannel ch = new EmbeddedChannel(handler);
assertThat(ch.pipeline().get(SslHandler.class).engine().getNeedClientAuth(), is(false));
assertThat(ch.pipeline().get(SslHandler.class).engine().getWantClientAuth(), is(true));
@ -197,7 +197,7 @@ public class SecurityNetty4TransportTests extends ESTestCase {
sslService = new SSLService(settings, env);
SecurityNetty4Transport transport = createTransport(settings);
Netty4MockUtil.setOpenChannelsHandlerToMock(transport);
final ChannelHandler handler = transport.getServerChannelInitializer("default", Settings.EMPTY);
final ChannelHandler handler = transport.getServerChannelInitializer("default");
final EmbeddedChannel ch = new EmbeddedChannel(handler);
final SSLEngine engine = ch.pipeline().get(SslHandler.class).engine();
assertFalse(engine.getNeedClientAuth());