diff --git a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/che.properties b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/che.properties
index 168288800b97..47b70d7f15b4 100644
--- a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/che.properties
+++ b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/che.properties
@@ -494,4 +494,6 @@ che.workspace.feature.api=NULL
# Suitable values:
# - 'default': no additionally authentication system will be enabled.
# So servers should authenticate requests themselves.
+# - 'jwtproxy': jwtproxy will authenticate requests.
+# So servers will receive only authenticated ones.
che.agents.auth.secure_server_exposer=default
diff --git a/infrastructures/kubernetes/pom.xml b/infrastructures/kubernetes/pom.xml
index 84a0eaa1341a..c6181d81505b 100644
--- a/infrastructures/kubernetes/pom.xml
+++ b/infrastructures/kubernetes/pom.xml
@@ -122,6 +122,10 @@
org.eclipse.che.infrastructure.docker
docker-environment
+
+ org.eclipse.che.multiuser
+ che-multiuser-machine-authentication
+
org.eclipse.persistence
javax.persistence
@@ -215,6 +219,15 @@
+
+ com.mycila
+ license-maven-plugin
+
+
+ src/test/resources/jwtproxy-confg.yaml
+
+
+
diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesInfraModule.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesInfraModule.java
index 66d2e35edd43..012cf8cc80cf 100644
--- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesInfraModule.java
+++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesInfraModule.java
@@ -51,6 +51,7 @@
import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ExternalServerExposerStrategyProvider;
import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.MultiHostIngressExternalServerExposer;
import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.SingleHostIngressExternalServerExposer;
+import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.JwtProxySecureServerExposerFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.wsnext.KubernetesWorkspaceNextApplier;
/** @author Sergii Leshchenko */
@@ -70,6 +71,12 @@ protected void configure() {
install(new FactoryModuleBuilder().build(KubernetesRuntimeFactory.class));
install(new FactoryModuleBuilder().build(KubernetesBootstrapperFactory.class));
install(new FactoryModuleBuilder().build(StartSynchronizerFactory.class));
+
+ install(
+ new FactoryModuleBuilder()
+ .build(
+ new TypeLiteral>() {}));
+
bind(WorkspacePVCCleaner.class).asEagerSingleton();
bind(RemoveNamespaceOnWorkspaceRemove.class).asEagerSingleton();
diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/SecureServerExposerFactory.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/SecureServerExposerFactory.java
index 8cceb295c6d8..c129adbe947a 100644
--- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/SecureServerExposerFactory.java
+++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/SecureServerExposerFactory.java
@@ -16,6 +16,7 @@
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ExternalServerExposerStrategy;
import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.SecureServerExposer.DefaultSecureServerExposer;
+import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.JwtProxySecureServerExposerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -27,15 +28,18 @@ public class SecureServerExposerFactory {
private final String serverExposer;
private final ExternalServerExposerStrategy exposerStrategy;
+ private final JwtProxySecureServerExposerFactory jwtProxySecureServerExposerFactory;
@Inject
public SecureServerExposerFactory(
@Named("che.agents.auth_enabled") boolean agentsAuthEnabled,
@Named("che.agents.auth.secure_server_exposer") String serverExposer,
- ExternalServerExposerStrategy exposerStrategy) {
+ ExternalServerExposerStrategy exposerStrategy,
+ JwtProxySecureServerExposerFactory jwtProxySecureServerExposerFactory) {
this.agentsAuthEnabled = agentsAuthEnabled;
this.serverExposer = serverExposer;
this.exposerStrategy = exposerStrategy;
+ this.jwtProxySecureServerExposerFactory = jwtProxySecureServerExposerFactory;
}
/**
@@ -49,6 +53,8 @@ public SecureServerExposer create(RuntimeIdentity identity) {
}
switch (serverExposer) {
+ case "jwtproxy":
+ return jwtProxySecureServerExposerFactory.create(identity);
case "default":
return new DefaultSecureServerExposer<>(exposerStrategy);
default:
diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/JwtProxyConfigBuilder.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/JwtProxyConfigBuilder.java
new file mode 100644
index 000000000000..d580bd88de33
--- /dev/null
+++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/JwtProxyConfigBuilder.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2012-2018 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.kubernetes.server.secure.jwtproxy;
+
+import static org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.JwtProxyProvisioner.JWT_PROXY_CONFIG_FOLDER;
+import static org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.JwtProxyProvisioner.JWT_PROXY_PUBLIC_KEY_FILE;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Helps to build JWTProxy config with several verifier proxies.
+ *
+ * @author Sergii Leshchenko
+ */
+public class JwtProxyConfigBuilder {
+ private final List verifierProxies = new ArrayList<>();
+ private final String workspaceId;
+
+ public JwtProxyConfigBuilder(String workspaceId) {
+ this.workspaceId = workspaceId;
+ }
+
+ public void addVerifierProxy(Integer listenPort, String upstream) {
+ verifierProxies.add(new VerifierProxy(listenPort, upstream));
+ }
+
+ public String build() {
+ StringBuilder configBuilder = new StringBuilder();
+
+ configBuilder.append("jwtproxy:\n" + " verifier_proxies:\n");
+ for (VerifierProxy verifierProxy : verifierProxies) {
+ configBuilder.append(
+ String.format(
+ " - listen_addr: :%s\n" // :4471
+ + " verifier:\n"
+ + " upstream: %s/\n" // http://localhost:4401
+ + " audience: http://%s\n"
+ + " max_skew: 1m\n"
+ + " max_ttl: 3h\n"
+ + " key_server:\n"
+ + " type: preshared\n"
+ + " options:\n"
+ + " issuer: wsmaster\n"
+ + " key_id: mykey\n"
+ + " public_key_path: "
+ + JWT_PROXY_CONFIG_FOLDER
+ + "/"
+ + JWT_PROXY_PUBLIC_KEY_FILE
+ + "\n"
+ + " claims_verifiers:\n"
+ + " - type: static\n"
+ + " options:\n"
+ + " iss: wsmaster\n"
+ + " nonce_storage:\n"
+ + " type: void\n",
+ verifierProxy.listenPort,
+ verifierProxy.upstream,
+ workspaceId));
+ }
+ configBuilder.append(" signer_proxy:\n" + " enabled: false\n");
+ return configBuilder.toString();
+ }
+
+ private class VerifierProxy {
+ private Integer listenPort;
+ private String upstream;
+
+ VerifierProxy(Integer listenPort, String upstream) {
+ this.listenPort = listenPort;
+ this.upstream = upstream;
+ }
+ }
+}
diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/JwtProxyProvisioner.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/JwtProxyProvisioner.java
new file mode 100644
index 000000000000..acab5bd7a06d
--- /dev/null
+++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/JwtProxyProvisioner.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2012-2018 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.kubernetes.server.secure.jwtproxy;
+
+import static java.util.Collections.emptyList;
+import static java.util.Collections.emptyMap;
+import static org.eclipse.che.commons.lang.NameGenerator.generate;
+import static org.eclipse.che.workspace.infrastructure.kubernetes.Constants.CHE_ORIGINAL_NAME_LABEL;
+import static org.eclipse.che.workspace.infrastructure.kubernetes.server.KubernetesServerExposer.SERVER_PREFIX;
+import static org.eclipse.che.workspace.infrastructure.kubernetes.server.KubernetesServerExposer.SERVER_UNIQUE_PART_SIZE;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableMap;
+import io.fabric8.kubernetes.api.model.ConfigMap;
+import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
+import io.fabric8.kubernetes.api.model.ContainerBuilder;
+import io.fabric8.kubernetes.api.model.Pod;
+import io.fabric8.kubernetes.api.model.PodBuilder;
+import io.fabric8.kubernetes.api.model.Service;
+import io.fabric8.kubernetes.api.model.ServicePort;
+import io.fabric8.kubernetes.api.model.ServicePortBuilder;
+import io.fabric8.kubernetes.api.model.VolumeBuilder;
+import io.fabric8.kubernetes.api.model.VolumeMount;
+import java.security.KeyPair;
+import java.util.HashMap;
+import java.util.Map;
+import org.eclipse.che.api.core.model.workspace.config.MachineConfig;
+import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
+import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
+import org.eclipse.che.api.workspace.server.spi.InternalInfrastructureException;
+import org.eclipse.che.api.workspace.server.spi.environment.InternalMachineConfig;
+import org.eclipse.che.multiuser.machine.authentication.server.signature.SignatureKeyManager;
+import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
+import org.eclipse.che.workspace.infrastructure.kubernetes.server.ServerServiceBuilder;
+
+/** @author Sergii Leshchenko */
+public class JwtProxyProvisioner {
+
+ static final int FIRST_AVAILABLE_PORT = 4400;
+
+ static final int JWT_PROXY_MEMORY_LIMIT_BYTES = 128 * 1024 * 1024; // 128mb
+
+ static final String PUBLIC_KEY_HEADER = "-----BEGIN PUBLIC KEY-----\n";
+ static final String PUBLIC_KEY_FOOTER = "\n-----END PUBLIC KEY-----";
+
+ static final String JWTPROXY_IMAGE = "ksmster/jwtproxy";
+ static final String JWT_PROXY_CONFIG_FILE = "config.yaml";
+ static final String JWT_PROXY_MACHINE_NAME = "jwtproxy";
+
+ static final String JWT_PROXY_CONFIG_FOLDER = "/config";
+ static final String JWT_PROXY_PUBLIC_KEY_FILE = "mykey.pub";
+
+ private final SignatureKeyManager signatureKeyManager;
+
+ private final RuntimeIdentity identity;
+
+ private final JwtProxyConfigBuilder proxyConfigBuilder;
+
+ private final String serviceName;
+ private int availablePort;
+
+ public JwtProxyProvisioner(RuntimeIdentity identity, SignatureKeyManager signatureKeyManager) {
+ this.signatureKeyManager = signatureKeyManager;
+
+ this.identity = identity;
+
+ this.proxyConfigBuilder = new JwtProxyConfigBuilder(identity.getWorkspaceId());
+
+ this.serviceName = generate(SERVER_PREFIX, SERVER_UNIQUE_PART_SIZE) + "-jwtproxy";
+ this.availablePort = FIRST_AVAILABLE_PORT;
+ }
+
+ /**
+ * Modifies Kubernetes environment to expose the specified service port via JWTProxy.
+ *
+ * @param k8sEnv Kubernetes environment to modify
+ * @param backendServiceName service name that will be exposed
+ * @param backendServicePort service port that will be exposed
+ * @param protocol protocol that will be used for exposed port
+ * @return JWTProxy service port that expose the specified one
+ * @throws InfrastructureException if any exception occurs during port exposing
+ */
+ public ServicePort expose(
+ KubernetesEnvironment k8sEnv,
+ String backendServiceName,
+ int backendServicePort,
+ String protocol)
+ throws InfrastructureException {
+ ensureJwtProxyInjected(k8sEnv);
+
+ int listenPort = availablePort++;
+
+ proxyConfigBuilder.addVerifierProxy(
+ listenPort, "http://" + backendServiceName + ":" + backendServicePort);
+ k8sEnv
+ .getConfigMaps()
+ .get(getConfigMapName())
+ .getData()
+ .put(JWT_PROXY_CONFIG_FILE, proxyConfigBuilder.build());
+
+ ServicePort exposedPort =
+ new ServicePortBuilder()
+ .withName(backendServiceName + "-" + listenPort)
+ .withPort(listenPort)
+ .withProtocol(protocol)
+ .withNewTargetPort(listenPort)
+ .build();
+
+ k8sEnv.getServices().get(getServiceName()).getSpec().getPorts().add(exposedPort);
+
+ return exposedPort;
+ }
+
+ /** Returns service name that exposed JWTProxy Pod. */
+ public String getServiceName() {
+ return serviceName;
+ }
+
+ /** Returns config map name that will be mounted into JWTProxy Pod. */
+ @VisibleForTesting
+ String getConfigMapName() {
+ return "jwtproxy-config-" + identity.getWorkspaceId();
+ }
+
+ private void ensureJwtProxyInjected(KubernetesEnvironment k8sEnv) throws InfrastructureException {
+ if (!k8sEnv.getMachines().containsKey(JWT_PROXY_MACHINE_NAME)) {
+ k8sEnv.getMachines().put(JWT_PROXY_MACHINE_NAME, createJwtProxyMachine());
+ k8sEnv.getPods().put("jwtproxy", createJwtProxyPod(identity));
+
+ KeyPair keyPair = signatureKeyManager.getKeyPair();
+ if (keyPair == null) {
+ throw new InternalInfrastructureException(
+ "Key pair for machine authentication does not exist");
+ }
+ Map initConfigMapData = new HashMap<>();
+ initConfigMapData.put(
+ JWT_PROXY_PUBLIC_KEY_FILE,
+ PUBLIC_KEY_HEADER
+ + java.util.Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded())
+ + PUBLIC_KEY_FOOTER);
+
+ initConfigMapData.put(JWT_PROXY_CONFIG_FILE, proxyConfigBuilder.build());
+
+ ConfigMap jwtProxyConfigMap =
+ new ConfigMapBuilder()
+ .withNewMetadata()
+ .withName(getConfigMapName())
+ .endMetadata()
+ .withData(initConfigMapData)
+ .build();
+ k8sEnv.getConfigMaps().put(jwtProxyConfigMap.getMetadata().getName(), jwtProxyConfigMap);
+
+ Service jwtProxyService =
+ new ServerServiceBuilder()
+ .withName(serviceName)
+ .withSelectorEntry(CHE_ORIGINAL_NAME_LABEL, JWT_PROXY_MACHINE_NAME)
+ .withMachineName(JWT_PROXY_MACHINE_NAME)
+ .withPorts(emptyList())
+ .build();
+ k8sEnv.getServices().put(jwtProxyService.getMetadata().getName(), jwtProxyService);
+ }
+ }
+
+ private InternalMachineConfig createJwtProxyMachine() {
+ return new InternalMachineConfig(
+ null,
+ emptyMap(),
+ emptyMap(),
+ ImmutableMap.of(
+ MachineConfig.MEMORY_LIMIT_ATTRIBUTE, Integer.toString(JWT_PROXY_MEMORY_LIMIT_BYTES)),
+ null);
+ }
+
+ private Pod createJwtProxyPod(RuntimeIdentity identity) {
+ return new PodBuilder()
+ .withNewMetadata()
+ .withName("jwtproxy")
+ .withAnnotations(
+ ImmutableMap.of(
+ "org.eclipse.che.container.verifier.machine_name", JWT_PROXY_MACHINE_NAME))
+ .endMetadata()
+ .withNewSpec()
+ .withContainers(
+ new ContainerBuilder()
+ .withName("verifier")
+ .withImage(JWTPROXY_IMAGE)
+ .withVolumeMounts(
+ new VolumeMount(
+ JWT_PROXY_CONFIG_FOLDER + "/", "jwtproxy-config-volume", false, null))
+ .withArgs("-config", JWT_PROXY_CONFIG_FOLDER + "/" + JWT_PROXY_CONFIG_FILE)
+ .build())
+ .withVolumes(
+ new VolumeBuilder()
+ .withName("jwtproxy-config-volume")
+ .withNewConfigMap()
+ .withName("jwtproxy-config-" + identity.getWorkspaceId())
+ .endConfigMap()
+ .build())
+ .endSpec()
+ .build();
+ }
+}
diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/JwtProxySecureServerExposer.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/JwtProxySecureServerExposer.java
new file mode 100644
index 000000000000..aa2fbfc50b11
--- /dev/null
+++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/JwtProxySecureServerExposer.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2012-2018 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.kubernetes.server.secure.jwtproxy;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.inject.assistedinject.Assisted;
+import io.fabric8.kubernetes.api.model.ServicePort;
+import java.util.Map;
+import javax.inject.Inject;
+import org.eclipse.che.api.core.model.workspace.config.ServerConfig;
+import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
+import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
+import org.eclipse.che.multiuser.machine.authentication.server.signature.SignatureKeyManager;
+import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
+import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ExternalServerExposerStrategy;
+import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.SecureServerExposer;
+
+/**
+ * Expose secure servers with JWTProxy.
+ *
+ * @author Sergii Leshchenko
+ */
+public class JwtProxySecureServerExposer
+ implements SecureServerExposer {
+
+ private final ExternalServerExposerStrategy exposerStrategy;
+ private final JwtProxyProvisioner proxyProvisioner;
+
+ @VisibleForTesting
+ JwtProxySecureServerExposer(
+ JwtProxyProvisioner jwtProxyProvisioner, ExternalServerExposerStrategy exposerStrategy) {
+ this.exposerStrategy = exposerStrategy;
+ this.proxyProvisioner = jwtProxyProvisioner;
+ }
+
+ @Inject
+ public JwtProxySecureServerExposer(
+ @Assisted RuntimeIdentity identity,
+ SignatureKeyManager signatureKeyManager,
+ ExternalServerExposerStrategy exposerStrategy) {
+ this.exposerStrategy = exposerStrategy;
+
+ proxyProvisioner = new JwtProxyProvisioner(identity, signatureKeyManager);
+ }
+
+ @Override
+ public void expose(
+ T k8sEnv,
+ String machineName,
+ String serviceName,
+ ServicePort servicePort,
+ Map secureServers)
+ throws InfrastructureException {
+ ServicePort exposedServicePort =
+ proxyProvisioner.expose(
+ k8sEnv,
+ serviceName,
+ servicePort.getTargetPort().getIntVal(),
+ servicePort.getProtocol());
+
+ exposerStrategy.expose(
+ k8sEnv, machineName, proxyProvisioner.getServiceName(), exposedServicePort, secureServers);
+ }
+}
diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/JwtProxySecureServerExposerFactory.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/JwtProxySecureServerExposerFactory.java
new file mode 100644
index 000000000000..b8d17c51dc3a
--- /dev/null
+++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/JwtProxySecureServerExposerFactory.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2012-2018 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.kubernetes.server.secure.jwtproxy;
+
+import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
+import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
+
+/** @author Sergii Leshchenko */
+public interface JwtProxySecureServerExposerFactory {
+ JwtProxySecureServerExposer create(RuntimeIdentity identity);
+}
diff --git a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/JwtProxyConfigBuilderTest.java b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/JwtProxyConfigBuilderTest.java
new file mode 100644
index 000000000000..0334798a41fe
--- /dev/null
+++ b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/JwtProxyConfigBuilderTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2012-2018 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.kubernetes.server.secure.jwtproxy;
+
+import static org.testng.Assert.assertEquals;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.testng.reporters.Files;
+
+/**
+ * Tests {@link JwtProxyConfigBuilder}.
+ *
+ * @author Sergii Leshchenko
+ */
+public class JwtProxyConfigBuilderTest {
+
+ private JwtProxyConfigBuilder jwtProxyConfigBuilder;
+
+ @BeforeMethod
+ public void setUp() {
+ jwtProxyConfigBuilder = new JwtProxyConfigBuilder("workspace123");
+ }
+
+ @Test
+ public void shouldBuildJwtProxyConfigInYamlFormat() throws Exception {
+ // given
+ jwtProxyConfigBuilder.addVerifierProxy(8080, "http://tomcat:8080");
+ jwtProxyConfigBuilder.addVerifierProxy(4101, "ws://terminal:4101");
+
+ // when
+ String jwtProxyConfigYaml = jwtProxyConfigBuilder.build();
+
+ // then
+ assertEquals(
+ jwtProxyConfigYaml,
+ Files.readFile(getClass().getClassLoader().getResourceAsStream("jwtproxy-confg.yaml")));
+ }
+}
diff --git a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/JwtProxyProvisionerTest.java b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/JwtProxyProvisionerTest.java
new file mode 100644
index 000000000000..2fa60c93b73a
--- /dev/null
+++ b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/JwtProxyProvisionerTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2012-2018 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.kubernetes.server.secure.jwtproxy;
+
+import static org.eclipse.che.workspace.infrastructure.kubernetes.server.KubernetesServerExposer.SERVER_PREFIX;
+import static org.eclipse.che.workspace.infrastructure.kubernetes.server.KubernetesServerExposer.SERVER_UNIQUE_PART_SIZE;
+import static org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.JwtProxyProvisioner.JWT_PROXY_CONFIG_FILE;
+import static org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.JwtProxyProvisioner.JWT_PROXY_PUBLIC_KEY_FILE;
+import static org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.JwtProxyProvisioner.PUBLIC_KEY_FOOTER;
+import static org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.JwtProxyProvisioner.PUBLIC_KEY_HEADER;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import io.fabric8.kubernetes.api.model.ConfigMap;
+import io.fabric8.kubernetes.api.model.Pod;
+import io.fabric8.kubernetes.api.model.Service;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.util.Base64;
+import java.util.regex.Pattern;
+import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
+import org.eclipse.che.api.workspace.server.model.impl.RuntimeIdentityImpl;
+import org.eclipse.che.api.workspace.server.spi.environment.InternalMachineConfig;
+import org.eclipse.che.multiuser.machine.authentication.server.signature.SignatureKeyManager;
+import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
+import org.mockito.Mock;
+import org.mockito.testng.MockitoTestNGListener;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Listeners;
+import org.testng.annotations.Test;
+
+/**
+ * Tests {@link JwtProxyProvisioner}.
+ *
+ * @author Sergii Leshchenko
+ */
+@Listeners(MockitoTestNGListener.class)
+public class JwtProxyProvisionerTest {
+
+ private static final String WORKSPACE_ID = "workspace123";
+ private static final Pattern JWTPROXY_SERVICE_NAME_PATTERN =
+ Pattern.compile(SERVER_PREFIX + "\\w{" + SERVER_UNIQUE_PART_SIZE + "}-jwtproxy");
+ private final RuntimeIdentity runtimeId =
+ new RuntimeIdentityImpl(WORKSPACE_ID, "env123", "owner123");
+
+ @Mock private SignatureKeyManager signatureKeyManager;
+ private KeyPair keyPair;
+ @Mock private PublicKey publicKey;
+
+ private JwtProxyProvisioner jwtProxyProvisioner;
+ private KubernetesEnvironment k8sEnv;
+
+ @BeforeMethod
+ public void setUp() {
+ keyPair = new KeyPair(publicKey, null);
+ when(signatureKeyManager.getKeyPair()).thenReturn(keyPair);
+ when(publicKey.getEncoded()).thenReturn("publickey".getBytes());
+
+ jwtProxyProvisioner = new JwtProxyProvisioner(runtimeId, signatureKeyManager);
+ k8sEnv = KubernetesEnvironment.builder().build();
+ }
+
+ @Test
+ public void shouldReturnGeneratedJwtProxyServiceName() {
+ // when
+ String jwtProxyServiceName = jwtProxyProvisioner.getServiceName();
+
+ // then
+ assertTrue(JWTPROXY_SERVICE_NAME_PATTERN.matcher(jwtProxyServiceName).matches());
+ }
+
+ @Test
+ public void shouldReturnGeneratedJwtProxyConfigMapName() {
+ // when
+ String jwtProxyConfigMap = jwtProxyProvisioner.getConfigMapName();
+
+ // then
+ assertEquals(jwtProxyConfigMap, "jwtproxy-config-" + WORKSPACE_ID);
+ }
+
+ @Test
+ public void shouldProvisionJwtProxyRelatedObjectsIntoKubernetesEnvironment() throws Exception {
+ // when
+ jwtProxyProvisioner.expose(k8sEnv, "terminal", 4401, "TCP");
+
+ // then
+ InternalMachineConfig jwtProxyMachine =
+ k8sEnv.getMachines().get(JwtProxyProvisioner.JWT_PROXY_MACHINE_NAME);
+ assertNotNull(jwtProxyMachine);
+
+ ConfigMap configMap = k8sEnv.getConfigMaps().get(jwtProxyProvisioner.getConfigMapName());
+ assertNotNull(configMap);
+ assertEquals(
+ configMap.getData().get(JWT_PROXY_PUBLIC_KEY_FILE),
+ PUBLIC_KEY_HEADER
+ + Base64.getEncoder().encodeToString("publickey".getBytes())
+ + PUBLIC_KEY_FOOTER);
+ assertNotNull(configMap.getData().get(JWT_PROXY_CONFIG_FILE));
+
+ Pod jwtProxyPod = k8sEnv.getPods().get("jwtproxy");
+ assertNotNull(jwtProxyPod);
+
+ Service jwtProxyService = k8sEnv.getServices().get(jwtProxyProvisioner.getServiceName());
+ assertNotNull(jwtProxyService);
+ }
+}
diff --git a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/JwtProxySecureServerExposerTest.java b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/JwtProxySecureServerExposerTest.java
new file mode 100644
index 000000000000..ba19af057a96
--- /dev/null
+++ b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/JwtProxySecureServerExposerTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2012-2018 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.kubernetes.server.secure.jwtproxy;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import com.google.common.collect.ImmutableMap;
+import io.fabric8.kubernetes.api.model.IntOrString;
+import io.fabric8.kubernetes.api.model.ServicePort;
+import java.util.Map;
+import org.eclipse.che.api.core.model.workspace.config.ServerConfig;
+import org.eclipse.che.api.workspace.server.model.impl.ServerConfigImpl;
+import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
+import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ExternalServerExposerStrategy;
+import org.mockito.Mock;
+import org.mockito.testng.MockitoTestNGListener;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Listeners;
+import org.testng.annotations.Test;
+
+/**
+ * Tests {@link JwtProxySecureServerExposer}
+ *
+ * @author Sergii Leshchenko
+ */
+@Listeners(MockitoTestNGListener.class)
+public class JwtProxySecureServerExposerTest {
+
+ private static final String MACHINE_SERVICE_NAME = "service123";
+ private static final String MACHINE_NAME = "machine123";
+ public static final String JWT_PROXY_SERVICE_NAME = "jwtProxyServiceName";
+
+ @Mock private KubernetesEnvironment k8sEnv;
+ @Mock private JwtProxyProvisioner jwtProxyProvisioner;
+ @Mock private ExternalServerExposerStrategy externalServerExposer;
+
+ private JwtProxySecureServerExposer secureServerExposer;
+
+ @BeforeMethod
+ public void setUp() {
+ secureServerExposer =
+ new JwtProxySecureServerExposer<>(jwtProxyProvisioner, externalServerExposer);
+ }
+
+ @Test
+ public void shouldExposeSecureServersWithNewJwtProxyServicePort() throws Exception {
+ // given
+ ServicePort machineServicePort = new ServicePort();
+ machineServicePort.setTargetPort(new IntOrString(8080));
+ machineServicePort.setProtocol("TCP");
+ Map servers =
+ ImmutableMap.of(
+ "server1",
+ new ServerConfigImpl("8080/tcp", "http", "/api", ImmutableMap.of("secure", "true")),
+ "server2",
+ new ServerConfigImpl("8080/tcp", "ws", "/connect", ImmutableMap.of("secure", "true")));
+
+ ServicePort jwtProxyServicePort = new ServicePort();
+ doReturn(jwtProxyServicePort)
+ .when(jwtProxyProvisioner)
+ .expose(any(), anyString(), anyInt(), anyString());
+
+ when(jwtProxyProvisioner.getServiceName()).thenReturn(JWT_PROXY_SERVICE_NAME);
+
+ // when
+ secureServerExposer.expose(
+ k8sEnv, MACHINE_NAME, MACHINE_SERVICE_NAME, machineServicePort, servers);
+
+ // then
+ verify(jwtProxyProvisioner).expose(k8sEnv, MACHINE_SERVICE_NAME, 8080, "TCP");
+ verify(externalServerExposer)
+ .expose(k8sEnv, MACHINE_NAME, JWT_PROXY_SERVICE_NAME, jwtProxyServicePort, servers);
+ }
+}
diff --git a/infrastructures/kubernetes/src/test/resources/jwtproxy-confg.yaml b/infrastructures/kubernetes/src/test/resources/jwtproxy-confg.yaml
new file mode 100644
index 000000000000..a6f83c5c2401
--- /dev/null
+++ b/infrastructures/kubernetes/src/test/resources/jwtproxy-confg.yaml
@@ -0,0 +1,40 @@
+jwtproxy:
+ verifier_proxies:
+ - listen_addr: :8080
+ verifier:
+ upstream: http://tomcat:8080/
+ audience: http://workspace123
+ max_skew: 1m
+ max_ttl: 3h
+ key_server:
+ type: preshared
+ options:
+ issuer: wsmaster
+ key_id: mykey
+ public_key_path: /config/mykey.pub
+ claims_verifiers:
+ - type: static
+ options:
+ iss: wsmaster
+ nonce_storage:
+ type: void
+ - listen_addr: :4101
+ verifier:
+ upstream: ws://terminal:4101/
+ audience: http://workspace123
+ max_skew: 1m
+ max_ttl: 3h
+ key_server:
+ type: preshared
+ options:
+ issuer: wsmaster
+ key_id: mykey
+ public_key_path: /config/mykey.pub
+ claims_verifiers:
+ - type: static
+ options:
+ iss: wsmaster
+ nonce_storage:
+ type: void
+ signer_proxy:
+ enabled: false
diff --git a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfraModule.java b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfraModule.java
index 99bdc580c9d1..2aac70ec95c4 100644
--- a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfraModule.java
+++ b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfraModule.java
@@ -42,6 +42,7 @@
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.env.LogsRootEnvVariableProvider;
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.server.ServersConverter;
import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ExternalServerExposerStrategy;
+import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.JwtProxySecureServerExposerFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.wsnext.KubernetesWorkspaceNextApplier;
import org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironment;
import org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironmentFactory;
@@ -67,6 +68,10 @@ protected void configure() {
install(new FactoryModuleBuilder().build(OpenShiftRuntimeFactory.class));
install(new FactoryModuleBuilder().build(StartSynchronizerFactory.class));
+ install(
+ new FactoryModuleBuilder()
+ .build(new TypeLiteral>() {}));
+
install(new FactoryModuleBuilder().build(KubernetesBootstrapperFactory.class));
bind(WorkspacePVCCleaner.class).asEagerSingleton();
bind(RemoveProjectOnWorkspaceRemove.class).asEagerSingleton();