Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Support single-host mode on the multi-user server #14335

Merged
merged 13 commits into from
Sep 10, 2019
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions assembly/assembly-root-war/src/main/webapp/_app/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -117,18 +117,18 @@ protected void configure() {
.addBinding()
.to(KubernetesClientTermination.class);

MapBinder<String, IngressServiceExposureStrategy> ingressStrategies =
MapBinder.newMapBinder(binder(), String.class, IngressServiceExposureStrategy.class);
MapBinder<String, ExternalServiceExposureStrategy> 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<ServersConverter<KubernetesEnvironment>>() {});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ public class ExternalServerExposer<T extends KubernetesEnvironment> {
*/
static final String PATH_TRANSFORM_PATH_CATCH = "%s";

private final IngressServiceExposureStrategy strategy;
private final ExternalServiceExposureStrategy strategy;
private final Map<String, String> ingressAnnotations;
private final String pathTransformFmt;

@Inject
public ExternalServerExposer(
IngressServiceExposureStrategy strategy,
ExternalServiceExposureStrategy strategy,
@Named("infra.kubernetes.ingress.annotations") Map<String, String> annotations,
@Nullable @Named("che.infra.kubernetes.ingress.path_transform") String pathTransformFmt) {
this.strategy = strategy;
Expand Down Expand Up @@ -71,7 +71,7 @@ private Ingress generateIngress(
Map<String, ServerConfig> ingressesServers) {

ExternalServerIngressBuilder ingressBuilder = new ExternalServerIngressBuilder();
String host = strategy.getIngressHost(serviceName, servicePort);
String host = strategy.getExternalHost(serviceName, servicePort);
if (host != null) {
ingressBuilder = ingressBuilder.withHost(host);
}
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,16 @@

@Singleton
public class IngressServiceExposureStrategyProvider
implements Provider<IngressServiceExposureStrategy> {
implements Provider<ExternalServiceExposureStrategy> {

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<String, IngressServiceExposureStrategy> strategies) {
Map<String, ExternalServiceExposureStrategy> strategies) {

namingStrategy = strategies.get(strategy);

Expand All @@ -42,7 +42,7 @@ public IngressServiceExposureStrategyProvider(
}

@Override
public IngressServiceExposureStrategy get() {
public ExternalServiceExposureStrategy get() {
return namingStrategy;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,15 @@
* @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";

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(
Expand All @@ -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 "/";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,36 @@ 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 authErrorRedirectUriPrefix the prefix used to generate the redirect to the auth page.
metlos marked this conversation as resolved.
Show resolved Hide resolved
* This should be set to the a URL path where the JWT proxy can pick up the request to perform
* the auth
*/
public void addVerifierProxy(
Integer listenPort, String upstream, Set<String> excludes, Boolean cookiesAuthEnabled) {
verifierProxies.add(new VerifierProxy(listenPort, upstream, excludes, cookiesAuthEnabled));
Integer listenPort,
String upstream,
Set<String> excludes,
Boolean cookiesAuthEnabled,
String cookiePath,
String authErrorRedirectUriPrefix) {
verifierProxies.add(
new VerifierProxy(
listenPort,
upstream,
excludes,
cookiesAuthEnabled,
cookiePath,
authErrorRedirectUriPrefix));
}

public String build() throws InternalInfrastructureException {
Expand Down Expand Up @@ -104,6 +131,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()
Expand All @@ -119,6 +147,10 @@ public String build() throws InternalInfrastructureException {
verifierConfig.setAuthUrl(authPageUrl.toString());
metlos marked this conversation as resolved.
Show resolved Hide resolved
}

if (verifierProxy.publicBasePath != null) {
verifierConfig.setPublicBasePath(verifierProxy.publicBasePath);
}

VerifierProxyConfig proxyConfig =
new VerifierProxyConfig()
.withListenAddr(":" + verifierProxy.listenPort)
Expand All @@ -135,18 +167,35 @@ public String build() throws InternalInfrastructureException {
}
}

private class VerifierProxy {
private Integer listenPort;
private String upstream;
private Set<String> excludes;
private boolean cookiesAuthEnabled;
private static final class VerifierProxy {
final Integer listenPort;
final String upstream;
final Set<String> excludes;
final boolean cookiesAuthEnabled;
final String cookiePath;
final String publicBasePath;

VerifierProxy(
Integer listenPort, String upstream, Set<String> excludes, boolean cookiesAuthEnabled) {
Integer listenPort,
String upstream,
Set<String> 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;
}
}
}
Loading