From dcbd5bf1e05992c11c237c6bfdbc91c3bf4632a3 Mon Sep 17 00:00:00 2001 From: Ilya Buziuk Date: Mon, 25 Sep 2017 14:33:27 +0200 Subject: [PATCH] che #6258: Adding TLS support for OpenShift routes Signed-off-by: Ilya Buziuk --- .../WEB-INF/classes/codenvy/che.properties | 3 + .../OpenShiftInfrastructureProvisioner.java | 7 +- .../openshift/RoutesAnnotations.java | 4 +- .../provision/route/TlsRouteProvisioner.java | 91 +++++++++++++++++++ ...penShiftInfrastructureProvisionerTest.java | 10 +- .../openshift/RoutesAnnotationsTest.java | 2 +- .../openshift/ServerExposerTest.java | 2 +- .../route/TlsRouteProvisionerTest.java | 56 ++++++++++++ 8 files changed, 168 insertions(+), 7 deletions(-) create mode 100644 infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/provision/route/TlsRouteProvisioner.java create mode 100644 infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/provision/route/TlsRouteProvisionerTest.java diff --git a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/codenvy/che.properties b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/codenvy/che.properties index 1d084bb57a..a081fb430a 100644 --- a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/codenvy/che.properties +++ b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/codenvy/che.properties @@ -398,6 +398,9 @@ che.infra.openshift.username= che.infra.openshift.password= che.infra.openshift.trust_certs= +# Create routes with Transport Layer Security (TLS) enabled +che.infra.openshift.tls_enabled=false + # Defines OpenShift namespace in which all workspaces will be created. # If not set, every workspace will be created in a new project, where project name = workspace id che.infra.openshift.project= diff --git a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfrastructureProvisioner.java b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfrastructureProvisioner.java index 333725285f..2ff93fb995 100644 --- a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfrastructureProvisioner.java +++ b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfrastructureProvisioner.java @@ -18,6 +18,7 @@ import org.eclipse.che.api.workspace.server.spi.InternalEnvironment; import org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironment; import org.eclipse.che.workspace.infrastructure.openshift.provision.UniqueNamesProvisioner; import org.eclipse.che.workspace.infrastructure.openshift.provision.installer.InstallerConfigProvisioner; +import org.eclipse.che.workspace.infrastructure.openshift.provision.route.TlsRouteProvisioner; import org.eclipse.che.workspace.infrastructure.openshift.provision.volume.PersistentVolumeClaimProvisioner; /** @@ -32,15 +33,18 @@ public class OpenShiftInfrastructureProvisioner { private final InstallerConfigProvisioner installerConfigProvisioner; private final PersistentVolumeClaimProvisioner persistentVolumeClaimProvisioner; private final UniqueNamesProvisioner uniqueNamesProvisioner; + private final TlsRouteProvisioner tlsRouteProvisioner; @Inject public OpenShiftInfrastructureProvisioner( InstallerConfigProvisioner installerConfigProvisioner, PersistentVolumeClaimProvisioner projectVolumeProvisioner, - UniqueNamesProvisioner uniqueNamesProvisioner) { + UniqueNamesProvisioner uniqueNamesProvisioner, + TlsRouteProvisioner tlsRouteProvisioner) { this.installerConfigProvisioner = installerConfigProvisioner; this.persistentVolumeClaimProvisioner = projectVolumeProvisioner; this.uniqueNamesProvisioner = uniqueNamesProvisioner; + this.tlsRouteProvisioner = tlsRouteProvisioner; } public void provision( @@ -49,5 +53,6 @@ public class OpenShiftInfrastructureProvisioner { installerConfigProvisioner.provision(environment, osEnv, identity); persistentVolumeClaimProvisioner.provision(environment, osEnv, identity); uniqueNamesProvisioner.provision(environment, osEnv, identity); + tlsRouteProvisioner.provision(environment, osEnv, identity); } } diff --git a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/RoutesAnnotations.java b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/RoutesAnnotations.java index 6c05610822..9c470e6f16 100644 --- a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/RoutesAnnotations.java +++ b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/RoutesAnnotations.java @@ -90,8 +90,8 @@ public class RoutesAnnotations { /** * Retrieves server configuration from route annotations and returns (ref -> server config) map. */ - public Map servers() { - Map servers = new HashMap<>(); + public Map servers() { + Map servers = new HashMap<>(); for (Map.Entry entry : annotations.entrySet()) { Matcher refMatcher = SERVER_ANNOTATION_PATTERN.matcher(entry.getKey()); if (refMatcher.matches()) { diff --git a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/provision/route/TlsRouteProvisioner.java b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/provision/route/TlsRouteProvisioner.java new file mode 100644 index 0000000000..536f3ef722 --- /dev/null +++ b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/provision/route/TlsRouteProvisioner.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2012-2017 Red Hat, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.workspace.infrastructure.openshift.provision.route; + +import io.fabric8.openshift.api.model.Route; +import io.fabric8.openshift.api.model.RouteSpec; +import io.fabric8.openshift.api.model.TLSConfig; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity; +import org.eclipse.che.api.workspace.server.model.impl.ServerConfigImpl; +import org.eclipse.che.api.workspace.server.spi.InfrastructureException; +import org.eclipse.che.api.workspace.server.spi.InternalEnvironment; +import org.eclipse.che.workspace.infrastructure.openshift.RoutesAnnotations; +import org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironment; +import org.eclipse.che.workspace.infrastructure.openshift.provision.ConfigurationProvisioner; + +/** + * Enables Transport Layer Security (TLS) for workspace routes and changes protocol to secure (wss / + * https) in servers' configuration + * + * @author Ilya Buziuk + */ +@Singleton +public class TlsRouteProvisioner implements ConfigurationProvisioner { + private static final String TERMINATION_EDGE = "edge"; + private static final String TERMINATION_POLICY_REDIRECT = "Redirect"; + private final boolean isTlsEnabled; + + @Inject + public TlsRouteProvisioner(@Named("che.infra.openshift.tls_enabled") boolean isTlsEnabled) { + this.isTlsEnabled = isTlsEnabled; + } + + @Override + public void provision( + InternalEnvironment environment, OpenShiftEnvironment osEnv, RuntimeIdentity identity) + throws InfrastructureException { + if (isTlsEnabled) { + final Set routes = new HashSet<>(osEnv.getRoutes().values()); + for (Route route : routes) { + useSecureProtocolForServers(route); + enableTls(route); + } + } + } + + private void useSecureProtocolForServers(final Route route) { + Map servers = + RoutesAnnotations.newDeserializer(route.getMetadata().getAnnotations()).servers(); + + servers.values().forEach(s -> s.setProtocol(getSecureProtocol(s.getProtocol()))); + + Map annotations = + RoutesAnnotations.newSerializer().servers(servers).annotations(); + + route.getMetadata().getAnnotations().putAll(annotations); + } + + private String getSecureProtocol(final String protocol) { + if ("ws".equals(protocol)) { + return "wss"; + } else if ("http".equals(protocol)) { + return "https"; + } else return protocol; + } + + private void enableTls(final Route route) { + RouteSpec spec = route.getSpec(); + spec.setTls(getTLSConfig()); + } + + private TLSConfig getTLSConfig() { + TLSConfig config = new TLSConfig(); + config.setTermination(TERMINATION_EDGE); + config.setInsecureEdgeTerminationPolicy(TERMINATION_POLICY_REDIRECT); + return config; + } +} diff --git a/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfrastructureProvisionerTest.java b/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfrastructureProvisionerTest.java index 73a8e32625..fed0aa8da1 100644 --- a/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfrastructureProvisionerTest.java +++ b/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfrastructureProvisionerTest.java @@ -18,6 +18,7 @@ import org.eclipse.che.api.workspace.server.spi.InternalEnvironment; import org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironment; import org.eclipse.che.workspace.infrastructure.openshift.provision.UniqueNamesProvisioner; import org.eclipse.che.workspace.infrastructure.openshift.provision.installer.InstallerConfigProvisioner; +import org.eclipse.che.workspace.infrastructure.openshift.provision.route.TlsRouteProvisioner; import org.eclipse.che.workspace.infrastructure.openshift.provision.volume.PersistentVolumeClaimProvisioner; import org.mockito.InOrder; import org.mockito.Mock; @@ -40,6 +41,7 @@ public class OpenShiftInfrastructureProvisionerTest { @Mock private InternalEnvironment environment; @Mock private OpenShiftEnvironment osEnv; @Mock private RuntimeIdentity runtimeIdentity; + @Mock private TlsRouteProvisioner tlsRouteProvisioner; private OpenShiftInfrastructureProvisioner osInfraProvisioner; @@ -49,8 +51,9 @@ public class OpenShiftInfrastructureProvisionerTest { public void setUp() { osInfraProvisioner = new OpenShiftInfrastructureProvisioner( - installerProvisioner, pvcProvisioner, uniqueNamesProvisioner); - provisionOrder = inOrder(installerProvisioner, pvcProvisioner, uniqueNamesProvisioner); + installerProvisioner, pvcProvisioner, uniqueNamesProvisioner, tlsRouteProvisioner); + provisionOrder = + inOrder(installerProvisioner, pvcProvisioner, uniqueNamesProvisioner, tlsRouteProvisioner); } @Test @@ -66,6 +69,9 @@ public class OpenShiftInfrastructureProvisionerTest { provisionOrder .verify(uniqueNamesProvisioner) .provision(eq(environment), eq(osEnv), eq(runtimeIdentity)); + provisionOrder + .verify(tlsRouteProvisioner) + .provision(eq(environment), eq(osEnv), eq(runtimeIdentity)); provisionOrder.verifyNoMoreInteractions(); } } diff --git a/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/RoutesAnnotationsTest.java b/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/RoutesAnnotationsTest.java index d364a7a3b7..9919e7bfca 100644 --- a/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/RoutesAnnotationsTest.java +++ b/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/RoutesAnnotationsTest.java @@ -61,7 +61,7 @@ public class RoutesAnnotationsTest { RoutesAnnotations.Deserializer deserializer = RoutesAnnotations.newDeserializer(annotations); - Map servers = deserializer.servers(); + Map servers = deserializer.servers(); ServerConfig server1 = servers.get("my-server1/http"); assertNotNull(server1, "first server"); assertEquals(server1.getPort(), "8000/tcp"); diff --git a/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/ServerExposerTest.java b/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/ServerExposerTest.java index 20465ca555..ac3d73e66f 100644 --- a/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/ServerExposerTest.java +++ b/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/ServerExposerTest.java @@ -218,7 +218,7 @@ public class ServerExposerTest { RoutesAnnotations.Deserializer routeDeserializer = RoutesAnnotations.newDeserializer(route.getMetadata().getAnnotations()); - Map servers = routeDeserializer.servers(); + Map servers = routeDeserializer.servers(); ServerConfig serverConfig = servers.get(serverName); assertEquals(serverConfig, expected); } diff --git a/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/provision/route/TlsRouteProvisionerTest.java b/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/provision/route/TlsRouteProvisionerTest.java new file mode 100644 index 0000000000..b1012e09f7 --- /dev/null +++ b/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/provision/route/TlsRouteProvisionerTest.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2012-2017 Red Hat, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.workspace.infrastructure.openshift.provision.route; + +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import io.fabric8.openshift.api.model.Route; +import java.util.HashMap; +import java.util.Map; +import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity; +import org.eclipse.che.api.workspace.server.spi.InternalEnvironment; +import org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironment; +import org.mockito.Mock; +import org.mockito.testng.MockitoTestNGListener; +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; + +/** + * Tests {@link TlsRouteProvisioner}. + * + * @author Ilya Buziuk + */ +@Listeners(MockitoTestNGListener.class) +public class TlsRouteProvisionerTest { + + @Mock private InternalEnvironment environment; + @Mock private OpenShiftEnvironment osEnv; + @Mock private RuntimeIdentity runtimeIdentity; + + @Test + public void doNothingWhenTlsDisabled() throws Exception { + TlsRouteProvisioner tlsProvisioner = new TlsRouteProvisioner(false); + tlsProvisioner.provision(environment, osEnv, runtimeIdentity); + verify(osEnv, never()).getRoutes(); + } + + @Test + public void provisionTlsForRoutes() throws Exception { + TlsRouteProvisioner tlsProvisioner = new TlsRouteProvisioner(true); + final Map routes = new HashMap<>(); + when(osEnv.getRoutes()).thenReturn(routes); + tlsProvisioner.provision(environment, osEnv, runtimeIdentity); + verify(osEnv, times(1)).getRoutes(); + } +}