Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add some settings to specify a remote Openshift cluster URL #111

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ The following additional parameters can be set in set in `elasticsearch.yml`:
|*_openshift.acl.expire_in_millis_*| The delay in milliseconds before generated ACLs are removed from|
|*_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: <ul><li>**unique** (Default) - Each user gets a unique index for kibana visualizations (e.g. .kibana.USER_UUID)</ul></li><ul><li>**ops_shared** - Users who are in an ops role will share an index (e.g. kibana) while non ops users will have a unique index (e.g. .kibana.USER_UUID)</ul></li>|
|*openshift.master.url*| 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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ 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.url";
static final String OPENSHIFT_CA_PATH = "openshift.ca.path";
static final String OPENSHIFT_TRUST_CERT = "openshift.trust.certificates";

/**
* List of index patterns to create for operations users
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public Collection<Object> createComponents(Client client, ClusterService cluster
final PluginSettings pluginSettings = new PluginSettings(settings);
final IndexMappingLoader indexMappingLoader = new IndexMappingLoader(settings);
final PluginClient pluginClient = new PluginClient(client, threadPool.getThreadContext());
final OpenshiftAPIService apiService = new OpenshiftAPIService();
final OpenshiftAPIService apiService = new OpenshiftAPIService(pluginSettings);
final RequestUtils requestUtils = new RequestUtils(pluginSettings, apiService);
final OpenshiftRequestContextFactory contextFactory = new OpenshiftRequestContextFactory(settings, requestUtils,
apiService);
Expand Down Expand Up @@ -216,7 +216,10 @@ public List<Setting<?>> getSettings() {
settings.add(Setting.simpleString(OPENSHIFT_ACL_ROLE_STRATEGY, Property.NodeScope));
settings.add(Setting.listSetting(OPENSHIFT_KIBANA_OPS_INDEX_PATTERNS, Arrays.asList(DEFAULT_KIBANA_OPS_INDEX_PATTERNS),
Function.identity(), Property.NodeScope, Property.Dynamic));

settings.add(Setting.simpleString(OPENSHIFT_CA_PATH, Property.NodeScope));
settings.add(Setting.simpleString(OPENSHIFT_MASTER, Property.NodeScope));
settings.add(Setting.simpleString(OPENSHIFT_TRUST_CERT, Property.NodeScope));

return settings;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,20 @@ public class OpenshiftAPIService {
private static final String APPLICATION_JSON = "application/json";
private static final Logger LOGGER = Loggers.getLogger(OpenshiftAPIService.class);
private final OpenShiftClientFactory factory;

public OpenshiftAPIService() {
this(new OpenShiftClientFactory(){});
private final PluginSettings settings;

public OpenshiftAPIService(PluginSettings settings) {
this(new OpenShiftClientFactory(){}, settings);
}
public OpenshiftAPIService(OpenShiftClientFactory factory) {

public OpenshiftAPIService(OpenShiftClientFactory factory, PluginSettings settings) {
this.factory = factory;
this.settings = settings;
}

public String userName(final String token) {
Response response = null;
try (DefaultOpenShiftClient client = factory.buildClient(token)) {
try (DefaultOpenShiftClient client = factory.buildClient(settings, token)) {
Request okRequest = new Request.Builder()
.url(client.getMasterUrl() + "apis/user.openshift.io/v1/users/~")
.header(ACCEPT, APPLICATION_JSON)
Expand All @@ -83,7 +85,7 @@ public String userName(final String token) {
}

public Set<Project> projectNames(final String token){
try (DefaultOpenShiftClient client = factory.buildClient(token)) {
try (DefaultOpenShiftClient client = factory.buildClient(settings, token)) {
Request request = new Request.Builder()
.url(client.getMasterUrl() + "apis/project.openshift.io/v1/projects")
.header(ACCEPT, APPLICATION_JSON)
Expand Down Expand Up @@ -124,7 +126,7 @@ public Set<Project> projectNames(final String token){
*/
public boolean localSubjectAccessReview(final String token,
final String project, final String verb, final String resource, final String resourceAPIGroup, final String [] scopes) {
try (DefaultOpenShiftClient client = factory.buildClient(token)) {
try (DefaultOpenShiftClient client = factory.buildClient(settings, token)) {
XContentBuilder payload = XContentFactory.jsonBuilder()
.startObject()
.field("kind","SubjectAccessReview")
Expand Down Expand Up @@ -189,8 +191,20 @@ private void log(Response response, String body) {
}

interface OpenShiftClientFactory {
default DefaultOpenShiftClient buildClient(final String token) {
default DefaultOpenShiftClient buildClient(final PluginSettings settings, final String token) {
Config config = new ConfigBuilder().withOauthToken(token).build();

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

Expand Down
24 changes: 24 additions & 0 deletions src/main/java/io/fabric8/elasticsearch/plugin/PluginSettings.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ public class PluginSettings implements ConfigurationSettings {
private final Set<String> opsIndexPatterns;
private final long expireInMillis;
private final Settings settings;
private final String masterUrl;
private final Boolean isTrustCerts;
private final String openshiftCaPath;

public PluginSettings(final Settings settings) {
this.settings = settings;
Expand All @@ -64,6 +67,15 @@ public PluginSettings(final Settings settings) {
this.opsIndexPatterns = new HashSet<String>(Arrays.asList(settings.getAsArray(OPENSHIFT_KIBANA_OPS_INDEX_PATTERNS, DEFAULT_KIBANA_OPS_INDEX_PATTERNS)));
this.expireInMillis = settings.getAsLong(OPENSHIFT_ACL_EXPIRE_IN_MILLIS, new Long(1000 * 60));

this.masterUrl = settings.get(OPENSHIFT_MASTER);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

settings.get returns null if there is no such setting?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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);
Expand Down Expand Up @@ -113,4 +125,16 @@ public void setKibanaIndexMode(String kibanaIndexMode) {
public Set<String> getKibanaOpsIndexPatterns() {
return opsIndexPatterns;
}

public String getMasterUrl() {
return masterUrl;
}

public String getOpenshiftCaPath() {
return openshiftCaPath;
}

public Boolean isTrustCerts() {
return isTrustCerts;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ public String getBearerToken(RestRequest request) {
}
return StringUtils.defaultIfEmpty(token, "");
}

public boolean isClientCertAuth(final ThreadContext threadContext) {
return threadContext != null && StringUtils.isNotEmpty(threadContext.getTransient("_sg_ssl_transport_principal"));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.mock;
Expand All @@ -29,6 +30,7 @@
import java.util.Set;

import org.apache.commons.lang.ArrayUtils;
import org.elasticsearch.common.settings.Settings;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
Expand All @@ -55,7 +57,8 @@ public class OpenshiftAPIServiceTest {

@Rule
public OpenShiftServer apiServer = new OpenShiftServer();
private OpenshiftAPIService service = new OpenshiftAPIService();
private final PluginSettings pluginSettings = new PluginSettings(Settings.EMPTY);
private OpenshiftAPIService service = new OpenshiftAPIService(pluginSettings);

@Before
public void setup() {
Expand Down Expand Up @@ -105,7 +108,7 @@ public void testLocalSubjectAccessReviewWhenNotNonResourceURL() throws IOExcepti
DefaultOpenShiftClient client = mock(DefaultOpenShiftClient.class);
OpenShiftClientFactory factory = mock(OpenShiftClientFactory.class);
Call call = mock(Call.class);
when(factory.buildClient(anyString())).thenReturn(client);
when(factory.buildClient(eq(pluginSettings), anyString())).thenReturn(client);
when(client.getHttpClient()).thenReturn(okClient);
when(client.getMasterUrl()).thenReturn(new URL("https://localhost:8443/"));

Expand All @@ -121,7 +124,7 @@ public void testLocalSubjectAccessReviewWhenNotNonResourceURL() throws IOExcepti
when(okClient.newCall(any(Request.class))).thenAnswer(answer);
when(call.execute()).thenReturn(response);

service = new OpenshiftAPIService(factory );
service = new OpenshiftAPIService(factory, pluginSettings);

assertTrue(service.localSubjectAccessReview("sometoken", "openshift-logging", "get", "pod/metrics", null, ArrayUtils.EMPTY_STRING_ARRAY));
Buffer buffer = new Buffer();
Expand All @@ -139,7 +142,7 @@ public void testLocalSubjectAccessReviewForNonResourceURL() throws IOException{
DefaultOpenShiftClient client = mock(DefaultOpenShiftClient.class);
OpenShiftClientFactory factory = mock(OpenShiftClientFactory.class);
Call call = mock(Call.class);
when(factory.buildClient(anyString())).thenReturn(client);
when(factory.buildClient(eq(pluginSettings), anyString())).thenReturn(client);
when(client.getHttpClient()).thenReturn(okClient);
when(client.getMasterUrl()).thenReturn(new URL("https://localhost:8443/"));

Expand All @@ -155,7 +158,7 @@ public void testLocalSubjectAccessReviewForNonResourceURL() throws IOException{
when(okClient.newCall(any(Request.class))).thenAnswer(answer);
when(call.execute()).thenReturn(response);

service = new OpenshiftAPIService(factory );
service = new OpenshiftAPIService(factory, pluginSettings);

assertTrue(service.localSubjectAccessReview("sometoken", "openshift-logging", "get", "/metrics", null, ArrayUtils.EMPTY_STRING_ARRAY));
Buffer buffer = new Buffer();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/**
* Copyright (C) 2015 Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

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

OpenshiftAPIService.OpenShiftClientFactory factory = new OpenshiftAPIService.OpenShiftClientFactory(){};

final NamespacedOpenShiftClient openShiftClient = factory.buildClient(pluginSettings, "foo");
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());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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.url: " + 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());
}

}