Skip to content

Commit

Permalink
Merge pull request #10252 from sleshchenko/jwtproxy-refactor
Browse files Browse the repository at this point in the history
Added an ability to authenticate requests to servers with jwtproxy
  • Loading branch information
sleshchenko authored Jul 12, 2018
2 parents d578406 + d6a913b commit cb562b9
Show file tree
Hide file tree
Showing 45 changed files with 1,473 additions and 86 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,18 @@
"exec-agent/http": {
"port": "4412/tcp",
"protocol": "http",
"path" : "/process"
"path" : "/process",
"attributes": {
"secure": "true"
}
},
"exec-agent/ws": {
"port": "4412/tcp",
"protocol": "ws",
"path": "/connect"
"path": "/connect",
"attributes": {
"secure": "true"
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,18 @@
"exec-agent/http": {
"port": "4412/tcp",
"protocol": "http",
"path" : "/process"
"path" : "/process",
"attributes": {
"secure": "true"
}
},
"exec-agent/ws": {
"port": "4412/tcp",
"protocol": "ws",
"path": "/connect"
"path": "/connect",
"attributes": {
"secure": "true"
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
"terminal": {
"port": "4411/tcp",
"protocol": "ws",
"path" : "/pty"
"path" : "/pty",
"attributes": {
"secure": "true"
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
"terminal": {
"port": "4411/tcp",
"protocol": "ws",
"path" : "/pty"
"path" : "/pty",
"attributes": {
"secure": "true"
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -489,3 +489,11 @@ che.singleport.wildcard_domain.ipless=false
# Workspace.Next feature API endpoint. Should be a valid HTTP URL that includes protocol, port etc.
# In case Workspace.Next is not needed value 'NULL' should be used
che.workspace.feature.api=NULL

# Configures in which way secure servers will be protected with authentication.
# 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.server.secure_exposer=default
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ public interface ServerConfig {
*/
String INTERNAL_SERVER_ATTRIBUTE = "internal";

/**
* {@link ServerConfig} and {@link Server} attribute name which can identify server as secure or
* non-secure. Requests to secure servers will be authenticated and must contain machine token.
* Attribute value {@code true} makes a server secure, any other value or lack of the attribute
* makes the server non-secure.
*/
String SECURE_SERVER_ATTRIBUTE = "secure";

/**
* Port used by server.
*
Expand Down
13 changes: 13 additions & 0 deletions infrastructures/kubernetes/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@
<groupId>org.eclipse.che.infrastructure.docker</groupId>
<artifactId>docker-environment</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-machine-authentication</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>javax.persistence</artifactId>
Expand Down Expand Up @@ -215,6 +219,15 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.mycila</groupId>
<artifactId>license-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>src/test/resources/jwtproxy-confg.yaml</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@
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.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.JwtProxySecureServerExposerFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.wsnext.KubernetesWorkspaceNextApplier;

/** @author Sergii Leshchenko */
Expand All @@ -70,6 +74,7 @@ protected void configure() {
install(new FactoryModuleBuilder().build(KubernetesRuntimeFactory.class));
install(new FactoryModuleBuilder().build(KubernetesBootstrapperFactory.class));
install(new FactoryModuleBuilder().build(StartSynchronizerFactory.class));

bind(WorkspacePVCCleaner.class).asEagerSingleton();
bind(RemoveNamespaceOnWorkspaceRemove.class).asEagerSingleton();

Expand Down Expand Up @@ -119,5 +124,28 @@ protected void configure() {
MapBinder<String, WorkspaceNextApplier> wsNext =
MapBinder.newMapBinder(binder(), String.class, WorkspaceNextApplier.class);
wsNext.addBinding(KubernetesEnvironment.TYPE).to(KubernetesWorkspaceNextApplier.class);

bind(new TypeLiteral<SecureServerExposerFactory<KubernetesEnvironment>>() {})
.toProvider(
new TypeLiteral<SecureServerExposerFactoryProvider<KubernetesEnvironment>>() {});

MapBinder<String, SecureServerExposerFactory<KubernetesEnvironment>>
secureServerExposerFactories =
MapBinder.newMapBinder(
binder(),
new TypeLiteral<String>() {},
new TypeLiteral<SecureServerExposerFactory<KubernetesEnvironment>>() {});

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

install(
new FactoryModuleBuilder()
.build(
new TypeLiteral<JwtProxySecureServerExposerFactory<KubernetesEnvironment>>() {}));
secureServerExposerFactories
.addBinding("jwtproxy")
.to(new TypeLiteral<JwtProxySecureServerExposerFactory<KubernetesEnvironment>>() {});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.inject.assistedinject.Assisted;
import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.Container;
import io.fabric8.kubernetes.api.model.ObjectMeta;
import io.fabric8.kubernetes.api.model.Pod;
Expand Down Expand Up @@ -470,6 +471,10 @@ protected void startMachines() throws InfrastructureException {
namespace.secrets().create(secret);
}

for (ConfigMap configMap : k8sEnv.getConfigMaps().values()) {
namespace.configMaps().create(configMap);
}

List<Service> createdServices = new ArrayList<>();
for (Service service : k8sEnv.getServices().values()) {
createdServices.add(namespace.services().create(service));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
*/
package org.eclipse.che.workspace.infrastructure.kubernetes.environment;

import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.PersistentVolumeClaim;
import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.api.model.Secret;
Expand Down Expand Up @@ -38,6 +39,7 @@ public class KubernetesEnvironment extends InternalEnvironment {
private final Map<String, Ingress> ingresses;
private final Map<String, PersistentVolumeClaim> persistentVolumeClaims;
private final Map<String, Secret> secrets;
private final Map<String, ConfigMap> configMaps;

public KubernetesEnvironment(KubernetesEnvironment k8sEnv) {
this(
Expand All @@ -48,7 +50,8 @@ public KubernetesEnvironment(KubernetesEnvironment k8sEnv) {
k8sEnv.getServices(),
k8sEnv.getIngresses(),
k8sEnv.getPersistentVolumeClaims(),
k8sEnv.getSecrets());
k8sEnv.getSecrets(),
k8sEnv.getConfigMaps());
}

public static Builder builder() {
Expand All @@ -63,13 +66,15 @@ protected KubernetesEnvironment(
Map<String, Service> services,
Map<String, Ingress> ingresses,
Map<String, PersistentVolumeClaim> persistentVolumeClaims,
Map<String, Secret> secrets) {
Map<String, Secret> secrets,
Map<String, ConfigMap> configMaps) {
super(internalRecipe, machines, warnings);
this.pods = pods;
this.services = services;
this.ingresses = ingresses;
this.persistentVolumeClaims = persistentVolumeClaims;
this.secrets = secrets;
this.configMaps = configMaps;
}

/** Returns pods that should be created when environment starts. */
Expand Down Expand Up @@ -97,6 +102,11 @@ public Map<String, Secret> getSecrets() {
return secrets;
}

/** Returns config maps that should be created when environment starts. */
public Map<String, ConfigMap> getConfigMaps() {
return configMaps;
}

public static class Builder {
protected InternalRecipe internalRecipe;
protected final Map<String, InternalMachineConfig> machines = new HashMap<>();
Expand All @@ -106,6 +116,7 @@ public static class Builder {
protected final Map<String, Ingress> ingresses = new HashMap<>();
protected final Map<String, PersistentVolumeClaim> pvcs = new HashMap<>();
protected final Map<String, Secret> secrets = new HashMap<>();
protected final Map<String, ConfigMap> configMaps = new HashMap<>();

protected Builder() {}

Expand Down Expand Up @@ -149,9 +160,14 @@ public Builder setSecrets(Map<String, Secret> secrets) {
return this;
}

public Builder setConfigMaps(Map<String, ConfigMap> configMaps) {
this.configMaps.putAll(configMaps);
return this;
}

public KubernetesEnvironment build() {
return new KubernetesEnvironment(
internalRecipe, machines, warnings, pods, services, ingresses, pvcs, secrets);
internalRecipe, machines, warnings, pods, services, ingresses, pvcs, secrets, configMaps);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import static org.eclipse.che.api.core.model.workspace.config.MachineConfig.MEMORY_LIMIT_ATTRIBUTE;

import com.google.common.annotations.VisibleForTesting;
import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.Container;
import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.api.model.KubernetesList;
Expand Down Expand Up @@ -67,6 +68,10 @@ public class KubernetesEnvironmentFactory
static final String SECRET_IGNORED_WARNING_MESSAGE =
"Secrets specified in Kubernetes recipe are ignored.";

static final int CONFIG_MAP_IGNORED_WARNING_CODE = 4103;
static final String CONFIG_MAP_IGNORED_WARNING_MESSAGE =
"Config maps specified in Kubernetes recipe are ignored.";

private final KubernetesClientFactory clientFactory;
private final KubernetesEnvironmentValidator envValidator;
private final String defaultMachineMemorySizeAttribute;
Expand Down Expand Up @@ -121,6 +126,7 @@ protected KubernetesEnvironment doCreate(
boolean isAnyIngressPresent = false;
boolean isAnyPVCPresent = false;
boolean isAnySecretPresent = false;
boolean isAnyConfigMapPresent = false;
for (HasMetadata object : list.getItems()) {
if (object instanceof Pod) {
Pod pod = (Pod) object;
Expand All @@ -134,6 +140,8 @@ protected KubernetesEnvironment doCreate(
isAnyPVCPresent = true;
} else if (object instanceof Secret) {
isAnySecretPresent = true;
} else if (object instanceof ConfigMap) {
isAnyConfigMapPresent = true;
} else {
throw new ValidationException(
format("Found unknown object type '%s'", object.getMetadata()));
Expand All @@ -153,6 +161,11 @@ protected KubernetesEnvironment doCreate(
warnings.add(new WarningImpl(SECRET_IGNORED_WARNING_CODE, SECRET_IGNORED_WARNING_MESSAGE));
}

if (isAnyConfigMapPresent) {
warnings.add(
new WarningImpl(CONFIG_MAP_IGNORED_WARNING_CODE, CONFIG_MAP_IGNORED_WARNING_MESSAGE));
}

addRamLimitAttribute(machines, pods.values());

KubernetesEnvironment k8sEnv =
Expand All @@ -165,6 +178,7 @@ protected KubernetesEnvironment doCreate(
.setIngresses(new HashMap<>())
.setPersistentVolumeClaims(new HashMap<>())
.setSecrets(new HashMap<>())
.setConfigMaps(new HashMap<>())
.build();

envValidator.validate(k8sEnv);
Expand Down
Original file line number Diff line number Diff line change
@@ -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.namespace;

import static org.eclipse.che.workspace.infrastructure.kubernetes.Constants.CHE_WORKSPACE_ID_LABEL;
import static org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesObjectUtil.putLabel;

import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.client.KubernetesClientException;
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesInfrastructureException;

/**
* Defines an internal API for managing {@link ConfigMap} instances in {@link
* KubernetesConfigsMaps#namespace predefined namespace}.
*
* @author Sergii Leshchenko
*/
public class KubernetesConfigsMaps {
private final String namespace;
private final String workspaceId;
private final KubernetesClientFactory clientFactory;

KubernetesConfigsMaps(
String namespace, String workspaceId, KubernetesClientFactory clientFactory) {
this.namespace = namespace;
this.workspaceId = workspaceId;
this.clientFactory = clientFactory;
}

/**
* Creates specified config map.
*
* @param configMap config map to create
* @throws InfrastructureException when any exception occurs
*/
public void create(ConfigMap configMap) throws InfrastructureException {
putLabel(configMap, CHE_WORKSPACE_ID_LABEL, workspaceId);
try {
clientFactory.create(workspaceId).configMaps().inNamespace(namespace).create(configMap);
} catch (KubernetesClientException e) {
throw new KubernetesInfrastructureException(e);
}
}

/**
* Deletes all existing secrets.
*
* @throws InfrastructureException when any exception occurs
*/
public void delete() throws InfrastructureException {
try {
clientFactory
.create(workspaceId)
.configMaps()
.inNamespace(namespace)
.withLabel(CHE_WORKSPACE_ID_LABEL, workspaceId)
.delete();
} catch (KubernetesClientException e) {
throw new KubernetesInfrastructureException(e);
}
}
}
Loading

0 comments on commit cb562b9

Please sign in to comment.