diff --git a/assembly/assembly-root-war/src/main/webapp/_app/loader.js b/assembly/assembly-root-war/src/main/webapp/_app/loader.js index c5ebdf07186..7ebb3f11d07 100644 --- a/assembly/assembly-root-war/src/main/webapp/_app/loader.js +++ b/assembly/assembly-root-war/src/main/webapp/_app/loader.js @@ -278,10 +278,8 @@ class Loader { * @param {string} token */ asyncAuthenticate(redirectUrl, token) { - const re = new RegExp(/(https?:\/\/[^\/]+?)(?:$|\/).*/), - // \ / \ / - // scheme host:port - url = redirectUrl.replace(re, "$1" + "/jwt/auth"); + redirectUrl = new URL(redirectUrl); + const url = redirectUrl.origin + redirectUrl.pathname.replace("//", "/") + "jwt/auth"; return new Promise((resolve, reject) => { const request = new XMLHttpRequest(); request.open('GET', url); @@ -293,7 +291,7 @@ class Loader { return; } if (request.status !== 204) { - const errorMessage = 'Failed to authenticate: "' + this.getRequestErrorMessage(xhr) + '"'; + const errorMessage = 'Failed to authenticate: "' + this.getRequestErrorMessage(request) + '"'; reject(new Error(errorMessage)); return; } 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 60cd85e29b7..a710e27669d 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 @@ -620,7 +620,7 @@ che.server.secure_exposer=default che.server.secure_exposer.jwtproxy.token.issuer=wsmaster che.server.secure_exposer.jwtproxy.token.ttl=8800h che.server.secure_exposer.jwtproxy.auth.loader.path=/_app/loader.html -che.server.secure_exposer.jwtproxy.image=eclipse/che-jwtproxy:latest +che.server.secure_exposer.jwtproxy.image=quay.io/eclipse/che-jwtproxy:dbd0578 che.server.secure_exposer.jwtproxy.memory_limit=128mb 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 74318f9c042..4f4eb0e0386 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 @@ -17,9 +17,9 @@ import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.pvc.CommonPVCStrategy.COMMON_STRATEGY; import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.pvc.PerWorkspacePVCStrategy.PER_WORKSPACE_STRATEGY; import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.pvc.UniqueWorkspacePVCStrategy.UNIQUE_STRATEGY; -import static org.eclipse.che.workspace.infrastructure.kubernetes.server.external.DefaultHostIngressServiceExposureStrategy.DEFAULT_HOST_STRATEGY; -import static org.eclipse.che.workspace.infrastructure.kubernetes.server.external.MultiHostIngressServiceExposureStrategy.MULTI_HOST_STRATEGY; -import static org.eclipse.che.workspace.infrastructure.kubernetes.server.external.SingleHostIngressServiceExposureStrategy.SINGLE_HOST_STRATEGY; +import static org.eclipse.che.workspace.infrastructure.kubernetes.server.external.DefaultHostExternalServiceExposureStrategy.DEFAULT_HOST_STRATEGY; +import static org.eclipse.che.workspace.infrastructure.kubernetes.server.external.MultiHostExternalServiceExposureStrategy.MULTI_HOST_STRATEGY; +import static org.eclipse.che.workspace.infrastructure.kubernetes.server.external.SingleHostExternalServiceExposureStrategy.SINGLE_HOST_STRATEGY; import com.google.inject.AbstractModule; import com.google.inject.TypeLiteral; @@ -60,11 +60,11 @@ 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.IngressAnnotationsProvider; -import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.DefaultHostIngressServiceExposureStrategy; -import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.IngressServiceExposureStrategy; +import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.DefaultHostExternalServiceExposureStrategy; +import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ExternalServiceExposureStrategy; import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.IngressServiceExposureStrategyProvider; -import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.MultiHostIngressServiceExposureStrategy; -import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.SingleHostIngressServiceExposureStrategy; +import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.MultiHostExternalServiceExposureStrategy; +import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.SingleHostExternalServiceExposureStrategy; import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.DefaultSecureServersFactory; import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.SecureServerExposerFactory; import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.SecureServerExposerFactoryProvider; @@ -117,18 +117,18 @@ protected void configure() { .addBinding() .to(KubernetesClientTermination.class); - MapBinder ingressStrategies = - MapBinder.newMapBinder(binder(), String.class, IngressServiceExposureStrategy.class); + MapBinder ingressStrategies = + MapBinder.newMapBinder(binder(), String.class, ExternalServiceExposureStrategy.class); ingressStrategies .addBinding(MULTI_HOST_STRATEGY) - .to(MultiHostIngressServiceExposureStrategy.class); + .to(MultiHostExternalServiceExposureStrategy.class); ingressStrategies .addBinding(SINGLE_HOST_STRATEGY) - .to(SingleHostIngressServiceExposureStrategy.class); + .to(SingleHostExternalServiceExposureStrategy.class); ingressStrategies .addBinding(DEFAULT_HOST_STRATEGY) - .to(DefaultHostIngressServiceExposureStrategy.class); - bind(IngressServiceExposureStrategy.class) + .to(DefaultHostExternalServiceExposureStrategy.class); + bind(ExternalServiceExposureStrategy.class) .toProvider(IngressServiceExposureStrategyProvider.class); bind(ServersConverter.class).to(new TypeLiteral>() {}); diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/DefaultHostIngressServiceExposureStrategy.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/DefaultHostExternalServiceExposureStrategy.java similarity index 86% rename from infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/DefaultHostIngressServiceExposureStrategy.java rename to infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/DefaultHostExternalServiceExposureStrategy.java index aac53c52d62..62850687d26 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/DefaultHostIngressServiceExposureStrategy.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/DefaultHostExternalServiceExposureStrategy.java @@ -40,17 +40,17 @@ * @author Sergii Leshchenko * @author Guy Daich */ -public class DefaultHostIngressServiceExposureStrategy implements IngressServiceExposureStrategy { +public class DefaultHostExternalServiceExposureStrategy implements ExternalServiceExposureStrategy { public static final String DEFAULT_HOST_STRATEGY = "default-host"; @Override - public String getIngressHost(String serviceName, ServicePort servicePort) { + public String getExternalHost(String serviceName, ServicePort servicePort) { return null; } @Override - public String getIngressPath(String serviceName, ServicePort servicePort) { + public String getExternalPath(String serviceName, ServicePort servicePort) { return "/" + serviceName + "/" + servicePort.getName(); } } diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/ExternalServerExposer.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/ExternalServerExposer.java index 89053b61a28..dcad785bbdf 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/ExternalServerExposer.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/ExternalServerExposer.java @@ -29,13 +29,13 @@ public class ExternalServerExposer { */ static final String PATH_TRANSFORM_PATH_CATCH = "%s"; - private final IngressServiceExposureStrategy strategy; + private final ExternalServiceExposureStrategy strategy; private final Map ingressAnnotations; private final String pathTransformFmt; @Inject public ExternalServerExposer( - IngressServiceExposureStrategy strategy, + ExternalServiceExposureStrategy strategy, @Named("infra.kubernetes.ingress.annotations") Map annotations, @Nullable @Named("che.infra.kubernetes.ingress.path_transform") String pathTransformFmt) { this.strategy = strategy; @@ -71,7 +71,7 @@ private Ingress generateIngress( Map ingressesServers) { ExternalServerIngressBuilder ingressBuilder = new ExternalServerIngressBuilder(); - String host = strategy.getIngressHost(serviceName, servicePort); + String host = strategy.getExternalHost(serviceName, servicePort); if (host != null) { ingressBuilder = ingressBuilder.withHost(host); } @@ -80,7 +80,7 @@ private Ingress generateIngress( .withPath( String.format( pathTransformFmt, - ensureEndsWithSlash(strategy.getIngressPath(serviceName, servicePort)))) + ensureEndsWithSlash(strategy.getExternalPath(serviceName, servicePort)))) .withName(getIngressName(serviceName, servicePort)) .withMachineName(machineName) .withServiceName(serviceName) diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/IngressServiceExposureStrategy.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/ExternalServiceExposureStrategy.java similarity index 80% rename from infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/IngressServiceExposureStrategy.java rename to infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/ExternalServiceExposureStrategy.java index b9caa9f9827..01e3a6551df 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/IngressServiceExposureStrategy.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/ExternalServiceExposureStrategy.java @@ -18,12 +18,12 @@ * Implementations of this strategy are used by the {@link ExternalServerExposer} to compose an * Ingress rule that exposes the services. */ -public interface IngressServiceExposureStrategy { +public interface ExternalServiceExposureStrategy { /** Returns a host that should be used to expose the service */ @Nullable - String getIngressHost(String serviceName, ServicePort servicePort); + String getExternalHost(String serviceName, ServicePort servicePort); /** Returns the path on which the service should be exposed */ - String getIngressPath(String serviceName, ServicePort servicePort); + String getExternalPath(String serviceName, ServicePort servicePort); } diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/IngressServiceExposureStrategyProvider.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/IngressServiceExposureStrategyProvider.java index d281bc0db8a..cbe23bb0e51 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/IngressServiceExposureStrategyProvider.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/IngressServiceExposureStrategyProvider.java @@ -22,16 +22,16 @@ @Singleton public class IngressServiceExposureStrategyProvider - implements Provider { + implements Provider { - static final String STRATEGY_PROPERTY = "che.infra.kubernetes.server_strategy"; + public static final String STRATEGY_PROPERTY = "che.infra.kubernetes.server_strategy"; - private final IngressServiceExposureStrategy namingStrategy; + private final ExternalServiceExposureStrategy namingStrategy; @Inject public IngressServiceExposureStrategyProvider( @Named(STRATEGY_PROPERTY) String strategy, - Map strategies) { + Map strategies) { namingStrategy = strategies.get(strategy); @@ -42,7 +42,7 @@ public IngressServiceExposureStrategyProvider( } @Override - public IngressServiceExposureStrategy get() { + public ExternalServiceExposureStrategy get() { return namingStrategy; } } diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/MultiHostIngressServiceExposureStrategy.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/MultiHostExternalServiceExposureStrategy.java similarity index 88% rename from infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/MultiHostIngressServiceExposureStrategy.java rename to infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/MultiHostExternalServiceExposureStrategy.java index 07565ef950c..4fdcba9a69d 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/MultiHostIngressServiceExposureStrategy.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/MultiHostExternalServiceExposureStrategy.java @@ -44,7 +44,7 @@ * @author Sergii Leshchenko * @author Guy Daich */ -public class MultiHostIngressServiceExposureStrategy implements IngressServiceExposureStrategy { +public class MultiHostExternalServiceExposureStrategy implements ExternalServiceExposureStrategy { public static final String MULTI_HOST_STRATEGY = "multi-host"; private static final String INGRESS_DOMAIN_PROPERTY = "che.infra.kubernetes.ingress.domain"; @@ -52,7 +52,7 @@ public class MultiHostIngressServiceExposureStrategy implements IngressServiceEx private final String domain; @Inject - public MultiHostIngressServiceExposureStrategy( + public MultiHostExternalServiceExposureStrategy( @Named(INGRESS_DOMAIN_PROPERTY) String domain, @Named(STRATEGY_PROPERTY) String strategy) { if (Strings.isNullOrEmpty(domain) && MULTI_HOST_STRATEGY.equals(strategy)) { throw new ConfigurationException( @@ -65,12 +65,12 @@ public MultiHostIngressServiceExposureStrategy( } @Override - public String getIngressHost(String serviceName, ServicePort servicePort) { + public String getExternalHost(String serviceName, ServicePort servicePort) { return serviceName + "-" + servicePort.getName() + "." + domain; } @Override - public String getIngressPath(String serviceName, ServicePort servicePort) { + public String getExternalPath(String serviceName, ServicePort servicePort) { return "/"; } } diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/SingleHostIngressServiceExposureStrategy.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/SingleHostExternalServiceExposureStrategy.java similarity index 82% rename from infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/SingleHostIngressServiceExposureStrategy.java rename to infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/SingleHostExternalServiceExposureStrategy.java index 1415be590ae..f614ba4a8c0 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/SingleHostIngressServiceExposureStrategy.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/SingleHostExternalServiceExposureStrategy.java @@ -42,23 +42,23 @@ * @author Sergii Leshchenko * @author Guy Daich */ -public class SingleHostIngressServiceExposureStrategy implements IngressServiceExposureStrategy { +public class SingleHostExternalServiceExposureStrategy implements ExternalServiceExposureStrategy { public static final String SINGLE_HOST_STRATEGY = "single-host"; private final String cheHost; @Inject - public SingleHostIngressServiceExposureStrategy(@Named("che.host") String cheHost) { + public SingleHostExternalServiceExposureStrategy(@Named("che.host") String cheHost) { this.cheHost = cheHost; } @Override - public String getIngressHost(String serviceName, ServicePort servicePort) { + public String getExternalHost(String serviceName, ServicePort servicePort) { return cheHost; } @Override - public String getIngressPath(String serviceName, ServicePort servicePort) { + public String getExternalPath(String serviceName, ServicePort servicePort) { return "/" + serviceName + "/" + servicePort.getName(); } } diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/CookiePathStrategy.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/CookiePathStrategy.java new file mode 100644 index 00000000000..d4d59fe8537 --- /dev/null +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/CookiePathStrategy.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2012-2018 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy; + +import static java.lang.String.format; +import static org.eclipse.che.workspace.infrastructure.kubernetes.server.external.DefaultHostExternalServiceExposureStrategy.DEFAULT_HOST_STRATEGY; +import static org.eclipse.che.workspace.infrastructure.kubernetes.server.external.MultiHostExternalServiceExposureStrategy.MULTI_HOST_STRATEGY; +import static org.eclipse.che.workspace.infrastructure.kubernetes.server.external.SingleHostExternalServiceExposureStrategy.SINGLE_HOST_STRATEGY; + +import io.fabric8.kubernetes.api.model.ServicePort; +import java.util.function.BiFunction; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.IngressServiceExposureStrategyProvider; + +/** + * The cookie path for the access token cookie is server-strategy dependent. This class represents + * the different strategies for getting the cookie path. + * + *

Note that instead of going with full-blown strategy pattern and different implementations of + * some interface and a provider for the currently active strategy (as is done for example with + * {@link + * org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ExternalServiceExposureStrategy}), + * this class merely internally uses different functions for different service exposure strategies. + * This is done because the full-blown stragegy pattern implementation felt like over-engineering + * when compared with the simplicity of the functions. + */ +@Singleton +public class CookiePathStrategy { + + private final BiFunction getCookiePath; + + @Inject + public CookiePathStrategy( + @Named(IngressServiceExposureStrategyProvider.STRATEGY_PROPERTY) String serverStrategy) { + switch (serverStrategy) { + case MULTI_HOST_STRATEGY: + getCookiePath = (__, ___) -> "/"; + break; + case SINGLE_HOST_STRATEGY: + case DEFAULT_HOST_STRATEGY: + getCookiePath = (serviceName, __) -> serviceName; + break; + default: + throw new IllegalArgumentException( + format("Unsupported server strategy: %s", serverStrategy)); + } + } + + public String get(String serviceName, ServicePort port) { + return getCookiePath.apply(serviceName, port); + } +} 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 index 0fd969ddcf4..6bfbbc5fbb4 100644 --- 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 @@ -71,9 +71,31 @@ public JwtProxyConfigBuilder( this.ttl = ttl; } + /** + * Adds a proxy before a service that will perform the JWT authentication on its behalf. + * + * @param listenPort the port to listen on + * @param upstream the URL to the backend service this proxy should be put in front of + * @param excludes the list of unsecured paths that the proxy should let pass through + * @param cookiesAuthEnabled should the JWT proxy use cookies? + * @param cookiePath the path of the cookie. This is should either be "/" or some portion of the + * URL the JWT proxy will be exposed on. It is used to enable using different proxies for + * different services, each with a different auth cookie. Super useful for having multiple + * workspaces, each authenticated with its machine token. + * @param publicBasePath the prefix used to generate the redirect back to the original page from + * the auth page by the JWT proxy. This is to work across path-rewriting proxies like nginx + * ingress controller. + */ public void addVerifierProxy( - Integer listenPort, String upstream, Set excludes, Boolean cookiesAuthEnabled) { - verifierProxies.add(new VerifierProxy(listenPort, upstream, excludes, cookiesAuthEnabled)); + Integer listenPort, + String upstream, + Set excludes, + Boolean cookiesAuthEnabled, + String cookiePath, + String publicBasePath) { + verifierProxies.add( + new VerifierProxy( + listenPort, upstream, excludes, cookiesAuthEnabled, cookiePath, publicBasePath)); } public String build() throws InternalInfrastructureException { @@ -104,6 +126,7 @@ public String build() throws InternalInfrastructureException { "public_key_path", JWT_PROXY_CONFIG_FOLDER + '/' + JWT_PROXY_PUBLIC_KEY_FILE))) .withCookiesEnabled(verifierProxy.cookiesAuthEnabled) + .withCookiePath(ensureStartsWithSlash(verifierProxy.cookiePath)) .withClaimsVerifier( Collections.singleton( new RegistrableComponentConfig() @@ -119,6 +142,10 @@ public String build() throws InternalInfrastructureException { verifierConfig.setAuthUrl(authPageUrl.toString()); } + if (verifierProxy.publicBasePath != null) { + verifierConfig.setPublicBasePath(verifierProxy.publicBasePath); + } + VerifierProxyConfig proxyConfig = new VerifierProxyConfig() .withListenAddr(":" + verifierProxy.listenPort) @@ -135,18 +162,35 @@ public String build() throws InternalInfrastructureException { } } - private class VerifierProxy { - private Integer listenPort; - private String upstream; - private Set excludes; - private boolean cookiesAuthEnabled; + private static final class VerifierProxy { + final Integer listenPort; + final String upstream; + final Set excludes; + final boolean cookiesAuthEnabled; + final String cookiePath; + final String publicBasePath; VerifierProxy( - Integer listenPort, String upstream, Set excludes, boolean cookiesAuthEnabled) { + Integer listenPort, + String upstream, + Set excludes, + boolean cookiesAuthEnabled, + String cookiePath, + String publicBasePath) { this.listenPort = listenPort; this.upstream = upstream; this.excludes = excludes; this.cookiesAuthEnabled = cookiesAuthEnabled; + this.cookiePath = cookiePath; + this.publicBasePath = publicBasePath; + } + } + + private static String ensureStartsWithSlash(String val) { + if (isNullOrEmpty(val)) { + return "/"; + } else { + return val.charAt(0) == '/' ? val : "/" + val; } } } 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 index ec64a009089..b9b7620093c 100644 --- 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 @@ -58,6 +58,7 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.Names; import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment; import org.eclipse.che.workspace.infrastructure.kubernetes.server.ServerServiceBuilder; +import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ExternalServiceExposureStrategy; import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.factory.JwtProxyConfigBuilderFactory; /** @@ -109,10 +110,15 @@ public class JwtProxyProvisioner { private final String serviceName; private int availablePort; + private final ExternalServiceExposureStrategy externalServiceExposureStrategy; + private final CookiePathStrategy cookiePathStrategy; + @Inject public JwtProxyProvisioner( SignatureKeyManager signatureKeyManager, JwtProxyConfigBuilderFactory jwtProxyConfigBuilderFactory, + ExternalServiceExposureStrategy externalServiceExposureStrategy, + CookiePathStrategy cookiePathStrategy, @Named("che.server.secure_exposer.jwtproxy.image") String jwtProxyImage, @Named("che.server.secure_exposer.jwtproxy.memory_limit") String memoryLimitBytes, @Assisted RuntimeIdentity identity) { @@ -121,8 +127,13 @@ public JwtProxyProvisioner( this.jwtProxyImage = jwtProxyImage; this.proxyConfigBuilder = jwtProxyConfigBuilderFactory.create(identity.getWorkspaceId()); + + this.externalServiceExposureStrategy = externalServiceExposureStrategy; + this.cookiePathStrategy = cookiePathStrategy; + this.identity = identity; this.serviceName = generate(SERVER_PREFIX, SERVER_UNIQUE_PART_SIZE) + "-jwtproxy"; + this.availablePort = FIRST_AVAILABLE_PORT; long memoryLimitLong = Size.parseSizeToMegabytes(memoryLimitBytes) * MEGABYTES_TO_BYTES_DIVIDER; this.attributes = @@ -149,7 +160,7 @@ public JwtProxyProvisioner( public ServicePort expose( KubernetesEnvironment k8sEnv, String backendServiceName, - int backendServicePort, + ServicePort backendServicePort, String protocol, Map secureServers) throws InfrastructureException { @@ -181,17 +192,6 @@ public ServicePort expose( } } - proxyConfigBuilder.addVerifierProxy( - listenPort, - "http://" + backendServiceName + ":" + backendServicePort, - excludes, - cookiesAuthEnabled); - k8sEnv - .getConfigMaps() - .get(getConfigMapName()) - .getData() - .put(JWT_PROXY_CONFIG_FILE, proxyConfigBuilder.build()); - ServicePort exposedPort = new ServicePortBuilder() .withName("server-" + listenPort) @@ -200,7 +200,20 @@ public ServicePort expose( .withNewTargetPort(listenPort) .build(); - k8sEnv.getServices().get(getServiceName()).getSpec().getPorts().add(exposedPort); + k8sEnv.getServices().get(serviceName).getSpec().getPorts().add(exposedPort); + + proxyConfigBuilder.addVerifierProxy( + listenPort, + "http://" + backendServiceName + ":" + backendServicePort.getTargetPort().getIntVal(), + excludes, + cookiesAuthEnabled, + cookiePathStrategy.get(serviceName, exposedPort), + externalServiceExposureStrategy.getExternalPath(serviceName, exposedPort)); + k8sEnv + .getConfigMaps() + .get(getConfigMapName()) + .getData() + .put(JWT_PROXY_CONFIG_FILE, proxyConfigBuilder.build()); return exposedPort; } 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 index daddbaaa33d..ee15085a29a 100644 --- 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 @@ -70,11 +70,7 @@ public void expose( throws InfrastructureException { ServicePort exposedServicePort = proxyProvisioner.expose( - k8sEnv, - serviceName, - servicePort.getTargetPort().getIntVal(), - servicePort.getProtocol(), - secureServers); + k8sEnv, serviceName, servicePort, servicePort.getProtocol(), secureServers); exposer.expose( k8sEnv, machineName, proxyProvisioner.getServiceName(), exposedServicePort, secureServers); diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/model/VerifierConfig.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/model/VerifierConfig.java index 50ec1fff3f2..63e9e93b996 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/model/VerifierConfig.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/model/VerifierConfig.java @@ -50,6 +50,12 @@ public class VerifierConfig { private Set excludes; + @JsonProperty("public_base_path") + private String publicBasePath; + + @JsonProperty("cookie_path") + private String cookiePath; + public String getAudience() { return audience; } @@ -180,4 +186,30 @@ public VerifierConfig withCookiesEnabled(boolean cookiesEnabled) { this.cookiesEnabled = cookiesEnabled; return this; } + + public String getPublicBasePath() { + return publicBasePath; + } + + public void setPublicBasePath(String publicBasePath) { + this.publicBasePath = publicBasePath; + } + + public VerifierConfig withPublicBasePath(String publicBasePath) { + this.publicBasePath = publicBasePath; + return this; + } + + public String getCookiePath() { + return cookiePath; + } + + public void setCookiePath(String cookiePath) { + this.cookiePath = cookiePath; + } + + public VerifierConfig withCookiePath(String cookiePath) { + this.cookiePath = cookiePath; + return this; + } } diff --git a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/DefaultHostIngressServiceExposureStrategyTest.java b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/DefaultHostExternalServiceExposureStrategyTest.java similarity index 97% rename from infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/DefaultHostIngressServiceExposureStrategyTest.java rename to infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/DefaultHostExternalServiceExposureStrategyTest.java index 87e30a05c28..26071a203a7 100644 --- a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/DefaultHostIngressServiceExposureStrategyTest.java +++ b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/DefaultHostExternalServiceExposureStrategyTest.java @@ -36,7 +36,7 @@ import org.testng.annotations.Test; /** @author Guy Daich */ -public class DefaultHostIngressServiceExposureStrategyTest { +public class DefaultHostExternalServiceExposureStrategyTest { private static final Map ATTRIBUTES_MAP = singletonMap("key", "value"); private static final String MACHINE_NAME = "pod/main"; @@ -62,7 +62,7 @@ public void setUp() throws Exception { KubernetesEnvironment.builder().setPods(ImmutableMap.of("pod", pod)).build(); externalServerExposer = new ExternalServerExposer<>( - new DefaultHostIngressServiceExposureStrategy(), emptyMap(), "%s"); + new DefaultHostExternalServiceExposureStrategy(), emptyMap(), "%s"); } @Test diff --git a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/MultiHostIngressServiceExposureStrategyTest.java b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/MultiHostExternalServiceExposureStrategyTest.java similarity index 96% rename from infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/MultiHostIngressServiceExposureStrategyTest.java rename to infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/MultiHostExternalServiceExposureStrategyTest.java index 500288c3fb2..ad4247e18bc 100644 --- a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/MultiHostIngressServiceExposureStrategyTest.java +++ b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/external/MultiHostExternalServiceExposureStrategyTest.java @@ -14,7 +14,7 @@ import static java.util.Collections.emptyMap; import static java.util.Collections.singletonMap; import static org.eclipse.che.workspace.infrastructure.kubernetes.server.KubernetesServerExposer.SERVER_PREFIX; -import static org.eclipse.che.workspace.infrastructure.kubernetes.server.external.MultiHostIngressServiceExposureStrategy.MULTI_HOST_STRATEGY; +import static org.eclipse.che.workspace.infrastructure.kubernetes.server.external.MultiHostExternalServiceExposureStrategy.MULTI_HOST_STRATEGY; import static org.testng.Assert.assertEquals; import com.google.common.collect.ImmutableMap; @@ -37,7 +37,7 @@ import org.testng.annotations.Test; /** @author Guy Daich */ -public class MultiHostIngressServiceExposureStrategyTest { +public class MultiHostExternalServiceExposureStrategyTest { private static final Map ATTRIBUTES_MAP = singletonMap("key", "value"); private static final String MACHINE_NAME = "pod/main"; @@ -64,7 +64,7 @@ public void setUp() throws Exception { KubernetesEnvironment.builder().setPods(ImmutableMap.of("pod", pod)).build(); externalServerExposer = new ExternalServerExposer<>( - new MultiHostIngressServiceExposureStrategy(DOMAIN, MULTI_HOST_STRATEGY), + new MultiHostExternalServiceExposureStrategy(DOMAIN, MULTI_HOST_STRATEGY), emptyMap(), "%s"); } 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 index 6f3fd26166b..bb864603fab 100644 --- 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 @@ -45,11 +45,11 @@ public void shouldBuildJwtProxyConfigInYamlFormat() throws Exception { // given Set excludes = new HashSet<>(); jwtProxyConfigBuilder.addVerifierProxy( - 8080, "http://tomcat:8080", new HashSet<>(excludes), false); + 8080, "http://tomcat:8080", new HashSet<>(excludes), false, "", "/there"); excludes.add("/api/liveness"); excludes.add("/other/exclude"); jwtProxyConfigBuilder.addVerifierProxy( - 4101, "ws://terminal:4101", new HashSet<>(excludes), true); + 4101, "ws://terminal:4101", new HashSet<>(excludes), true, "/cookies", "/here"); // when String jwtProxyConfigYaml = jwtProxyConfigBuilder.build(); 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 index dd100e1982d..bdc4c3acd3a 100644 --- 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 @@ -14,6 +14,7 @@ import static org.eclipse.che.api.core.model.workspace.config.ServerConfig.SECURE_SERVER_COOKIES_AUTH_ENABLED_ATTRIBUTE; 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.external.MultiHostExternalServiceExposureStrategy.MULTI_HOST_STRATEGY; 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; @@ -30,8 +31,10 @@ import com.google.common.collect.ImmutableMap; import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.IntOrString; import io.fabric8.kubernetes.api.model.Pod; import io.fabric8.kubernetes.api.model.Service; +import io.fabric8.kubernetes.api.model.ServicePort; import java.net.URI; import java.security.KeyPair; import java.security.PublicKey; @@ -45,6 +48,7 @@ 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.external.ExternalServiceExposureStrategy; import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.factory.JwtProxyConfigBuilderFactory; import org.mockito.Mock; import org.mockito.testng.MockitoTestNGListener; @@ -69,6 +73,8 @@ public class JwtProxyProvisionerTest { @Mock private SignatureKeyManager signatureKeyManager; @Mock private PublicKey publicKey; @Mock private JwtProxyConfigBuilderFactory configBuilderFactory; + @Mock private ExternalServiceExposureStrategy externalServiceExposureStrategy; + private CookiePathStrategy cookiePathStrategy = new CookiePathStrategy(MULTI_HOST_STRATEGY); private JwtProxyProvisioner jwtProxyProvisioner; private KubernetesEnvironment k8sEnv; @@ -85,7 +91,13 @@ public void setUp() throws Exception { URI.create("http://che.api"), "iss", "1h", "", runtimeId.getWorkspaceId())); jwtProxyProvisioner = new JwtProxyProvisioner( - signatureKeyManager, configBuilderFactory, "eclipse/che-jwtproxy", "128mb", runtimeId); + signatureKeyManager, + configBuilderFactory, + externalServiceExposureStrategy, + cookiePathStrategy, + "eclipse/che-jwtproxy", + "128mb", + runtimeId); k8sEnv = KubernetesEnvironment.builder().build(); } @@ -113,9 +125,12 @@ public void shouldProvisionJwtProxyRelatedObjectsIntoKubernetesEnvironment() thr ServerConfigImpl secureServer = new ServerConfigImpl("4401/tcp", "ws", "/", Collections.emptyMap()); + ServicePort port = new ServicePort(); + port.setTargetPort(new IntOrString(4401)); + // when jwtProxyProvisioner.expose( - k8sEnv, "terminal", 4401, "TCP", ImmutableMap.of("server", secureServer)); + k8sEnv, "terminal", port, "TCP", ImmutableMap.of("server", secureServer)); // then InternalMachineConfig jwtProxyMachine = @@ -160,11 +175,14 @@ public void shouldThrowAnExceptionIsServersHaveDifferentValueForCookiesAuthEnabl ImmutableMap.of(SECURE_SERVER_COOKIES_AUTH_ENABLED_ATTRIBUTE, "false")); ServerConfigImpl server3 = new ServerConfigImpl("4401/tcp", "ws", "/", Collections.emptyMap()); + ServicePort port = new ServicePort(); + port.setTargetPort(new IntOrString(4401)); + // when jwtProxyProvisioner.expose( k8sEnv, "terminal", - 4401, + port, "TCP", ImmutableMap.of("server1", server1, "server2", server2, "server3", server3)); } @@ -177,7 +195,13 @@ public void shouldUseCookiesAuthEnabledFromServersConfigs() throws Exception { jwtProxyProvisioner = new JwtProxyProvisioner( - signatureKeyManager, configBuilderFactory, "eclipse/che-jwtproxy", "128mb", runtimeId); + signatureKeyManager, + configBuilderFactory, + externalServiceExposureStrategy, + cookiePathStrategy, + "eclipse/che-jwtproxy", + "128mb", + runtimeId); ServerConfigImpl server1 = new ServerConfigImpl( @@ -192,12 +216,15 @@ public void shouldUseCookiesAuthEnabledFromServersConfigs() throws Exception { "/", ImmutableMap.of(SECURE_SERVER_COOKIES_AUTH_ENABLED_ATTRIBUTE, "true")); + ServicePort port = new ServicePort(); + port.setTargetPort(new IntOrString(4401)); + // when jwtProxyProvisioner.expose( - k8sEnv, "terminal", 4401, "TCP", ImmutableMap.of("server1", server1, "server2", server2)); + k8sEnv, "terminal", port, "TCP", ImmutableMap.of("server1", server1, "server2", server2)); // then - verify(configBuilder).addVerifierProxy(any(), any(), any(), eq(true)); + verify(configBuilder).addVerifierProxy(any(), any(), any(), eq(true), any(), any()); } @Test @@ -208,16 +235,25 @@ public void shouldFalseValueAsDefaultForCookiesAuthEnabledAttribute() throws Exc jwtProxyProvisioner = new JwtProxyProvisioner( - signatureKeyManager, configBuilderFactory, "eclipse/che-jwtproxy", "128mb", runtimeId); + signatureKeyManager, + configBuilderFactory, + externalServiceExposureStrategy, + cookiePathStrategy, + "eclipse/che-jwtproxy", + "128mb", + runtimeId); ServerConfigImpl server1 = new ServerConfigImpl("4401/tcp", "http", "/", Collections.emptyMap()); + ServicePort port = new ServicePort(); + port.setTargetPort(new IntOrString(4401)); + // when jwtProxyProvisioner.expose( - k8sEnv, "terminal", 4401, "TCP", ImmutableMap.of("server1", server1)); + k8sEnv, "terminal", port, "TCP", ImmutableMap.of("server1", server1)); // then - verify(configBuilder).addVerifierProxy(any(), any(), any(), eq(false)); + verify(configBuilder).addVerifierProxy(any(), any(), any(), eq(false), any(), any()); } } 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 index 4e8675d8a0f..175e288d2c0 100644 --- 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 @@ -12,9 +12,9 @@ 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.anyMap; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -73,7 +73,7 @@ public void shouldExposeSecureServersWithNewJwtProxyServicePort() throws Excepti ServicePort jwtProxyServicePort = new ServicePort(); doReturn(jwtProxyServicePort) .when(jwtProxyProvisioner) - .expose(any(), anyString(), anyInt(), anyString(), anyMap()); + .expose(any(), anyString(), any(), anyString(), anyMap()); when(jwtProxyProvisioner.getServiceName()).thenReturn(JWT_PROXY_SERVICE_NAME); @@ -82,8 +82,15 @@ public void shouldExposeSecureServersWithNewJwtProxyServicePort() throws Excepti k8sEnv, MACHINE_NAME, MACHINE_SERVICE_NAME, machineServicePort, servers); // then - verify(jwtProxyProvisioner).expose(k8sEnv, MACHINE_SERVICE_NAME, 8080, "TCP", servers); + verify(jwtProxyProvisioner) + .expose( + eq(k8sEnv), eq(MACHINE_SERVICE_NAME), eq(machineServicePort), eq("TCP"), eq(servers)); verify(externalServerExposer) - .expose(k8sEnv, MACHINE_NAME, JWT_PROXY_SERVICE_NAME, jwtProxyServicePort, servers); + .expose( + eq(k8sEnv), + eq(MACHINE_NAME), + eq(JWT_PROXY_SERVICE_NAME), + eq(jwtProxyServicePort), + eq(servers)); } } diff --git a/infrastructures/kubernetes/src/test/resources/jwtproxy-confg.yaml b/infrastructures/kubernetes/src/test/resources/jwtproxy-confg.yaml index cd10e4a8fce..10f60db899e 100644 --- a/infrastructures/kubernetes/src/test/resources/jwtproxy-confg.yaml +++ b/infrastructures/kubernetes/src/test/resources/jwtproxy-confg.yaml @@ -11,6 +11,7 @@ jwtproxy: - options: iss: "wsmaster" type: "static" + cookie_path: "/" key_server: options: issuer: "wsmaster" @@ -21,6 +22,7 @@ jwtproxy: max_ttl: "1m" nonce_storage: type: "void" + public_base_path: "/there" upstream: "http://tomcat:8080" - listen_addr: ":4101" verifier: @@ -31,6 +33,7 @@ jwtproxy: - options: iss: "wsmaster" type: "static" + cookie_path: "/cookies" excludes: - "/api/liveness" - "/other/exclude" @@ -44,4 +47,5 @@ jwtproxy: max_ttl: "1m" nonce_storage: type: "void" + public_base_path: "/here" upstream: "ws://terminal:4101" 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 bc7293aeb96..fe2fc1dcdfe 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 @@ -60,9 +60,11 @@ 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.ExternalServerExposer; +import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ExternalServiceExposureStrategy; import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.DefaultSecureServersFactory; import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.SecureServerExposerFactory; import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.SecureServerExposerFactoryProvider; +import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.CookiePathStrategy; import org.eclipse.che.workspace.infrastructure.kubernetes.wsplugins.KubernetesPluginsToolingApplier; import org.eclipse.che.workspace.infrastructure.kubernetes.wsplugins.PluginBrokerManager; import org.eclipse.che.workspace.infrastructure.kubernetes.wsplugins.SidecarToolingProvisioner; @@ -73,7 +75,9 @@ import org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironmentFactory; import org.eclipse.che.workspace.infrastructure.openshift.project.OpenShiftProjectFactory; import org.eclipse.che.workspace.infrastructure.openshift.project.RemoveProjectOnWorkspaceRemove; +import org.eclipse.che.workspace.infrastructure.openshift.server.OpenShiftCookiePathStrategy; import org.eclipse.che.workspace.infrastructure.openshift.server.OpenShiftExternalServerExposer; +import org.eclipse.che.workspace.infrastructure.openshift.server.OpenShiftServerExposureStrategy; import org.eclipse.che.workspace.infrastructure.openshift.wsplugins.brokerphases.OpenshiftBrokerEnvironmentFactory; /** @author Sergii Leshchenko */ @@ -187,5 +191,8 @@ protected void configure() { KubernetesDevfileBindings.addAllowedEnvironmentTypeUpgradeBindings( binder(), OpenShiftEnvironment.TYPE, KubernetesEnvironment.TYPE); + + bind(ExternalServiceExposureStrategy.class).to(OpenShiftServerExposureStrategy.class); + bind(CookiePathStrategy.class).to(OpenShiftCookiePathStrategy.class); } } diff --git a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/server/OpenShiftCookiePathStrategy.java b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/server/OpenShiftCookiePathStrategy.java new file mode 100644 index 00000000000..62cc1ecba56 --- /dev/null +++ b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/server/OpenShiftCookiePathStrategy.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2012-2018 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.workspace.infrastructure.openshift.server; + +import static org.eclipse.che.workspace.infrastructure.kubernetes.server.external.MultiHostExternalServiceExposureStrategy.MULTI_HOST_STRATEGY; + +import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.CookiePathStrategy; + +/** + * On OpenShift we always use the equivalent of the multi-host strategy and therefore use the + * appropriate cookie path strategy for the secured endpoints. + */ +public class OpenShiftCookiePathStrategy extends CookiePathStrategy { + + public OpenShiftCookiePathStrategy() { + super(MULTI_HOST_STRATEGY); + } +} diff --git a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/server/OpenShiftServerExposureStrategy.java b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/server/OpenShiftServerExposureStrategy.java new file mode 100644 index 00000000000..c3bbebff735 --- /dev/null +++ b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/server/OpenShiftServerExposureStrategy.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2012-2018 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.workspace.infrastructure.openshift.server; + +import io.fabric8.kubernetes.api.model.ServicePort; +import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ExternalServiceExposureStrategy; + +/** + * Even though we don't use ingresses to expose the services on OpenShift, we still need to provide + * an implementation of this strategy that gives the rest of the system the idea of the paths on + * which the services are exposed. + */ +public class OpenShiftServerExposureStrategy implements ExternalServiceExposureStrategy { + @Override + public String getExternalHost(String serviceName, ServicePort servicePort) { + return null; + } + + @Override + public String getExternalPath(String serviceName, ServicePort servicePort) { + return "/"; + } +}