Skip to content

Commit

Permalink
Show SSL usage when security is not disabled (#40672)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
tvernum authored Apr 2, 2019
1 parent 911fd5a commit 57431bd
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ protected void innerXContent(XContentBuilder builder, Params params) throws IOEx
builder.field(AUDIT_XFIELD, auditUsage);
builder.field(IP_FILTER_XFIELD, ipFilterUsage);
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);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,18 @@ public void usage(ActionListener<XPackFeatureSet.Usage> listener) {
}

static Map<String, Object> sslUsage(Settings 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;
// If security has been explicitly disabled in the settings, then SSL is also explicitly disabled, and we don't want to report
// these http/transport settings as they would be misleading (they could report `true` even though they were ignored)
// But, if security has not been explicitly configured, but has defaulted to off due to the current license type,
// 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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.elasticsearch.xpack.security.transport.filter.IPFilter;
import org.junit.Before;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
Expand Down Expand Up @@ -126,29 +127,10 @@ public void testUsage() throws Exception {


final boolean rolesStoreEnabled = randomBoolean();
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));
configureRoleStoreUsage(rolesStoreEnabled);

final boolean roleMappingStoreEnabled = randomBoolean();
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));
configureRoleMappingStoreUsage(roleMappingStoreEnabled);

Map<String, Object> realmsUsageStats = new HashMap<>();
for (int i = 0; i < 5; i++) {
Expand All @@ -158,11 +140,7 @@ public void testUsage() throws Exception {
realmUsage.put("key2", Arrays.asList(i));
realmUsage.put("key3", Arrays.asList(i % 2 == 0));
}
doAnswer(invocationOnMock -> {
ActionListener<Map<String, Object>> listener = (ActionListener) invocationOnMock.getArguments()[0];
listener.onResponse(realmsUsageStats);
return Void.TYPE;
}).when(realms).usageStats(any(ActionListener.class));
configureRealmsUsage(realmsUsageStats);

final boolean anonymousEnabled = randomBoolean();
if (anonymousEnabled) {
Expand All @@ -182,11 +160,7 @@ public void testUsage() throws Exception {
assertThat(usage.name(), is(XPackField.SECURITY));
assertThat(usage.enabled(), is(enabled));
assertThat(usage.available(), is(authcAuthzAvailable));
XContentSource source;
try (XContentBuilder builder = XContentFactory.jsonBuilder()) {
usage.toXContent(builder, ToXContent.EMPTY_PARAMS);
source = new XContentSource(builder);
}
XContentSource source = getXContentSource(usage);

if (enabled) {
if (authcAuthzAvailable) {
Expand Down Expand Up @@ -251,4 +225,101 @@ public void testUsage() throws Exception {
}
}
}

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));
}
}

0 comments on commit 57431bd

Please sign in to comment.