Allow license installation in dev mode without TLS (elastic/x-pack-elasticsearch#4155)
This commit allows license installation without TLS being enabled when the cluster is in dev mode. The main difference this change enables is the ability to install a production license on a single node cluster that is bound to localhost and does not have the single-node discovery enabled. relates elastic/x-pack-elasticsearch#4123 Original commit: elastic/x-pack-elasticsearch@04ebcc0fab
This commit is contained in:
parent
f0be979541
commit
3bc6fd76a3
|
@ -14,6 +14,7 @@ import org.elasticsearch.cluster.ClusterState;
|
||||||
import org.elasticsearch.cluster.ClusterStateListener;
|
import org.elasticsearch.cluster.ClusterStateListener;
|
||||||
import org.elasticsearch.cluster.ack.ClusterStateUpdateResponse;
|
import org.elasticsearch.cluster.ack.ClusterStateUpdateResponse;
|
||||||
import org.elasticsearch.cluster.metadata.MetaData;
|
import org.elasticsearch.cluster.metadata.MetaData;
|
||||||
|
import org.elasticsearch.cluster.node.DiscoveryNode;
|
||||||
import org.elasticsearch.cluster.service.ClusterService;
|
import org.elasticsearch.cluster.service.ClusterService;
|
||||||
import org.elasticsearch.common.component.AbstractLifecycleComponent;
|
import org.elasticsearch.common.component.AbstractLifecycleComponent;
|
||||||
import org.elasticsearch.common.component.Lifecycle;
|
import org.elasticsearch.common.component.Lifecycle;
|
||||||
|
@ -206,7 +207,7 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
|
||||||
if (newLicense.isProductionLicense()
|
if (newLicense.isProductionLicense()
|
||||||
&& XPackSettings.SECURITY_ENABLED.get(settings)
|
&& XPackSettings.SECURITY_ENABLED.get(settings)
|
||||||
&& XPackSettings.TRANSPORT_SSL_ENABLED.get(settings) == false
|
&& XPackSettings.TRANSPORT_SSL_ENABLED.get(settings) == false
|
||||||
&& "single-node".equals(DiscoveryModule.DISCOVERY_TYPE_SETTING.get(settings)) == false) {
|
&& isProductionMode(settings, clusterService.localNode())) {
|
||||||
// security is on but TLS is not configured we gonna fail the entire request and throw an exception
|
// security is on but TLS is not configured we gonna fail the entire request and throw an exception
|
||||||
throw new IllegalStateException("Cannot install a [" + newLicense.operationMode() +
|
throw new IllegalStateException("Cannot install a [" + newLicense.operationMode() +
|
||||||
"] license unless TLS is configured or security is disabled");
|
"] license unless TLS is configured or security is disabled");
|
||||||
|
@ -512,4 +513,13 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isProductionMode(Settings settings, DiscoveryNode localNode) {
|
||||||
|
final boolean singleNodeDisco = "single-node".equals(DiscoveryModule.DISCOVERY_TYPE_SETTING.get(settings));
|
||||||
|
return singleNodeDisco == false && isBoundToLoopback(localNode) == false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isBoundToLoopback(DiscoveryNode localNode) {
|
||||||
|
return localNode.getAddress().address().getAddress().isLoopbackAddress();
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -64,7 +64,7 @@ public abstract class AbstractLicenseServiceTestCase extends ESTestCase {
|
||||||
MetaData metaData = mock(MetaData.class);
|
MetaData metaData = mock(MetaData.class);
|
||||||
when(metaData.custom(LicensesMetaData.TYPE)).thenReturn(new LicensesMetaData(license, null));
|
when(metaData.custom(LicensesMetaData.TYPE)).thenReturn(new LicensesMetaData(license, null));
|
||||||
when(state.metaData()).thenReturn(metaData);
|
when(state.metaData()).thenReturn(metaData);
|
||||||
final DiscoveryNode mockNode = new DiscoveryNode("b", buildNewFakeTransportAddress(), emptyMap(), emptySet(), Version.CURRENT);
|
final DiscoveryNode mockNode = getLocalNode();
|
||||||
when(discoveryNodes.getMasterNode()).thenReturn(mockNode);
|
when(discoveryNodes.getMasterNode()).thenReturn(mockNode);
|
||||||
when(discoveryNodes.isLocalNodeElectedMaster()).thenReturn(false);
|
when(discoveryNodes.isLocalNodeElectedMaster()).thenReturn(false);
|
||||||
when(state.nodes()).thenReturn(discoveryNodes);
|
when(state.nodes()).thenReturn(discoveryNodes);
|
||||||
|
@ -72,6 +72,11 @@ public abstract class AbstractLicenseServiceTestCase extends ESTestCase {
|
||||||
when(clusterService.state()).thenReturn(state);
|
when(clusterService.state()).thenReturn(state);
|
||||||
when(clusterService.lifecycleState()).thenReturn(Lifecycle.State.STARTED);
|
when(clusterService.lifecycleState()).thenReturn(Lifecycle.State.STARTED);
|
||||||
when(clusterService.getClusterName()).thenReturn(new ClusterName("a"));
|
when(clusterService.getClusterName()).thenReturn(new ClusterName("a"));
|
||||||
|
when(clusterService.localNode()).thenReturn(mockNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected DiscoveryNode getLocalNode() {
|
||||||
|
return new DiscoveryNode("b", buildNewFakeTransportAddress(), emptyMap(), emptySet(), Version.CURRENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
package org.elasticsearch.license;
|
||||||
|
|
||||||
|
import org.elasticsearch.Version;
|
||||||
|
import org.elasticsearch.action.support.PlainActionFuture;
|
||||||
|
import org.elasticsearch.cluster.ClusterStateUpdateTask;
|
||||||
|
import org.elasticsearch.cluster.node.DiscoveryNode;
|
||||||
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
import org.elasticsearch.common.transport.TransportAddress;
|
||||||
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
|
||||||
|
import static java.util.Collections.emptyMap;
|
||||||
|
import static java.util.Collections.emptySet;
|
||||||
|
import static org.hamcrest.Matchers.containsString;
|
||||||
|
import static org.mockito.Matchers.any;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
public class LicenseTLSTests extends AbstractLicenseServiceTestCase {
|
||||||
|
|
||||||
|
private InetAddress inetAddress;
|
||||||
|
|
||||||
|
public void testApplyLicenseInDevMode() throws Exception {
|
||||||
|
License newLicense = TestUtils.generateSignedLicense(randomFrom("gold", "platinum"), TimeValue.timeValueHours(24L));
|
||||||
|
PutLicenseRequest request = new PutLicenseRequest();
|
||||||
|
request.acknowledge(true);
|
||||||
|
request.license(newLicense);
|
||||||
|
Settings settings = Settings.builder().put("xpack.security.enabled", true).build();
|
||||||
|
XPackLicenseState licenseState = new XPackLicenseState(settings);
|
||||||
|
inetAddress = InetAddress.getLoopbackAddress();
|
||||||
|
|
||||||
|
setInitialState(null, licenseState, settings);
|
||||||
|
licenseService.start();
|
||||||
|
PlainActionFuture<PutLicenseResponse> responseFuture = new PlainActionFuture<>();
|
||||||
|
licenseService.registerLicense(request, responseFuture);
|
||||||
|
verify(clusterService).submitStateUpdateTask(any(String.class), any(ClusterStateUpdateTask.class));
|
||||||
|
|
||||||
|
inetAddress = TransportAddress.META_ADDRESS;
|
||||||
|
settings = Settings.builder()
|
||||||
|
.put("xpack.security.enabled", true)
|
||||||
|
.put("discovery.type", "single-node")
|
||||||
|
.build();
|
||||||
|
licenseService.stop();
|
||||||
|
licenseState = new XPackLicenseState(settings);
|
||||||
|
setInitialState(null, licenseState, settings);
|
||||||
|
licenseService.start();
|
||||||
|
licenseService.registerLicense(request, responseFuture);
|
||||||
|
verify(clusterService, times(2)).submitStateUpdateTask(any(String.class), any(ClusterStateUpdateTask.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testApplyLicenseInProdMode() throws Exception {
|
||||||
|
final String licenseType = randomFrom("GOLD", "PLATINUM");
|
||||||
|
License newLicense = TestUtils.generateSignedLicense(licenseType, TimeValue.timeValueHours(24L));
|
||||||
|
PutLicenseRequest request = new PutLicenseRequest();
|
||||||
|
request.acknowledge(true);
|
||||||
|
request.license(newLicense);
|
||||||
|
Settings settings = Settings.builder().put("xpack.security.enabled", true).build();
|
||||||
|
XPackLicenseState licenseState = new XPackLicenseState(settings);
|
||||||
|
inetAddress = TransportAddress.META_ADDRESS;
|
||||||
|
|
||||||
|
setInitialState(null, licenseState, settings);
|
||||||
|
licenseService.start();
|
||||||
|
PlainActionFuture<PutLicenseResponse> responseFuture = new PlainActionFuture<>();
|
||||||
|
IllegalStateException e = expectThrows(IllegalStateException.class, () -> licenseService.registerLicense(request, responseFuture));
|
||||||
|
assertThat(e.getMessage(),
|
||||||
|
containsString("Cannot install a [" + licenseType + "] license unless TLS is configured or security is disabled"));
|
||||||
|
|
||||||
|
settings = Settings.builder().put("xpack.security.enabled", false).build();
|
||||||
|
licenseService.stop();
|
||||||
|
licenseState = new XPackLicenseState(settings);
|
||||||
|
setInitialState(null, licenseState, settings);
|
||||||
|
licenseService.start();
|
||||||
|
licenseService.registerLicense(request, responseFuture);
|
||||||
|
verify(clusterService).submitStateUpdateTask(any(String.class), any(ClusterStateUpdateTask.class));
|
||||||
|
|
||||||
|
settings = Settings.builder()
|
||||||
|
.put("xpack.security.enabled", true)
|
||||||
|
.put("xpack.security.transport.ssl.enabled", true)
|
||||||
|
.build();
|
||||||
|
licenseService.stop();
|
||||||
|
licenseState = new XPackLicenseState(settings);
|
||||||
|
setInitialState(null, licenseState, settings);
|
||||||
|
licenseService.start();
|
||||||
|
licenseService.registerLicense(request, responseFuture);
|
||||||
|
verify(clusterService, times(2)).submitStateUpdateTask(any(String.class), any(ClusterStateUpdateTask.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DiscoveryNode getLocalNode() {
|
||||||
|
return new DiscoveryNode("localnode", new TransportAddress(inetAddress, randomIntBetween(9300, 9399)),
|
||||||
|
emptyMap(), emptySet(), Version.CURRENT);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,40 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
||||||
* or more contributor license agreements. Licensed under the Elastic License;
|
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
|
||||||
*/
|
|
||||||
package org.elasticsearch.license;
|
|
||||||
|
|
||||||
import org.elasticsearch.common.settings.Settings;
|
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
|
||||||
import org.elasticsearch.test.ESIntegTestCase;
|
|
||||||
import org.elasticsearch.test.SecurityIntegTestCase;
|
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.equalTo;
|
|
||||||
|
|
||||||
@ESIntegTestCase.ClusterScope(
|
|
||||||
scope = ESIntegTestCase.Scope.TEST,
|
|
||||||
numDataNodes = 1,
|
|
||||||
numClientNodes = 0,
|
|
||||||
supportsDedicatedMasters = false,
|
|
||||||
autoMinMasterNodes = false)
|
|
||||||
public class LicenseServiceSingleNodeSecurityTests extends SecurityIntegTestCase {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Settings nodeSettings(int nodeOrdinal) {
|
|
||||||
return Settings
|
|
||||||
.builder()
|
|
||||||
.put(super.nodeSettings(nodeOrdinal))
|
|
||||||
.put("discovery.type", "single-node")
|
|
||||||
.put("transport.tcp.port", "0")
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testLicenseUpgradeSucceeds() throws Exception {
|
|
||||||
LicensingClient licensingClient = new LicensingClient(client());
|
|
||||||
License prodLicense = TestUtils.generateSignedLicense("platinum", TimeValue.timeValueHours(24));
|
|
||||||
PutLicenseResponse putLicenseResponse = licensingClient.preparePutLicense(prodLicense).get();
|
|
||||||
assertEquals(putLicenseResponse.status(), LicensesStatus.VALID);
|
|
||||||
assertThat(licensingClient.prepareGetLicense().get().license(), equalTo(prodLicense));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
||||||
* or more contributor license agreements. Licensed under the Elastic License;
|
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
|
||||||
*/
|
|
||||||
package org.elasticsearch.license;
|
|
||||||
|
|
||||||
import org.elasticsearch.analysis.common.CommonAnalysisPlugin;
|
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
|
||||||
import org.elasticsearch.plugins.Plugin;
|
|
||||||
import org.elasticsearch.test.SecurityIntegTestCase;
|
|
||||||
import org.elasticsearch.transport.Netty4Plugin;
|
|
||||||
import org.elasticsearch.xpack.security.LocalStateSecurity;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.equalTo;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Basic integration test that checks if license can be upgraded to a production license if TLS is enabled and vice versa.
|
|
||||||
*/
|
|
||||||
public class LicenseServiceWithSecurityTests extends SecurityIntegTestCase {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Collection<Class<? extends Plugin>> nodePlugins() {
|
|
||||||
return Arrays.asList(LocalStateSecurity.class, CommonAnalysisPlugin.class, Netty4Plugin.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Collection<Class<? extends Plugin>> transportClientPlugins() {
|
|
||||||
return nodePlugins();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testLicenseUpgradeFailsWithoutTLS() throws Exception {
|
|
||||||
assumeFalse("transport ssl is enabled", isTransportSSLEnabled());
|
|
||||||
LicensingClient licensingClient = new LicensingClient(client());
|
|
||||||
License license = licensingClient.prepareGetLicense().get().license();
|
|
||||||
License prodLicense = TestUtils.generateSignedLicense("platinum", TimeValue.timeValueHours(24));
|
|
||||||
IllegalStateException ise = expectThrows(IllegalStateException.class, () -> licensingClient.preparePutLicense(prodLicense).get());
|
|
||||||
assertEquals("Cannot install a [PLATINUM] license unless TLS is configured or security is disabled", ise.getMessage());
|
|
||||||
assertThat(licensingClient.prepareGetLicense().get().license(), equalTo(license));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testLicenseUpgradeSucceedsWithTLS() throws Exception {
|
|
||||||
assumeTrue("transport ssl is disabled", isTransportSSLEnabled());
|
|
||||||
LicensingClient licensingClient = new LicensingClient(client());
|
|
||||||
License prodLicense = TestUtils.generateSignedLicense("platinum", TimeValue.timeValueHours(24));
|
|
||||||
PutLicenseResponse putLicenseResponse = licensingClient.preparePutLicense(prodLicense).get();
|
|
||||||
assertEquals(putLicenseResponse.status(), LicensesStatus.VALID);
|
|
||||||
assertThat(licensingClient.prepareGetLicense().get().license(), equalTo(prodLicense));
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue