Show SSL usage when security is not disabled (#40761)

It is possible to have SSL enabled but security disabled if security
was dynamically disabled by the license type (e.g. trial license).

e.g. In the following configuration:

    xpack.license.self_generated.type: trial
    # xpack.security not set, default to disabled on trial
    xpack.security.transport.ssl.enabled: true

The security feature will be reported as

    available: true
    enabled: false

And in this case, SSL will be active even though security is not
enabled.

This commit causes the X-Pack feature usage to report the state of the
"ssl" features unless security was explicitly disabled in the
settings.

Backport of: #40672
This commit is contained in:
Tim Vernum 2019-04-04 14:40:15 +11:00 committed by GitHub
parent 9d785e2b69
commit 1a30ab22fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 118 additions and 35 deletions

View File

@ -107,6 +107,10 @@ public class SecurityFeatureSetUsage extends XPackFeatureSet.Usage {
builder.field(AUDIT_XFIELD, auditUsage); builder.field(AUDIT_XFIELD, auditUsage);
builder.field(IP_FILTER_XFIELD, ipFilterUsage); builder.field(IP_FILTER_XFIELD, ipFilterUsage);
builder.field(ANONYMOUS_XFIELD, anonymousUsage); builder.field(ANONYMOUS_XFIELD, anonymousUsage);
} else if (sslUsage.isEmpty() == false) {
// A trial (or basic) license can have SSL without security.
// This is because security defaults to disabled on that license, but that dynamic-default does not disable SSL.
builder.field(SSL_XFIELD, sslUsage);
} }
} }

View File

@ -150,10 +150,18 @@ public class SecurityFeatureSet implements XPackFeatureSet {
} }
static Map<String, Object> sslUsage(Settings settings) { static Map<String, Object> sslUsage(Settings settings) {
Map<String, Object> map = new HashMap<>(2); // If security has been explicitly disabled in the settings, then SSL is also explicitly disabled, and we don't want to report
map.put("http", singletonMap("enabled", HTTP_SSL_ENABLED.get(settings))); // these http/transport settings as they would be misleading (they could report `true` even though they were ignored)
map.put("transport", singletonMap("enabled", TRANSPORT_SSL_ENABLED.get(settings))); // But, if security has not been explicitly configured, but has defaulted to off due to the current license type,
return map; // then these SSL settings are still respected (that is SSL might be enabled, while the rest of security is disabled).
if (XPackSettings.SECURITY_ENABLED.get(settings)) {
Map<String, Object> map = new HashMap<>(2);
map.put("http", singletonMap("enabled", HTTP_SSL_ENABLED.get(settings)));
map.put("transport", singletonMap("enabled", TRANSPORT_SSL_ENABLED.get(settings)));
return map;
} else {
return Collections.emptyMap();
}
} }
static Map<String, Object> tokenServiceUsage(Settings settings) { static Map<String, Object> tokenServiceUsage(Settings settings) {

View File

@ -31,6 +31,7 @@ import org.elasticsearch.xpack.security.authz.store.CompositeRolesStore;
import org.elasticsearch.xpack.security.transport.filter.IPFilter; import org.elasticsearch.xpack.security.transport.filter.IPFilter;
import org.junit.Before; import org.junit.Before;
import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
@ -129,29 +130,10 @@ public class SecurityFeatureSetTests extends ESTestCase {
final boolean rolesStoreEnabled = randomBoolean(); final boolean rolesStoreEnabled = randomBoolean();
doAnswer(invocationOnMock -> { configureRoleStoreUsage(rolesStoreEnabled);
ActionListener<Map<String, Object>> listener = (ActionListener<Map<String, Object>>) invocationOnMock.getArguments()[0];
if (rolesStoreEnabled) {
listener.onResponse(Collections.singletonMap("count", 1));
} else {
listener.onResponse(Collections.emptyMap());
}
return Void.TYPE;
}).when(rolesStore).usageStats(any(ActionListener.class));
final boolean roleMappingStoreEnabled = randomBoolean(); final boolean roleMappingStoreEnabled = randomBoolean();
doAnswer(invocationOnMock -> { configureRoleMappingStoreUsage(roleMappingStoreEnabled);
ActionListener<Map<String, Object>> listener = (ActionListener) invocationOnMock.getArguments()[0];
if (roleMappingStoreEnabled) {
final Map<String, Object> map = new HashMap<>();
map.put("size", 12L);
map.put("enabled", 10L);
listener.onResponse(map);
} else {
listener.onResponse(Collections.emptyMap());
}
return Void.TYPE;
}).when(roleMappingStore).usageStats(any(ActionListener.class));
Map<String, Object> realmsUsageStats = new HashMap<>(); Map<String, Object> realmsUsageStats = new HashMap<>();
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {
@ -161,11 +143,7 @@ public class SecurityFeatureSetTests extends ESTestCase {
realmUsage.put("key2", Arrays.asList(i)); realmUsage.put("key2", Arrays.asList(i));
realmUsage.put("key3", Arrays.asList(i % 2 == 0)); realmUsage.put("key3", Arrays.asList(i % 2 == 0));
} }
doAnswer(invocationOnMock -> { configureRealmsUsage(realmsUsageStats);
ActionListener<Map<String, Object>> listener = (ActionListener) invocationOnMock.getArguments()[0];
listener.onResponse(realmsUsageStats);
return Void.TYPE;
}).when(realms).usageStats(any(ActionListener.class));
final boolean anonymousEnabled = randomBoolean(); final boolean anonymousEnabled = randomBoolean();
if (anonymousEnabled) { if (anonymousEnabled) {
@ -185,11 +163,7 @@ public class SecurityFeatureSetTests extends ESTestCase {
assertThat(usage.name(), is(XPackField.SECURITY)); assertThat(usage.name(), is(XPackField.SECURITY));
assertThat(usage.enabled(), is(enabled)); assertThat(usage.enabled(), is(enabled));
assertThat(usage.available(), is(authcAuthzAvailable)); assertThat(usage.available(), is(authcAuthzAvailable));
XContentSource source; XContentSource source = getXContentSource(usage);
try (XContentBuilder builder = XContentFactory.jsonBuilder()) {
usage.toXContent(builder, ToXContent.EMPTY_PARAMS);
source = new XContentSource(builder);
}
if (enabled) { if (enabled) {
if (authcAuthzAvailable) { if (authcAuthzAvailable) {
@ -268,4 +242,101 @@ public class SecurityFeatureSetTests extends ESTestCase {
assertThat(source.getValue("token_service"), is(nullValue())); assertThat(source.getValue("token_service"), is(nullValue()));
assertThat(source.getValue("api_key_service"), is(nullValue())); assertThat(source.getValue("api_key_service"), is(nullValue()));
} }
public void testUsageOnTrialLicenseWithSecurityDisabledByDefault() throws Exception {
when(licenseState.isSecurityAvailable()).thenReturn(true);
when(licenseState.isSecurityDisabledByTrialLicense()).thenReturn(true);
Settings.Builder settings = Settings.builder().put(this.settings);
final boolean httpSSLEnabled = randomBoolean();
settings.put("xpack.security.http.ssl.enabled", httpSSLEnabled);
final boolean transportSSLEnabled = randomBoolean();
settings.put("xpack.security.transport.ssl.enabled", transportSSLEnabled);
final boolean auditingEnabled = randomBoolean();
settings.put(XPackSettings.AUDIT_ENABLED.getKey(), auditingEnabled);
final boolean rolesStoreEnabled = randomBoolean();
configureRoleStoreUsage(rolesStoreEnabled);
final boolean roleMappingStoreEnabled = randomBoolean();
configureRoleMappingStoreUsage(roleMappingStoreEnabled);
configureRealmsUsage(Collections.emptyMap());
SecurityFeatureSet featureSet = new SecurityFeatureSet(settings.build(), licenseState,
realms, rolesStore, roleMappingStore, ipFilter);
PlainActionFuture<XPackFeatureSet.Usage> future = new PlainActionFuture<>();
featureSet.usage(future);
XPackFeatureSet.Usage securityUsage = future.get();
BytesStreamOutput out = new BytesStreamOutput();
securityUsage.writeTo(out);
XPackFeatureSet.Usage serializedUsage = new SecurityFeatureSetUsage(out.bytes().streamInput());
for (XPackFeatureSet.Usage usage : Arrays.asList(securityUsage, serializedUsage)) {
assertThat(usage, is(notNullValue()));
assertThat(usage.name(), is(XPackField.SECURITY));
assertThat(usage.enabled(), is(false));
assertThat(usage.available(), is(true));
XContentSource source = getXContentSource(usage);
// check SSL : This is permitted even though security has been dynamically disabled by the trial license.
assertThat(source.getValue("ssl"), is(notNullValue()));
assertThat(source.getValue("ssl.http.enabled"), is(httpSSLEnabled));
assertThat(source.getValue("ssl.transport.enabled"), is(transportSSLEnabled));
// everything else is missing because security is disabled
assertThat(source.getValue("realms"), is(nullValue()));
assertThat(source.getValue("token_service"), is(nullValue()));
assertThat(source.getValue("api_key_service"), is(nullValue()));
assertThat(source.getValue("audit"), is(nullValue()));
assertThat(source.getValue("anonymous"), is(nullValue()));
assertThat(source.getValue("ipfilter"), is(nullValue()));
assertThat(source.getValue("roles"), is(nullValue()));
}
}
private XContentSource getXContentSource(XPackFeatureSet.Usage usage) throws IOException {
XContentSource source;
try (XContentBuilder builder = XContentFactory.jsonBuilder()) {
usage.toXContent(builder, ToXContent.EMPTY_PARAMS);
source = new XContentSource(builder);
}
return source;
}
private void configureRealmsUsage(Map<String, Object> realmsUsageStats) {
doAnswer(invocationOnMock -> {
ActionListener<Map<String, Object>> listener = (ActionListener) invocationOnMock.getArguments()[0];
listener.onResponse(realmsUsageStats);
return Void.TYPE;
}).when(realms).usageStats(any(ActionListener.class));
}
private void configureRoleStoreUsage(boolean rolesStoreEnabled) {
doAnswer(invocationOnMock -> {
ActionListener<Map<String, Object>> listener = (ActionListener<Map<String, Object>>) invocationOnMock.getArguments()[0];
if (rolesStoreEnabled) {
listener.onResponse(Collections.singletonMap("count", 1));
} else {
listener.onResponse(Collections.emptyMap());
}
return Void.TYPE;
}).when(rolesStore).usageStats(any(ActionListener.class));
}
private void configureRoleMappingStoreUsage(boolean roleMappingStoreEnabled) {
doAnswer(invocationOnMock -> {
ActionListener<Map<String, Object>> listener = (ActionListener) invocationOnMock.getArguments()[0];
if (roleMappingStoreEnabled) {
final Map<String, Object> map = new HashMap<>();
map.put("size", 12L);
map.put("enabled", 10L);
listener.onResponse(map);
} else {
listener.onResponse(Collections.emptyMap());
}
return Void.TYPE;
}).when(roleMappingStore).usageStats(any(ActionListener.class));
}
} }