From 0156dec08f9871d9f4b8c604aba9d60bc1d714be Mon Sep 17 00:00:00 2001 From: Michael Morello Date: Thu, 2 Nov 2017 17:29:26 +0100 Subject: [PATCH] Add some settings to specify a remote Openshift cluster URL --- README.md | 3 + .../plugin/ConfigurationSettings.java | 6 +- .../plugin/OpenshiftClientFactory.java | 28 +++++++- .../elasticsearch/plugin/PluginSettings.java | 24 +++++++ .../elasticsearch/util/RequestUtils.java | 9 ++- .../plugin/OpenshiftClientFactoryTest.java | 67 +++++++++++++++++++ .../OpenshiftRequestContextFactoryTest.java | 15 ++--- .../plugin/PluginSettingsTest.java | 24 +++++++ .../elasticsearch/util/RequestUtilsTest.java | 4 +- 9 files changed, 164 insertions(+), 16 deletions(-) create mode 100644 src/test/java/io/fabric8/elasticsearch/plugin/OpenshiftClientFactoryTest.java diff --git a/README.md b/README.md index 4bf27c8..33a9830 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,9 @@ The following additional parameters can be set in set in `elasticsearch.yml`: |*io.fabric8.elasticsearch.kibana.mapping.empty*| Absolute file path to a JSON document that defines the index mapping for blank indexes| |*openshift.config.project_index_prefix*| The string value that project/namespace indices use as their prefix (default: ``) for example, with the common data model, if the namespace is `test`, the index name will be `project.test.$uuid.YYYY.MM.DD`. In this case, use `"project"` as the prefix - do not include the trailing `.`.| |*openshift.kibana.index.mode*| The setting that determines the kibana index is used by users. Valid values are one of the following: | +|*openshift.master*| If Openshift users must be authenticated to a remote cluster, this parameter must contain its URL (default: `https://kubernetes.default.svc`).| +|*openshift.trust.certificates*| Trust remote Openshift certificate (default: `true`)| +|*openshift.ca.path*| Absolute file path to the certificate that has to be used to authenticate remote Openshift cluster (default: `empty`)| *Note*: The `io.fabric8.elasticsearch.kibana.mapping.*` properties are required and must be defined for the plugin to function. A sample file may be found in the `samples` folder. diff --git a/src/main/java/io/fabric8/elasticsearch/plugin/ConfigurationSettings.java b/src/main/java/io/fabric8/elasticsearch/plugin/ConfigurationSettings.java index a515f19..2db61df 100644 --- a/src/main/java/io/fabric8/elasticsearch/plugin/ConfigurationSettings.java +++ b/src/main/java/io/fabric8/elasticsearch/plugin/ConfigurationSettings.java @@ -93,7 +93,11 @@ public interface ConfigurationSettings extends KibanaIndexMode{ */ static final String OPENSHIFT_ACL_ROLE_STRATEGY = "openshift.acl.role_strategy"; static final String DEFAULT_ACL_ROLE_STRATEGY = "user"; - + + static final String OPENSHIFT_MASTER = "openshift.master"; + static final String OPENSHIFT_CA_PATH = "openshift.ca.path"; + static final String OPENSHIFT_TRUST_CERT = "openshift.trust.certificates"; + static final String OPENSHIFT_KIBANA_REWRITE_ENABLED_FLAG = "openshift.kibana.rewrite.enabled"; diff --git a/src/main/java/io/fabric8/elasticsearch/plugin/OpenshiftClientFactory.java b/src/main/java/io/fabric8/elasticsearch/plugin/OpenshiftClientFactory.java index a6bae88..f7e1877 100644 --- a/src/main/java/io/fabric8/elasticsearch/plugin/OpenshiftClientFactory.java +++ b/src/main/java/io/fabric8/elasticsearch/plugin/OpenshiftClientFactory.java @@ -16,13 +16,37 @@ package io.fabric8.elasticsearch.plugin; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.logging.ESLogger; +import org.elasticsearch.common.logging.Loggers; + import io.fabric8.kubernetes.client.Config; import io.fabric8.openshift.client.DefaultOpenShiftClient; -import io.fabric8.openshift.client.OpenShiftClient; +import io.fabric8.openshift.client.NamespacedOpenShiftClient; public class OpenshiftClientFactory { - public OpenShiftClient create(Config config) { + private static final ESLogger LOGGER = Loggers.getLogger(OpenshiftClientFactory.class); + + private final PluginSettings settings; + + @Inject + public OpenshiftClientFactory(PluginSettings settings) { + this.settings = settings; + } + + public NamespacedOpenShiftClient create(Config config) { + if (settings.getMasterUrl() != null) { + config.setMasterUrl(settings.getMasterUrl()); + } + if (settings.isTrustCerts() != null) { + config.setTrustCerts(settings.isTrustCerts()); + } + if (settings.getOpenshiftCaPath() != null) { + config.setCaCertFile(settings.getOpenshiftCaPath()); + } + LOGGER.debug("Target cluster is {}, trust cert is {}, ca path is {}", + config.getMasterUrl(), config.isTrustCerts(), config.getCaCertFile()); return new DefaultOpenShiftClient(config); } } \ No newline at end of file diff --git a/src/main/java/io/fabric8/elasticsearch/plugin/PluginSettings.java b/src/main/java/io/fabric8/elasticsearch/plugin/PluginSettings.java index f3f293c..b6f02dd 100644 --- a/src/main/java/io/fabric8/elasticsearch/plugin/PluginSettings.java +++ b/src/main/java/io/fabric8/elasticsearch/plugin/PluginSettings.java @@ -37,6 +37,9 @@ public class PluginSettings implements ConfigurationSettings { private final String kibanaVersion; private final String kbnVersionHeader; private final Boolean enabled; + private final String masterUrl; + private final Boolean isTrustCerts; + private final String openshiftCaPath; @Inject public PluginSettings(final Settings settings) { @@ -58,6 +61,15 @@ public PluginSettings(final Settings settings) { this.kbnVersionHeader = settings.get(KIBANA_VERSION_HEADER, DEFAULT_KIBANA_VERSION_HEADER); this.enabled = settings.getAsBoolean(OPENSHIFT_DYNAMIC_ENABLED_FLAG, OPENSHIFT_DYNAMIC_ENABLED_DEFAULT); + this.masterUrl = settings.get(OPENSHIFT_MASTER); + this.openshiftCaPath = settings.get(OPENSHIFT_CA_PATH); + // Do not overwrite default K8S behavior + if (settings.get(OPENSHIFT_TRUST_CERT) != null) { + this.isTrustCerts = settings.getAsBoolean(OPENSHIFT_TRUST_CERT, true); + } else { + this.isTrustCerts = null; + } + LOGGER.info("Using kibanaIndexMode: '{}'", this.kibanaIndexMode); LOGGER.debug("searchGuardIndex: {}", this.searchGuardIndex); LOGGER.debug("roleStrategy: {}", this.roleStrategy); @@ -99,4 +111,16 @@ public Boolean isEnabled() { public void setKibanaIndexMode(String kibanaIndexMode) { this.kibanaIndexMode = kibanaIndexMode; } + + public String getMasterUrl() { + return masterUrl; + } + + public String getOpenshiftCaPath() { + return openshiftCaPath; + } + + public Boolean isTrustCerts() { + return isTrustCerts; + } } diff --git a/src/main/java/io/fabric8/elasticsearch/util/RequestUtils.java b/src/main/java/io/fabric8/elasticsearch/util/RequestUtils.java index bbe6b53..d0c29dd 100644 --- a/src/main/java/io/fabric8/elasticsearch/util/RequestUtils.java +++ b/src/main/java/io/fabric8/elasticsearch/util/RequestUtils.java @@ -26,10 +26,10 @@ import org.jboss.netty.handler.codec.http.HttpRequest; import io.fabric8.elasticsearch.plugin.ConfigurationSettings; +import io.fabric8.elasticsearch.plugin.OpenshiftClientFactory; import io.fabric8.elasticsearch.plugin.OpenshiftRequestContextFactory.OpenshiftRequestContext; import io.fabric8.kubernetes.client.ConfigBuilder; import io.fabric8.openshift.api.model.SubjectAccessReviewResponse; -import io.fabric8.openshift.client.DefaultOpenShiftClient; import io.fabric8.openshift.client.NamespacedOpenShiftClient; public class RequestUtils implements ConfigurationSettings { @@ -39,8 +39,11 @@ public class RequestUtils implements ConfigurationSettings { private String proxyUserHeader; + private final OpenshiftClientFactory clientFactory; + @Inject - public RequestUtils(final Settings settings) { + public RequestUtils(final OpenshiftClientFactory clientFactory, final Settings settings) { + this.clientFactory = clientFactory; this.proxyUserHeader = settings.get(SEARCHGUARD_AUTHENTICATION_PROXY_HEADER, DEFAULT_AUTH_PROXY_HEADER); } @@ -61,7 +64,7 @@ public boolean isOperationsUser(RestRequest request) { final String token = getBearerToken(request); ConfigBuilder builder = new ConfigBuilder().withOauthToken(token); boolean allowed = false; - try (NamespacedOpenShiftClient osClient = new DefaultOpenShiftClient(builder.build())) { + try (NamespacedOpenShiftClient osClient = clientFactory.create(builder.build())) { LOGGER.debug("Submitting a SAR to see if '{}' is able to retrieve logs across the cluster", user); SubjectAccessReviewResponse response = osClient.inAnyNamespace().subjectAccessReviews().createNew() .withVerb("get").withResource("pods/log").done(); diff --git a/src/test/java/io/fabric8/elasticsearch/plugin/OpenshiftClientFactoryTest.java b/src/test/java/io/fabric8/elasticsearch/plugin/OpenshiftClientFactoryTest.java new file mode 100644 index 0000000..6240199 --- /dev/null +++ b/src/test/java/io/fabric8/elasticsearch/plugin/OpenshiftClientFactoryTest.java @@ -0,0 +1,67 @@ +package io.fabric8.elasticsearch.plugin; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.File; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import com.google.common.io.Files; + +import io.fabric8.kubernetes.client.Config; +import io.fabric8.kubernetes.client.ConfigBuilder; +import io.fabric8.openshift.client.NamespacedOpenShiftClient; + +public class OpenshiftClientFactoryTest { + + private final String cert = + "-----BEGIN CERTIFICATE-----\n" + + "MIIC0DCCAbigAwIBAgIBATANBgkqhkiG9w0BAQsFADAZMRcwFQYDVQQDEw5sb2dn\n" + + "aW5nLXNpZ25lcjAeFw0xNzEwMTQxMTMwMDFaFw0yNzEwMTIxMTMwMDJaMBkxFzAV\n" + + "BgNVBAMTDmxvZ2dpbmctc2lnbmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n" + + "CgKCAQEAwjq3zygMEywx0PD/qGO2IwZxN18DwJbWB71JH+ldbLQHMJ3fvIy4wpJV\n" + + "FlJDPAejQ6hFnsoArVZInxcIcVfTiLgX15CXfCcrWUXXxfY2WWc6qDbQKje/+VZX\n" + + "/nr8c5DvbiDQxTDjXNO7WDGxqCaJIKg72VIqE4ac4AYNEwHeW3rd5cLEh/wfAu3n\n" + + "/iGFQ0v75ZG8ef2QQE364/d5GHMrXcWXUrXxuqRO/wdEjuXkP3SY/8sUZHCdugt8\n" + + "QygSXLp5mHaMc+Ie70/gl7u8wAxJGOvkjYVEgZPUTbemjEYhr9QwMuPvXxalWNc8\n" + + "kWIsOXnnyKG+RWDo7FE7kZtXpYfBcwIDAQABoyMwITAOBgNVHQ8BAf8EBAMCAqQw\n" + + "DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAf+1IaNvSYQ1BNQVa\n" + + "hODEru6x+Ytg5HUyykT4tmxvvlqLS03ez37zKi2tDQBI/Sl4l46mu9H7GS98viO7\n" + + "Cj9Vn7km70GH6vDvCjY3iMKYK+rXzp1D2az0wmdmYymfrP8WC4X0q+KMZKPSVb9g\n" + + "9/0kAKPtH7YRzTiaSMlWhxNFQxM+zrHvw/Vp16PXZwq+FCbtv6zemQKo4JBHN2LM\n" + + "dIfgLqEMBkpvo1TeD3HOB4LyJJ6nnG4bUWsOnYYSZw1L70rHX9Vu5xq7xap2eL9g\n" + + "Uk4XsS8F+8hOE3zaHbqKbxRSqxnNqBI+UM+nQc1i3Qh2CXy8jgdVTjxWstDN/IHN\n" + + "Y6RrKw== \n" + + "-----END CERTIFICATE-----"; + + // We need a temporary folder because K8S plugin check if CA file exists in constructor + @Rule + public TemporaryFolder certFolder = new TemporaryFolder(); + + @Test + public void testWhenRemoteCAIsNotNull() throws Exception { + + File tempCaFile = certFolder.newFile("ca.crt"); + Files.write(cert.getBytes(), tempCaFile); + + final Config config = (new ConfigBuilder()).build(); + final PluginSettings pluginSettings = mock(PluginSettings.class); + when(pluginSettings.getOpenshiftCaPath()).thenReturn(tempCaFile.getAbsolutePath()); + when(pluginSettings.getMasterUrl()).thenReturn("https://foo.bar"); + when(pluginSettings.isTrustCerts()).thenReturn(false); + + OpenshiftClientFactory clientFactory = new OpenshiftClientFactory(pluginSettings); + final NamespacedOpenShiftClient openShiftClient = clientFactory.create(config); + final Config k8sConfig = openShiftClient.getConfiguration(); + + assertEquals("Exp. the CA file path to be loaded by K8S plugin", tempCaFile.getAbsolutePath(), k8sConfig.getCaCertFile()); + assertEquals("Exp. the master url to be correctly set in the K8S plugin", "https://foo.bar/", k8sConfig.getMasterUrl()); + assertFalse("Exp. the trust cert boolean to be correctly set in the K8S plugin", k8sConfig.isTrustCerts()); + } + +} diff --git a/src/test/java/io/fabric8/elasticsearch/plugin/OpenshiftRequestContextFactoryTest.java b/src/test/java/io/fabric8/elasticsearch/plugin/OpenshiftRequestContextFactoryTest.java index d360391..7de93d4 100644 --- a/src/test/java/io/fabric8/elasticsearch/plugin/OpenshiftRequestContextFactoryTest.java +++ b/src/test/java/io/fabric8/elasticsearch/plugin/OpenshiftRequestContextFactoryTest.java @@ -19,10 +19,10 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyString; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @@ -32,9 +32,6 @@ import org.junit.Before; import org.junit.Test; -import io.fabric8.elasticsearch.plugin.ConfigurationSettings; -import io.fabric8.elasticsearch.plugin.KibanaUserReindexFilter; -import io.fabric8.elasticsearch.plugin.OpenshiftRequestContextFactory; import io.fabric8.elasticsearch.plugin.OpenshiftRequestContextFactory.OpenshiftRequestContext; import io.fabric8.elasticsearch.plugin.acl.UserProjectCache; import io.fabric8.elasticsearch.util.RequestUtils; @@ -46,7 +43,7 @@ import io.fabric8.openshift.api.model.ProjectBuilder; import io.fabric8.openshift.api.model.ProjectList; import io.fabric8.openshift.api.model.ProjectListBuilder; -import io.fabric8.openshift.client.OpenShiftClient; +import io.fabric8.openshift.client.NamespacedOpenShiftClient; public class OpenshiftRequestContextFactoryTest { @@ -72,7 +69,7 @@ private void givenUserIsCashed(boolean cached) { private void givenUserContextFactory(boolean isOperationsUser) { Settings settings = settingsBuilder.build(); - utils = spy(new RequestUtils(settings)); + utils = spy(new RequestUtils(clientFactory, settings)); doReturn(isOperationsUser).when(utils).isOperationsUser(any(RestRequest.class)); factory = new OpenshiftRequestContextFactory(settings, utils, clientFactory); @@ -80,7 +77,7 @@ private void givenUserContextFactory(boolean isOperationsUser) { @SuppressWarnings("unchecked") private void givenUserHasProjects() { - OpenShiftClient client = mock(OpenShiftClient.class); + NamespacedOpenShiftClient client = mock(NamespacedOpenShiftClient.class); ClientNonNamespaceOperation> projects = mock( ClientNonNamespaceOperation.class); ProjectList projectList = new ProjectListBuilder(false) diff --git a/src/test/java/io/fabric8/elasticsearch/plugin/PluginSettingsTest.java b/src/test/java/io/fabric8/elasticsearch/plugin/PluginSettingsTest.java index 07a332c..7e3c7df 100644 --- a/src/test/java/io/fabric8/elasticsearch/plugin/PluginSettingsTest.java +++ b/src/test/java/io/fabric8/elasticsearch/plugin/PluginSettingsTest.java @@ -17,6 +17,9 @@ package io.fabric8.elasticsearch.plugin; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import org.elasticsearch.common.settings.Settings; import org.junit.Test; @@ -37,4 +40,25 @@ public void testKibanaIndexModeDefault() { assertEquals("Exp. the plugin default to make kibana index mode unique", "unique", plugin.getKibanaIndexMode()); } + @Test + public void testRemoteOpenshiftWithDefaultConfiguration() { + PluginSettings plugin = new PluginSettings(Settings.builder().build()); + assertNull("Exp. remote Openshift URL is null by default to not override default K8S plugin behaviour", plugin.getMasterUrl()); + assertNull("Exp. Openshift certificate authority is null by default to not override default K8S plugin behaviour", plugin.getOpenshiftCaPath()); + assertNull("Exp. default trust cert is null to not override default K8S plugin behaviour", plugin.isTrustCerts()); + } + + @Test + public void testRemoteOpenshift() { + final String expectedRemoteOpenshiftUrl = "https://foo.bar:8443"; + final String expectedOpenshiftCaPath = "/etc/elasticsearch/secret/openshift-ca"; + final String source = "openshift.master: " + expectedRemoteOpenshiftUrl + "\n" + + "openshift.ca.path: " + expectedOpenshiftCaPath + "\n" + + "openshift.trust.certificates: false"; + PluginSettings plugin = new PluginSettings(Settings.builder().loadFromSource(source).build()); + assertEquals("Exp. the correct remote Openshift URL", expectedRemoteOpenshiftUrl, plugin.getMasterUrl()); + assertEquals("Exp. the correct Openshift certificate authority", expectedOpenshiftCaPath, plugin.getOpenshiftCaPath()); + assertFalse("Exp. the correct non default trust cert value from configuration", plugin.isTrustCerts()); + } + } diff --git a/src/test/java/io/fabric8/elasticsearch/util/RequestUtilsTest.java b/src/test/java/io/fabric8/elasticsearch/util/RequestUtilsTest.java index 4703a0f..78a1067 100644 --- a/src/test/java/io/fabric8/elasticsearch/util/RequestUtilsTest.java +++ b/src/test/java/io/fabric8/elasticsearch/util/RequestUtilsTest.java @@ -27,6 +27,7 @@ import org.junit.Test; import io.fabric8.elasticsearch.plugin.ConfigurationSettings; +import io.fabric8.elasticsearch.plugin.OpenshiftClientFactory; public class RequestUtilsTest { @@ -36,8 +37,9 @@ public class RequestUtilsTest { @Before public void setUp() throws Exception { + OpenshiftClientFactory clientFactory = mock(OpenshiftClientFactory.class); Settings settings = Settings.builder().put(ConfigurationSettings.SEARCHGUARD_AUTHENTICATION_PROXY_HEADER, PROXY_HEADER).build(); - util = new RequestUtils(settings); + util = new RequestUtils(clientFactory, settings); } @Test