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

Make single user work with secure components exposed through localhost only #16227

Merged
merged 6 commits into from
Mar 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesInfrastructure;
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.SecureServerExposerFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.factory.JwtProxyConfigBuilderFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.factory.JwtProxyProvisionerFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.factory.JwtProxySecureServerExposerFactory;
import org.eclipse.che.workspace.infrastructure.openshift.OpenShiftClientConfigFactory;
Expand Down Expand Up @@ -380,7 +379,6 @@ private void configureMultiUserMode(
}

private void configureJwtProxySecureProvisioner(String infrastructure) {
install(new FactoryModuleBuilder().build(JwtProxyConfigBuilderFactory.class));
install(new FactoryModuleBuilder().build(JwtProxyProvisionerFactory.class));
if (KubernetesInfrastructure.NAME.equals(infrastructure)) {
install(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,7 @@ che.workspace.persist_volumes.default=true

# Configures in which way secure servers will be protected with authentication.
# Suitable values:
# - 'default': no additionally authentication system will be enabled.
# - 'default': jwtproxy is configured in a pass-through mode.
# So, servers should authenticate requests themselves.
# - 'jwtproxy': jwtproxy will authenticate requests.
# So, servers will receive only authenticated ones.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,23 +157,29 @@ static void setUnique(Map<String, String> attributes, boolean value) {
}

/**
* Determines whether the attributes configure the server to be authenticated using JWT cookies.
* Determines whether the attributes configure the server to be authenticated using JWT cookies. A
* null value means that the attributes don't require any particular authentication.
*
* @param attributes the attributes with additional server configuration
* @see #SECURE_SERVER_COOKIES_AUTH_ENABLED_ATTRIBUTE
*/
static boolean isCookiesAuthEnabled(Map<String, String> attributes) {
return AttributesEvaluator.booleanAttr(
attributes, SECURE_SERVER_COOKIES_AUTH_ENABLED_ATTRIBUTE, false);
static @Nullable Boolean isCookiesAuthEnabled(Map<String, String> attributes) {
String val = attributes.get(SECURE_SERVER_COOKIES_AUTH_ENABLED_ATTRIBUTE);
return val == null ? null : Boolean.parseBoolean(val);
metlos marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Sets the "cookiesAuthEnabled" flag in the provided attributes to the provided value.
* Sets the "cookiesAuthEnabled" flag in the provided attributes to the provided value. A null
* value means that the attributes don't require any particular authentication.
*
* @param attributes the attributes with the additional server configuration
*/
static void setCookiesAuthEnabled(Map<String, String> attributes, boolean value) {
attributes.put(SECURE_SERVER_COOKIES_AUTH_ENABLED_ATTRIBUTE, Boolean.toString(value));
static void setCookiesAuthEnabled(Map<String, String> attributes, @Nullable Boolean value) {
if (value == null) {
attributes.remove(SECURE_SERVER_COOKIES_AUTH_ENABLED_ATTRIBUTE);
} else {
attributes.put(SECURE_SERVER_COOKIES_AUTH_ENABLED_ATTRIBUTE, Boolean.toString(value));
}
}

/**
Expand All @@ -199,22 +205,27 @@ static void setUnsecuredPaths(Map<String, String> attributes, List<String> value
attributes.put(UNSECURED_PATHS_ATTRIBUTE, join(",", value));
}

/** @see #isInternal(Map) */
default boolean isInternal() {
return isInternal(getAttributes());
}

/** @see #isSecure(Map) */
default boolean isSecure() {
return isSecure(getAttributes());
}

/** @see #isUnique(Map) */
default boolean isUnique() {
return isUnique(getAttributes());
}

default boolean isCookiesAuthEnabled() {
/** @see #isCookiesAuthEnabled(Map) */
default @Nullable Boolean isCookiesAuthEnabled() {
return isCookiesAuthEnabled(getAttributes());
}

/** @see #getUnsecuredPaths(Map) */
default List<String> getUnsecuredPaths() {
return getUnsecuredPaths(getAttributes());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,13 @@
import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.IngressServiceExposureStrategyProvider;
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.SecureServerExposer;
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.PassThroughProxySecureServerExposer;
import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.factory.JwtProxyConfigBuilderFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.factory.PassThroughProxyProvisionerFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.factory.PassThroughProxySecureServerExposerFactory;
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;
Expand Down Expand Up @@ -169,9 +173,22 @@ protected void configure() {
new TypeLiteral<String>() {},
new TypeLiteral<SecureServerExposerFactory<KubernetesEnvironment>>() {});

install(new FactoryModuleBuilder().build(JwtProxyConfigBuilderFactory.class));
install(new FactoryModuleBuilder().build(PassThroughProxyProvisionerFactory.class));
install(
new FactoryModuleBuilder()
.implement(
new TypeLiteral<SecureServerExposer<KubernetesEnvironment>>() {},
new TypeLiteral<PassThroughProxySecureServerExposer<KubernetesEnvironment>>() {})
.build(
new TypeLiteral<
PassThroughProxySecureServerExposerFactory<KubernetesEnvironment>>() {}));

secureServerExposerFactories
.addBinding("default")
.to(new TypeLiteral<DefaultSecureServersFactory<KubernetesEnvironment>>() {});
.to(
new TypeLiteral<
PassThroughProxySecureServerExposerFactory<KubernetesEnvironment>>() {});

bind(BrokerService.class);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* 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;

import com.google.common.annotations.VisibleForTesting;
import com.google.inject.assistedinject.Assisted;
import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.ServicePort;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
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.commons.annotation.Nullable;
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment.PodData;
import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ExternalServerExposer;
import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.PassThroughProxyProvisioner;

/**
* Exposes secure servers using a proxy.
*
* <p>To expose secure servers it provisions proxy objects into environment with {@link
* org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.ProxyProvisioner}. Then proxy
* service port is made public accessible by {@link ExternalServerExposer<T>}.
*
* <p>In this way, requests to exposed secure servers will be routed via the proxy that is added one
* per workspace.
*
* @see
* org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.JwtProxyProvisioner
* @see PassThroughProxyProvisioner
*/
public class DefaultSecureServerExposer<T extends KubernetesEnvironment>
implements SecureServerExposer<T> {

private final ExternalServerExposer<T> exposer;
private final ProxyProvisioner proxyProvisioner;

@VisibleForTesting
public DefaultSecureServerExposer(
ProxyProvisioner jwtProxyProvisioner, ExternalServerExposer<T> exposer) {
this.exposer = exposer;
this.proxyProvisioner = jwtProxyProvisioner;
}

@Inject
public DefaultSecureServerExposer(
@Assisted RuntimeIdentity identity,
ProxyProvisionerFactory proxyProvisionerFactory,
ExternalServerExposer<T> exposer) {
this.exposer = exposer;
this.proxyProvisioner = proxyProvisionerFactory.create(identity);
}

/**
* This always returns an empty optional because JWT proxy is injected into the workspace pod and
* assumes the servers it exposes listen on localhost.
*
* @see
* org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.SecureServerExposer#createService(Collection,
* PodData, String, Map)
*/
@Override
public Optional<Service> createService(
Collection<ServicePort> allSecurePorts,
PodData pod,
String machineName,
Map<String, ? extends ServerConfig> secureServers) {
return Optional.empty();
}

@Override
public void expose(
T k8sEnv,
PodData pod,
String machineName,
@Nullable String serviceName,
@Nullable String serverId,
ServicePort servicePort,
Map<String, ServerConfig> secureServers)
throws InfrastructureException {

ServicePort exposedServicePort =
proxyProvisioner.expose(
k8sEnv,
pod,
machineName,
serviceName,
servicePort,
servicePort.getProtocol(),
secureServers);

exposer.expose(
k8sEnv,
machineName,
proxyProvisioner.getServiceName(),
serverId,
exposedServicePort,
secureServers);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* 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;

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.spi.InfrastructureException;
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment.PodData;

/**
* A proxy provisioner is a class responsible for provisioning and exposing a reverse proxy that
* should proxy the access to a backend service.
*/
public interface ProxyProvisioner {
metlos marked this conversation as resolved.
Show resolved Hide resolved
int FIRST_AVAILABLE_PROXY_PORT = 4400;

/**
* Modifies the provided environment with Kubernetes objects needed for the proxy and creates a
* service that is pointing to the proxy that can then be used to expose the proxy.
*
* <p>Note that this method is called multiple times (once for each backend service) and so has to
* build the kubernetes objects and configuration iteratively, if required.
*
* @param k8sEnv Kubernetes environment to modify
* @param pod the pod that runs the server being exposed
* @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
* @param secureServers secure servers to expose
* @return JWTProxy service port that expose the specified one
* @throws InfrastructureException if any exception occurs during port exposing
*/
ServicePort expose(
KubernetesEnvironment k8sEnv,
PodData pod,
String machineName,
@Nullable String backendServiceName,
ServicePort backendServicePort,
String protocol,
Map<String, ServerConfig> secureServers)
throws InfrastructureException;

/** The name of the service handling the traffic to the proxy. */
String getServiceName();
}
Loading