Merge pull request #10252 from sleshchenko/jwtproxy-refactor
Added an ability to authenticate requests to servers with jwtproxy6.19.x
commit
cb562b9795
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,10 @@
|
|||
"terminal": {
|
||||
"port": "4411/tcp",
|
||||
"protocol": "ws",
|
||||
"path" : "/pty"
|
||||
"path" : "/pty",
|
||||
"attributes": {
|
||||
"secure": "true"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,10 @@
|
|||
"terminal": {
|
||||
"port": "4411/tcp",
|
||||
"protocol": "ws",
|
||||
"path" : "/pty"
|
||||
"path" : "/pty",
|
||||
"attributes": {
|
||||
"secure": "true"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -51,6 +51,10 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.Exter
|
|||
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 */
|
||||
|
|
@ -70,6 +74,7 @@ public class KubernetesInfraModule extends AbstractModule {
|
|||
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();
|
||||
|
||||
|
|
@ -119,5 +124,28 @@ public class KubernetesInfraModule extends AbstractModule {
|
|||
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>>() {});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import static org.eclipse.che.workspace.infrastructure.kubernetes.Constants.POD_
|
|||
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;
|
||||
|
|
@ -470,6 +471,10 @@ public class KubernetesInternalRuntime<
|
|||
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));
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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(
|
||||
|
|
@ -48,7 +50,8 @@ public class KubernetesEnvironment extends InternalEnvironment {
|
|||
k8sEnv.getServices(),
|
||||
k8sEnv.getIngresses(),
|
||||
k8sEnv.getPersistentVolumeClaims(),
|
||||
k8sEnv.getSecrets());
|
||||
k8sEnv.getSecrets(),
|
||||
k8sEnv.getConfigMaps());
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
|
|
@ -63,13 +66,15 @@ public class KubernetesEnvironment extends InternalEnvironment {
|
|||
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. */
|
||||
|
|
@ -97,6 +102,11 @@ public class KubernetesEnvironment extends InternalEnvironment {
|
|||
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<>();
|
||||
|
|
@ -106,6 +116,7 @@ public class KubernetesEnvironment extends InternalEnvironment {
|
|||
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() {}
|
||||
|
||||
|
|
@ -149,9 +160,14 @@ public class KubernetesEnvironment extends InternalEnvironment {
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import static java.lang.String.format;
|
|||
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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -121,6 +126,7 @@ public class KubernetesEnvironmentFactory
|
|||
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;
|
||||
|
|
@ -134,6 +140,8 @@ public class KubernetesEnvironmentFactory
|
|||
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()));
|
||||
|
|
@ -153,6 +161,11 @@ public class KubernetesEnvironmentFactory
|
|||
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 =
|
||||
|
|
@ -165,6 +178,7 @@ public class KubernetesEnvironmentFactory
|
|||
.setIngresses(new HashMap<>())
|
||||
.setPersistentVolumeClaims(new HashMap<>())
|
||||
.setSecrets(new HashMap<>())
|
||||
.setConfigMaps(new HashMap<>())
|
||||
.build();
|
||||
|
||||
envValidator.validate(k8sEnv);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
package org.eclipse.che.workspace.infrastructure.kubernetes.namespace;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import io.fabric8.kubernetes.api.model.ConfigMap;
|
||||
import io.fabric8.kubernetes.api.model.DoneableServiceAccount;
|
||||
import io.fabric8.kubernetes.api.model.Namespace;
|
||||
import io.fabric8.kubernetes.api.model.PersistentVolumeClaim;
|
||||
|
|
@ -64,6 +65,7 @@ public class KubernetesNamespace {
|
|||
private final KubernetesIngresses ingresses;
|
||||
private final KubernetesClientFactory clientFactory;
|
||||
private final KubernetesSecrets secrets;
|
||||
private final KubernetesConfigsMaps configMaps;
|
||||
|
||||
@VisibleForTesting
|
||||
protected KubernetesNamespace(
|
||||
|
|
@ -74,7 +76,8 @@ public class KubernetesNamespace {
|
|||
KubernetesServices services,
|
||||
KubernetesPersistentVolumeClaims pvcs,
|
||||
KubernetesIngresses kubernetesIngresses,
|
||||
KubernetesSecrets secrets) {
|
||||
KubernetesSecrets secrets,
|
||||
KubernetesConfigsMaps configMaps) {
|
||||
this.clientFactory = clientFactory;
|
||||
this.workspaceId = workspaceId;
|
||||
this.name = name;
|
||||
|
|
@ -83,6 +86,7 @@ public class KubernetesNamespace {
|
|||
this.pvcs = pvcs;
|
||||
this.ingresses = kubernetesIngresses;
|
||||
this.secrets = secrets;
|
||||
this.configMaps = configMaps;
|
||||
}
|
||||
|
||||
public KubernetesNamespace(
|
||||
|
|
@ -95,6 +99,7 @@ public class KubernetesNamespace {
|
|||
this.pvcs = new KubernetesPersistentVolumeClaims(name, workspaceId, clientFactory);
|
||||
this.ingresses = new KubernetesIngresses(name, workspaceId, clientFactory);
|
||||
this.secrets = new KubernetesSecrets(name, workspaceId, clientFactory);
|
||||
this.configMaps = new KubernetesConfigsMaps(name, workspaceId, clientFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -146,9 +151,19 @@ public class KubernetesNamespace {
|
|||
return secrets;
|
||||
}
|
||||
|
||||
/** Returns object for managing {@link ConfigMap} instances inside namespace. */
|
||||
public KubernetesConfigsMaps configMaps() {
|
||||
return configMaps;
|
||||
}
|
||||
|
||||
/** Removes all object except persistent volume claims inside namespace. */
|
||||
public void cleanUp() throws InfrastructureException {
|
||||
doRemove(ingresses::delete, services::delete, deployments::delete, secrets::delete);
|
||||
doRemove(
|
||||
ingresses::delete,
|
||||
services::delete,
|
||||
deployments::delete,
|
||||
secrets::delete,
|
||||
configMaps::delete);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFacto
|
|||
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesInfrastructureException;
|
||||
|
||||
/**
|
||||
* Defines an internal API for managing {@link Secret} instances in {@link KubernetesPods#namespace
|
||||
* predefined namespace}.
|
||||
* Defines an internal API for managing {@link Secret} instances in {@link
|
||||
* KubernetesSecrets#namespace predefined namespace}.
|
||||
*
|
||||
* @author Sergii Leshchenko
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.environment.Kubernete
|
|||
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.ConfigurationProvisioner;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.server.KubernetesServerExposer;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ExternalServerExposerStrategy;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.SecureServerExposer;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.SecureServerExposerFactory;
|
||||
|
||||
/**
|
||||
* Converts {@link ServerConfig} to Kubernetes related objects to add a server into Kubernetes
|
||||
|
|
@ -39,15 +41,20 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.Exter
|
|||
public class ServersConverter<T extends KubernetesEnvironment>
|
||||
implements ConfigurationProvisioner<T> {
|
||||
|
||||
private final SecureServerExposerFactory<T> secureServerExposerFactory;
|
||||
private final ExternalServerExposerStrategy<T> externalServerExposerStrategy;
|
||||
|
||||
@Inject
|
||||
public ServersConverter(ExternalServerExposerStrategy<T> externalServerExposerStrategy) {
|
||||
public ServersConverter(
|
||||
SecureServerExposerFactory<T> secureServerExposerFactory,
|
||||
ExternalServerExposerStrategy<T> externalServerExposerStrategy) {
|
||||
this.secureServerExposerFactory = secureServerExposerFactory;
|
||||
this.externalServerExposerStrategy = externalServerExposerStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void provision(T k8sEnv, RuntimeIdentity identity) throws InfrastructureException {
|
||||
SecureServerExposer<T> secureServerExposer = secureServerExposerFactory.create(identity);
|
||||
|
||||
for (Pod podConfig : k8sEnv.getPods().values()) {
|
||||
final PodSpec podSpec = podConfig.getSpec();
|
||||
|
|
@ -57,7 +64,12 @@ public class ServersConverter<T extends KubernetesEnvironment>
|
|||
if (!machineConfig.getServers().isEmpty()) {
|
||||
KubernetesServerExposer kubernetesServerExposer =
|
||||
new KubernetesServerExposer<>(
|
||||
externalServerExposerStrategy, machineName, podConfig, containerConfig, k8sEnv);
|
||||
externalServerExposerStrategy,
|
||||
secureServerExposer,
|
||||
machineName,
|
||||
podConfig,
|
||||
containerConfig,
|
||||
k8sEnv);
|
||||
kubernetesServerExposer.expose(machineConfig.getServers());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.Constants;
|
|||
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.UniqueNamesProvisioner;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ExternalServerExposerStrategy;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.SecureServerExposer;
|
||||
|
||||
/**
|
||||
* Helps to modify {@link KubernetesEnvironment} to make servers that are configured by {@link
|
||||
|
|
@ -102,6 +103,7 @@ public class KubernetesServerExposer<T extends KubernetesEnvironment> {
|
|||
public static final String SERVER_PREFIX = "server";
|
||||
|
||||
private final ExternalServerExposerStrategy<T> externalServerExposer;
|
||||
private final SecureServerExposer<T> secureServerExposer;
|
||||
private final String machineName;
|
||||
private final Container container;
|
||||
private final Pod pod;
|
||||
|
|
@ -109,11 +111,13 @@ public class KubernetesServerExposer<T extends KubernetesEnvironment> {
|
|||
|
||||
public KubernetesServerExposer(
|
||||
ExternalServerExposerStrategy<T> externalServerExposer,
|
||||
SecureServerExposer<T> secureServerExposer,
|
||||
String machineName,
|
||||
Pod pod,
|
||||
Container container,
|
||||
T k8sEnv) {
|
||||
this.externalServerExposer = externalServerExposer;
|
||||
this.secureServerExposer = secureServerExposer;
|
||||
this.machineName = machineName;
|
||||
this.pod = pod;
|
||||
this.container = container;
|
||||
|
|
@ -133,13 +137,21 @@ public class KubernetesServerExposer<T extends KubernetesEnvironment> {
|
|||
public void expose(Map<String, ? extends ServerConfig> servers) throws InfrastructureException {
|
||||
Map<String, ServerConfig> internalServers = new HashMap<>();
|
||||
Map<String, ServerConfig> externalServers = new HashMap<>();
|
||||
Map<String, ServerConfig> secureServers = new HashMap<>();
|
||||
|
||||
servers.forEach(
|
||||
(key, value) -> {
|
||||
if ("true".equals(value.getAttributes().get(INTERNAL_SERVER_ATTRIBUTE))) {
|
||||
// Server is internal. It doesn't make sense to make an it secure since
|
||||
// it is available only within workspace servers
|
||||
internalServers.put(key, value);
|
||||
} else {
|
||||
externalServers.put(key, value);
|
||||
// Server is external. Check if it should be secure or not
|
||||
if ("true".equals(value.getAttributes().get(ServerConfig.SECURE_SERVER_ATTRIBUTE))) {
|
||||
secureServers.put(key, value);
|
||||
} else {
|
||||
externalServers.put(key, value);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -163,6 +175,13 @@ public class KubernetesServerExposer<T extends KubernetesEnvironment> {
|
|||
externalServerExposer.expose(
|
||||
k8sEnv, machineName, serviceName, servicePort, matchedExternalServers);
|
||||
}
|
||||
|
||||
// expose service port related secure servers if exist
|
||||
Map<String, ServerConfig> matchedSecureServers = match(secureServers, servicePort);
|
||||
if (!matchedSecureServers.isEmpty()) {
|
||||
secureServerExposer.expose(
|
||||
k8sEnv, machineName, serviceName, servicePort, matchedSecureServers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2018 Red Hat, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
*/
|
||||
package org.eclipse.che.workspace.infrastructure.kubernetes.server.secure;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.ServicePort;
|
||||
import java.util.Map;
|
||||
import javax.inject.Inject;
|
||||
import org.eclipse.che.api.core.model.workspace.config.ServerConfig;
|
||||
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ExternalServerExposerStrategy;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link SecureServerExposerFactory} that creates instances of {@link
|
||||
* SecureServerExposer} that exposes secure servers as usual external server without setting
|
||||
* authentication layer.
|
||||
*
|
||||
* @author Sergii Leshchenko
|
||||
*/
|
||||
public class DefaultSecureServersFactory<T extends KubernetesEnvironment>
|
||||
implements SecureServerExposerFactory<T> {
|
||||
private final ExternalServerExposerStrategy<T> exposerStrategy;
|
||||
|
||||
@Inject
|
||||
public DefaultSecureServersFactory(ExternalServerExposerStrategy<T> exposerStrategy) {
|
||||
this.exposerStrategy = exposerStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecureServerExposer<T> create(RuntimeIdentity runtimeId) {
|
||||
return new DefaultSecureServerExposer();
|
||||
}
|
||||
|
||||
private class DefaultSecureServerExposer implements SecureServerExposer<T> {
|
||||
@Override
|
||||
public void expose(
|
||||
T k8sEnv,
|
||||
String machineName,
|
||||
String serviceName,
|
||||
ServicePort servicePort,
|
||||
Map<String, ServerConfig> secureServers) {
|
||||
exposerStrategy.expose(k8sEnv, machineName, serviceName, servicePort, secureServers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2018 Red Hat, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
*/
|
||||
package org.eclipse.che.workspace.infrastructure.kubernetes.server.secure;
|
||||
|
||||
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.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
|
||||
|
||||
/**
|
||||
* Modifies the specified Kubernetes environment to expose secure servers.
|
||||
*
|
||||
* <p>Note that ONE {@link SecureServerExposer} instance should be used for one workspace start.
|
||||
*
|
||||
* @author Sergii Leshchenko
|
||||
*/
|
||||
public interface SecureServerExposer<T extends KubernetesEnvironment> {
|
||||
|
||||
/**
|
||||
* Modifies the specified Kubernetes environment to expose secure servers.
|
||||
*
|
||||
* @param k8sEnv Kubernetes environment that should be modified.
|
||||
* @param machineName machine name to which secure servers belong to
|
||||
* @param serviceName service name that exposes secure servers
|
||||
* @param servicePort service port that exposes secure servers
|
||||
* @param secureServers secure servers to expose
|
||||
* @throws InfrastructureException when any exception occurs during servers exposing
|
||||
*/
|
||||
void expose(
|
||||
T k8sEnv,
|
||||
String machineName,
|
||||
String serviceName,
|
||||
ServicePort servicePort,
|
||||
Map<String, ServerConfig> secureServers)
|
||||
throws InfrastructureException;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2018 Red Hat, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
*/
|
||||
package org.eclipse.che.workspace.infrastructure.kubernetes.server.secure;
|
||||
|
||||
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
|
||||
|
||||
/**
|
||||
* Helps to create {@link SecureServerExposer} instances.
|
||||
*
|
||||
* <p>Note that ONE {@link SecureServerExposer} instance should be used for one workspace start.
|
||||
*
|
||||
* @author Sergii Leshchenko
|
||||
*/
|
||||
public interface SecureServerExposerFactory<T extends KubernetesEnvironment> {
|
||||
SecureServerExposer<T> create(RuntimeIdentity runtimeId);
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2018 Red Hat, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
*/
|
||||
package org.eclipse.che.workspace.infrastructure.kubernetes.server.secure;
|
||||
|
||||
import java.util.Map;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Provider;
|
||||
import org.eclipse.che.inject.ConfigurationException;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
|
||||
|
||||
/** @author Sergii Leshchenko */
|
||||
public class SecureServerExposerFactoryProvider<T extends KubernetesEnvironment>
|
||||
implements Provider<SecureServerExposerFactory<T>> {
|
||||
|
||||
private final boolean agentsAuthEnabled;
|
||||
private final String serverExposer;
|
||||
|
||||
private final DefaultSecureServersFactory<T> defaultSecureServersFactory;
|
||||
private final Map<String, SecureServerExposerFactory<T>> factories;
|
||||
|
||||
@Inject
|
||||
public SecureServerExposerFactoryProvider(
|
||||
@Named("che.agents.auth_enabled") boolean agentsAuthEnabled,
|
||||
@Named("che.server.secure_exposer") String serverExposer,
|
||||
DefaultSecureServersFactory<T> defaultSecureServersFactory,
|
||||
Map<String, SecureServerExposerFactory<T>> factories) {
|
||||
this.agentsAuthEnabled = agentsAuthEnabled;
|
||||
this.serverExposer = serverExposer;
|
||||
this.defaultSecureServersFactory = defaultSecureServersFactory;
|
||||
this.factories = factories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates instance of {@link SecureServerExposerFactory} that will expose secure servers for
|
||||
* runtime with the specified runtime identity.
|
||||
*/
|
||||
@Override
|
||||
public SecureServerExposerFactory<T> get() {
|
||||
if (!agentsAuthEnabled) {
|
||||
// return default secure server exposer because no need to protect servers with authentication
|
||||
return defaultSecureServersFactory;
|
||||
}
|
||||
|
||||
SecureServerExposerFactory<T> serverExposerFactory = factories.get(serverExposer);
|
||||
if (serverExposerFactory == null) {
|
||||
throw new ConfigurationException(
|
||||
"Unknown secure servers exposer is configured '" + serverExposer + "'. ");
|
||||
}
|
||||
|
||||
return serverExposerFactory;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2018 Red Hat, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
*/
|
||||
package org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy;
|
||||
|
||||
import static org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.JwtProxyProvisioner.JWT_PROXY_CONFIG_FOLDER;
|
||||
import static org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.JwtProxyProvisioner.JWT_PROXY_PUBLIC_KEY_FILE;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Helps to build JWTProxy config with several verifier proxies.
|
||||
*
|
||||
* @author Sergii Leshchenko
|
||||
*/
|
||||
public class JwtProxyConfigBuilder {
|
||||
private final List<VerifierProxy> verifierProxies = new ArrayList<>();
|
||||
private final String workspaceId;
|
||||
|
||||
public JwtProxyConfigBuilder(String workspaceId) {
|
||||
this.workspaceId = workspaceId;
|
||||
}
|
||||
|
||||
public void addVerifierProxy(Integer listenPort, String upstream) {
|
||||
verifierProxies.add(new VerifierProxy(listenPort, upstream));
|
||||
}
|
||||
|
||||
public String build() {
|
||||
StringBuilder configBuilder = new StringBuilder();
|
||||
|
||||
configBuilder.append("jwtproxy:\n" + " verifier_proxies:\n");
|
||||
for (VerifierProxy verifierProxy : verifierProxies) {
|
||||
configBuilder.append(
|
||||
String.format(
|
||||
" - listen_addr: :%s\n" // :4471
|
||||
+ " verifier:\n"
|
||||
+ " upstream: %s/\n" // http://localhost:4401
|
||||
+ " audience: http://%s\n"
|
||||
+ " max_skew: 1m\n"
|
||||
+ " max_ttl: 3h\n"
|
||||
+ " key_server:\n"
|
||||
+ " type: preshared\n"
|
||||
+ " options:\n"
|
||||
+ " issuer: wsmaster\n"
|
||||
+ " key_id: mykey\n"
|
||||
+ " public_key_path: "
|
||||
+ JWT_PROXY_CONFIG_FOLDER
|
||||
+ "/"
|
||||
+ JWT_PROXY_PUBLIC_KEY_FILE
|
||||
+ "\n"
|
||||
+ " claims_verifiers:\n"
|
||||
+ " - type: static\n"
|
||||
+ " options:\n"
|
||||
+ " iss: wsmaster\n"
|
||||
+ " nonce_storage:\n"
|
||||
+ " type: void\n",
|
||||
verifierProxy.listenPort,
|
||||
verifierProxy.upstream,
|
||||
workspaceId));
|
||||
}
|
||||
configBuilder.append(" signer_proxy:\n" + " enabled: false\n");
|
||||
return configBuilder.toString();
|
||||
}
|
||||
|
||||
private class VerifierProxy {
|
||||
private Integer listenPort;
|
||||
private String upstream;
|
||||
|
||||
VerifierProxy(Integer listenPort, String upstream) {
|
||||
this.listenPort = listenPort;
|
||||
this.upstream = upstream;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2018 Red Hat, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
*/
|
||||
package org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.emptyMap;
|
||||
import static org.eclipse.che.commons.lang.NameGenerator.generate;
|
||||
import static org.eclipse.che.workspace.infrastructure.kubernetes.Constants.CHE_ORIGINAL_NAME_LABEL;
|
||||
import static org.eclipse.che.workspace.infrastructure.kubernetes.server.KubernetesServerExposer.SERVER_PREFIX;
|
||||
import static org.eclipse.che.workspace.infrastructure.kubernetes.server.KubernetesServerExposer.SERVER_UNIQUE_PART_SIZE;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import io.fabric8.kubernetes.api.model.ConfigMap;
|
||||
import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
|
||||
import io.fabric8.kubernetes.api.model.ContainerBuilder;
|
||||
import io.fabric8.kubernetes.api.model.Pod;
|
||||
import io.fabric8.kubernetes.api.model.PodBuilder;
|
||||
import io.fabric8.kubernetes.api.model.Service;
|
||||
import io.fabric8.kubernetes.api.model.ServicePort;
|
||||
import io.fabric8.kubernetes.api.model.ServicePortBuilder;
|
||||
import io.fabric8.kubernetes.api.model.VolumeBuilder;
|
||||
import io.fabric8.kubernetes.api.model.VolumeMount;
|
||||
import java.security.KeyPair;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.eclipse.che.api.core.model.workspace.config.MachineConfig;
|
||||
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
|
||||
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||
import org.eclipse.che.api.workspace.server.spi.InternalInfrastructureException;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalMachineConfig;
|
||||
import org.eclipse.che.multiuser.machine.authentication.server.signature.SignatureKeyManager;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.server.ServerServiceBuilder;
|
||||
|
||||
/**
|
||||
* Modifies Kubernetes environment to expose the specified service port via JWTProxy.
|
||||
*
|
||||
* <p>Exposing includes the following operation:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Putting Machine configuration into Kubernetes environment if absent;
|
||||
* <li>Putting JwtProxy pod with one container if absent;
|
||||
* <li>Putting JwtProxy service that will expose added JWTProxy pod if absent;
|
||||
* <li>Putting JwtProxy ConfigMap that contains public key and jwtproxy config in yaml format if
|
||||
* absent;
|
||||
* <li>Updating JwtProxy Service to expose port for secure server;
|
||||
* <li>Updating jwtproxy configuration in config map by adding the corresponding verifier proxy
|
||||
* there;
|
||||
* </ul>
|
||||
*
|
||||
* @see JwtProxyConfigBuilder
|
||||
* @see SignatureKeyManager
|
||||
* @author Sergii Leshchenko
|
||||
*/
|
||||
public class JwtProxyProvisioner {
|
||||
|
||||
static final int FIRST_AVAILABLE_PORT = 4400;
|
||||
|
||||
static final int JWT_PROXY_MEMORY_LIMIT_BYTES = 128 * 1024 * 1024; // 128mb
|
||||
|
||||
static final String PUBLIC_KEY_HEADER = "-----BEGIN PUBLIC KEY-----\n";
|
||||
static final String PUBLIC_KEY_FOOTER = "\n-----END PUBLIC KEY-----";
|
||||
|
||||
static final String JWTPROXY_IMAGE = "ksmster/jwtproxy";
|
||||
static final String JWT_PROXY_CONFIG_FILE = "config.yaml";
|
||||
static final String JWT_PROXY_MACHINE_NAME = "che-jwtproxy";
|
||||
static final String JWT_PROXY_POD_NAME = JWT_PROXY_MACHINE_NAME;
|
||||
|
||||
static final String JWT_PROXY_CONFIG_FOLDER = "/config";
|
||||
static final String JWT_PROXY_PUBLIC_KEY_FILE = "mykey.pub";
|
||||
|
||||
private final SignatureKeyManager signatureKeyManager;
|
||||
|
||||
private final RuntimeIdentity identity;
|
||||
|
||||
private final JwtProxyConfigBuilder proxyConfigBuilder;
|
||||
|
||||
private final String serviceName;
|
||||
private int availablePort;
|
||||
|
||||
public JwtProxyProvisioner(RuntimeIdentity identity, SignatureKeyManager signatureKeyManager) {
|
||||
this.signatureKeyManager = signatureKeyManager;
|
||||
|
||||
this.identity = identity;
|
||||
|
||||
this.proxyConfigBuilder = new JwtProxyConfigBuilder(identity.getWorkspaceId());
|
||||
|
||||
this.serviceName = generate(SERVER_PREFIX, SERVER_UNIQUE_PART_SIZE) + "-jwtproxy";
|
||||
this.availablePort = FIRST_AVAILABLE_PORT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies Kubernetes environment to expose the specified service port via JWTProxy.
|
||||
*
|
||||
* @param k8sEnv Kubernetes environment to modify
|
||||
* @param backendServiceName service name that will be exposed
|
||||
* @param backendServicePort service port that will be exposed
|
||||
* @param protocol protocol that will be used for exposed port
|
||||
* @return JWTProxy service port that expose the specified one
|
||||
* @throws InfrastructureException if any exception occurs during port exposing
|
||||
*/
|
||||
public ServicePort expose(
|
||||
KubernetesEnvironment k8sEnv,
|
||||
String backendServiceName,
|
||||
int backendServicePort,
|
||||
String protocol)
|
||||
throws InfrastructureException {
|
||||
ensureJwtProxyInjected(k8sEnv);
|
||||
|
||||
int listenPort = availablePort++;
|
||||
|
||||
proxyConfigBuilder.addVerifierProxy(
|
||||
listenPort, "http://" + backendServiceName + ":" + backendServicePort);
|
||||
k8sEnv
|
||||
.getConfigMaps()
|
||||
.get(getConfigMapName())
|
||||
.getData()
|
||||
.put(JWT_PROXY_CONFIG_FILE, proxyConfigBuilder.build());
|
||||
|
||||
ServicePort exposedPort =
|
||||
new ServicePortBuilder()
|
||||
.withName("server-" + listenPort)
|
||||
.withPort(listenPort)
|
||||
.withProtocol(protocol)
|
||||
.withNewTargetPort(listenPort)
|
||||
.build();
|
||||
|
||||
k8sEnv.getServices().get(getServiceName()).getSpec().getPorts().add(exposedPort);
|
||||
|
||||
return exposedPort;
|
||||
}
|
||||
|
||||
/** Returns service name that exposed JWTProxy Pod. */
|
||||
public String getServiceName() {
|
||||
return serviceName;
|
||||
}
|
||||
|
||||
/** Returns config map name that will be mounted into JWTProxy Pod. */
|
||||
@VisibleForTesting
|
||||
String getConfigMapName() {
|
||||
return "jwtproxy-config-" + identity.getWorkspaceId();
|
||||
}
|
||||
|
||||
private void ensureJwtProxyInjected(KubernetesEnvironment k8sEnv) throws InfrastructureException {
|
||||
if (!k8sEnv.getMachines().containsKey(JWT_PROXY_MACHINE_NAME)) {
|
||||
k8sEnv.getMachines().put(JWT_PROXY_MACHINE_NAME, createJwtProxyMachine());
|
||||
k8sEnv.getPods().put(JWT_PROXY_POD_NAME, createJwtProxyPod(identity));
|
||||
|
||||
KeyPair keyPair = signatureKeyManager.getKeyPair();
|
||||
if (keyPair == null) {
|
||||
throw new InternalInfrastructureException(
|
||||
"Key pair for machine authentication does not exist");
|
||||
}
|
||||
Map<String, String> initConfigMapData = new HashMap<>();
|
||||
initConfigMapData.put(
|
||||
JWT_PROXY_PUBLIC_KEY_FILE,
|
||||
PUBLIC_KEY_HEADER
|
||||
+ java.util.Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded())
|
||||
+ PUBLIC_KEY_FOOTER);
|
||||
|
||||
initConfigMapData.put(JWT_PROXY_CONFIG_FILE, proxyConfigBuilder.build());
|
||||
|
||||
ConfigMap jwtProxyConfigMap =
|
||||
new ConfigMapBuilder()
|
||||
.withNewMetadata()
|
||||
.withName(getConfigMapName())
|
||||
.endMetadata()
|
||||
.withData(initConfigMapData)
|
||||
.build();
|
||||
k8sEnv.getConfigMaps().put(jwtProxyConfigMap.getMetadata().getName(), jwtProxyConfigMap);
|
||||
|
||||
Service jwtProxyService =
|
||||
new ServerServiceBuilder()
|
||||
.withName(serviceName)
|
||||
.withSelectorEntry(CHE_ORIGINAL_NAME_LABEL, JWT_PROXY_MACHINE_NAME)
|
||||
.withMachineName(JWT_PROXY_MACHINE_NAME)
|
||||
.withPorts(emptyList())
|
||||
.build();
|
||||
k8sEnv.getServices().put(jwtProxyService.getMetadata().getName(), jwtProxyService);
|
||||
}
|
||||
}
|
||||
|
||||
private InternalMachineConfig createJwtProxyMachine() {
|
||||
return new InternalMachineConfig(
|
||||
null,
|
||||
emptyMap(),
|
||||
emptyMap(),
|
||||
ImmutableMap.of(
|
||||
MachineConfig.MEMORY_LIMIT_ATTRIBUTE, Integer.toString(JWT_PROXY_MEMORY_LIMIT_BYTES)),
|
||||
null);
|
||||
}
|
||||
|
||||
private Pod createJwtProxyPod(RuntimeIdentity identity) {
|
||||
return new PodBuilder()
|
||||
.withNewMetadata()
|
||||
.withName(JWT_PROXY_POD_NAME)
|
||||
.withAnnotations(
|
||||
ImmutableMap.of(
|
||||
"org.eclipse.che.container.verifier.machine_name", JWT_PROXY_MACHINE_NAME))
|
||||
.endMetadata()
|
||||
.withNewSpec()
|
||||
.withContainers(
|
||||
new ContainerBuilder()
|
||||
.withName("verifier")
|
||||
.withImage(JWTPROXY_IMAGE)
|
||||
.withVolumeMounts(
|
||||
new VolumeMount(
|
||||
JWT_PROXY_CONFIG_FOLDER + "/", "jwtproxy-config-volume", false, null))
|
||||
.withArgs("-config", JWT_PROXY_CONFIG_FOLDER + "/" + JWT_PROXY_CONFIG_FILE)
|
||||
.build())
|
||||
.withVolumes(
|
||||
new VolumeBuilder()
|
||||
.withName("jwtproxy-config-volume")
|
||||
.withNewConfigMap()
|
||||
.withName("jwtproxy-config-" + identity.getWorkspaceId())
|
||||
.endConfigMap()
|
||||
.build())
|
||||
.endSpec()
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2018 Red Hat, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
*/
|
||||
package org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
import io.fabric8.kubernetes.api.model.ServicePort;
|
||||
import java.util.Map;
|
||||
import javax.inject.Inject;
|
||||
import org.eclipse.che.api.core.model.workspace.config.ServerConfig;
|
||||
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
|
||||
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||
import org.eclipse.che.multiuser.machine.authentication.server.signature.SignatureKeyManager;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ExternalServerExposerStrategy;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.SecureServerExposer;
|
||||
|
||||
/**
|
||||
* Exposes secure servers with JWTProxy.
|
||||
*
|
||||
* <p>To expose secure servers it provisions JwtProxy objects into environment with {@link
|
||||
* JwtProxyProvisioner}. Then JwtProxy service port is made public accessible by {@link
|
||||
* ExternalServerExposerStrategy<T>}.
|
||||
*
|
||||
* <p>In this way, requests to exposed secure servers will be routed via JwtProxy pod that is added
|
||||
* one per workspace. And it will be impossible to requests secure servers if there is no machine
|
||||
* token in request.
|
||||
*
|
||||
* @see JwtProxyProvisioner
|
||||
* @author Sergii Leshchenko
|
||||
*/
|
||||
public class JwtProxySecureServerExposer<T extends KubernetesEnvironment>
|
||||
implements SecureServerExposer<T> {
|
||||
|
||||
private final ExternalServerExposerStrategy<T> exposerStrategy;
|
||||
private final JwtProxyProvisioner proxyProvisioner;
|
||||
|
||||
@VisibleForTesting
|
||||
JwtProxySecureServerExposer(
|
||||
JwtProxyProvisioner jwtProxyProvisioner, ExternalServerExposerStrategy<T> exposerStrategy) {
|
||||
this.exposerStrategy = exposerStrategy;
|
||||
this.proxyProvisioner = jwtProxyProvisioner;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public JwtProxySecureServerExposer(
|
||||
@Assisted RuntimeIdentity identity,
|
||||
SignatureKeyManager signatureKeyManager,
|
||||
ExternalServerExposerStrategy<T> exposerStrategy) {
|
||||
this.exposerStrategy = exposerStrategy;
|
||||
|
||||
proxyProvisioner = new JwtProxyProvisioner(identity, signatureKeyManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void expose(
|
||||
T k8sEnv,
|
||||
String machineName,
|
||||
String serviceName,
|
||||
ServicePort servicePort,
|
||||
Map<String, ServerConfig> secureServers)
|
||||
throws InfrastructureException {
|
||||
ServicePort exposedServicePort =
|
||||
proxyProvisioner.expose(
|
||||
k8sEnv,
|
||||
serviceName,
|
||||
servicePort.getTargetPort().getIntVal(),
|
||||
servicePort.getProtocol());
|
||||
|
||||
exposerStrategy.expose(
|
||||
k8sEnv, machineName, proxyProvisioner.getServiceName(), exposedServicePort, secureServers);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2018 Red Hat, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
*/
|
||||
package org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy;
|
||||
|
||||
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.SecureServerExposerFactory;
|
||||
|
||||
/**
|
||||
* Helps to create {@link JwtProxySecureServerExposerFactory} with fields injected from DI
|
||||
* container.
|
||||
*
|
||||
* @author Sergii Leshchenko
|
||||
*/
|
||||
public interface JwtProxySecureServerExposerFactory<T extends KubernetesEnvironment>
|
||||
extends SecureServerExposerFactory<T> {
|
||||
@Override
|
||||
JwtProxySecureServerExposer<T> create(RuntimeIdentity identity);
|
||||
}
|
||||
|
|
@ -41,6 +41,7 @@ import static org.testng.Assert.fail;
|
|||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import io.fabric8.kubernetes.api.model.ConfigMap;
|
||||
import io.fabric8.kubernetes.api.model.Container;
|
||||
import io.fabric8.kubernetes.api.model.ContainerPort;
|
||||
import io.fabric8.kubernetes.api.model.ContainerPortBuilder;
|
||||
|
|
@ -109,6 +110,7 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.model.KubernetesMachi
|
|||
import org.eclipse.che.workspace.infrastructure.kubernetes.model.KubernetesRuntimeState;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.model.KubernetesRuntimeState.RuntimeId;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.model.KubernetesServerImpl;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesConfigsMaps;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesDeployments;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesIngresses;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespace;
|
||||
|
|
@ -172,6 +174,7 @@ public class KubernetesInternalRuntimeTest {
|
|||
@Mock private KubernetesServices services;
|
||||
@Mock private KubernetesIngresses ingresses;
|
||||
@Mock private KubernetesSecrets secrets;
|
||||
@Mock private KubernetesConfigsMaps configMaps;
|
||||
@Mock private KubernetesDeployments deployments;
|
||||
@Mock private KubernetesBootstrapper bootstrapper;
|
||||
@Mock private WorkspaceVolumesStrategy volumesStrategy;
|
||||
|
|
@ -256,6 +259,7 @@ public class KubernetesInternalRuntimeTest {
|
|||
when(namespace.ingresses()).thenReturn(ingresses);
|
||||
when(namespace.deployments()).thenReturn(deployments);
|
||||
when(namespace.secrets()).thenReturn(secrets);
|
||||
when(namespace.configMaps()).thenReturn(configMaps);
|
||||
when(bootstrapperFactory.create(any(), anyList(), any(), any(), any()))
|
||||
.thenReturn(bootstrapper);
|
||||
doReturn(
|
||||
|
|
@ -288,12 +292,15 @@ public class KubernetesInternalRuntimeTest {
|
|||
@Test
|
||||
public void startsKubernetesEnvironment() throws Exception {
|
||||
when(k8sEnv.getSecrets()).thenReturn(ImmutableMap.of("secret", new Secret()));
|
||||
when(k8sEnv.getConfigMaps()).thenReturn(ImmutableMap.of("configMap", new ConfigMap()));
|
||||
|
||||
internalRuntime.internalStart(emptyMap());
|
||||
|
||||
verify(deployments).deploy(any());
|
||||
verify(ingresses).create(any());
|
||||
verify(services).create(any());
|
||||
verify(secrets).create(any());
|
||||
verify(configMaps).create(any());
|
||||
verify(namespace.deployments(), times(2)).watchEvents(any());
|
||||
verify(bootstrapper, times(2)).bootstrapAsync();
|
||||
verify(eventService, times(4)).publish(any());
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ import static java.util.Collections.emptyMap;
|
|||
import static java.util.Collections.singletonList;
|
||||
import static org.eclipse.che.api.core.model.workspace.config.MachineConfig.MEMORY_LIMIT_ATTRIBUTE;
|
||||
import static org.eclipse.che.workspace.infrastructure.kubernetes.Constants.MACHINE_NAME_ANNOTATION_FMT;
|
||||
import static org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironmentFactory.CONFIG_MAP_IGNORED_WARNING_CODE;
|
||||
import static org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironmentFactory.CONFIG_MAP_IGNORED_WARNING_MESSAGE;
|
||||
import static org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironmentFactory.INGRESSES_IGNORED_WARNING_CODE;
|
||||
import static org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironmentFactory.INGRESSES_IGNORED_WARNING_MESSAGE;
|
||||
import static org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironmentFactory.PVC_IGNORED_WARNING_CODE;
|
||||
|
|
@ -33,6 +35,7 @@ import static org.testng.Assert.assertTrue;
|
|||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import io.fabric8.kubernetes.api.model.ConfigMap;
|
||||
import io.fabric8.kubernetes.api.model.Container;
|
||||
import io.fabric8.kubernetes.api.model.DoneableKubernetesList;
|
||||
import io.fabric8.kubernetes.api.model.HasMetadata;
|
||||
|
|
@ -150,13 +153,28 @@ public class KubernetesEnvironmentFactoryTest {
|
|||
final KubernetesEnvironment parsed =
|
||||
k8sEnvironmentFactory.doCreate(internalRecipe, emptyMap(), emptyList());
|
||||
|
||||
assertTrue(parsed.getPersistentVolumeClaims().isEmpty());
|
||||
assertTrue(parsed.getSecrets().isEmpty());
|
||||
assertEquals(parsed.getWarnings().size(), 1);
|
||||
assertEquals(
|
||||
parsed.getWarnings().get(0),
|
||||
new WarningImpl(SECRET_IGNORED_WARNING_CODE, SECRET_IGNORED_WARNING_MESSAGE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ignoreConfigMapsWhenRecipeContainsThem() throws Exception {
|
||||
final List<HasMetadata> recipeObjects = singletonList(new ConfigMap());
|
||||
when(validatedObjects.getItems()).thenReturn(recipeObjects);
|
||||
|
||||
final KubernetesEnvironment parsed =
|
||||
k8sEnvironmentFactory.doCreate(internalRecipe, emptyMap(), emptyList());
|
||||
|
||||
assertTrue(parsed.getConfigMaps().isEmpty());
|
||||
assertEquals(parsed.getWarnings().size(), 1);
|
||||
assertEquals(
|
||||
parsed.getWarnings().get(0),
|
||||
new WarningImpl(CONFIG_MAP_IGNORED_WARNING_CODE, CONFIG_MAP_IGNORED_WARNING_MESSAGE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetsRamLimitAttributeFromKubernetesResource() throws Exception {
|
||||
final long firstMachineRamLimit = 3072;
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ public class KubernetesNamespaceTest {
|
|||
@Mock private KubernetesIngresses ingresses;
|
||||
@Mock private KubernetesPersistentVolumeClaims pvcs;
|
||||
@Mock private KubernetesSecrets secrets;
|
||||
@Mock private KubernetesConfigsMaps configMaps;
|
||||
@Mock private KubernetesClientFactory clientFactory;
|
||||
@Mock private KubernetesClient kubernetesClient;
|
||||
@Mock private NonNamespaceOperation namespaceOperation;
|
||||
|
|
@ -125,7 +126,8 @@ public class KubernetesNamespaceTest {
|
|||
services,
|
||||
pvcs,
|
||||
ingresses,
|
||||
secrets);
|
||||
secrets,
|
||||
configMaps);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -163,6 +165,7 @@ public class KubernetesNamespaceTest {
|
|||
verify(services).delete();
|
||||
verify(deployments).delete();
|
||||
verify(secrets).delete();
|
||||
verify(configMaps).delete();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import org.eclipse.che.api.workspace.server.model.impl.ServerConfigImpl;
|
|||
import org.eclipse.che.workspace.infrastructure.kubernetes.Annotations;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ExternalServerExposerStrategy;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.SecureServerExposer;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.testng.MockitoTestNGListener;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
|
|
@ -52,11 +53,15 @@ import org.testng.annotations.Test;
|
|||
public class KubernetesServerExposerTest {
|
||||
|
||||
@Mock private ExternalServerExposerStrategy<KubernetesEnvironment> externalServerExposerStrategy;
|
||||
@Mock private SecureServerExposer<KubernetesEnvironment> secureServerExposer;
|
||||
|
||||
private static final Map<String, String> ATTRIBUTES_MAP = singletonMap("key", "value");
|
||||
private static final Map<String, String> INTERNAL_SERVER_ATTRIBUTE_MAP =
|
||||
singletonMap(ServerConfig.INTERNAL_SERVER_ATTRIBUTE, Boolean.TRUE.toString());
|
||||
|
||||
private static final Map<String, String> SECURE_SERVER_ATTRIBUTE_MAP =
|
||||
singletonMap(ServerConfig.SECURE_SERVER_ATTRIBUTE, Boolean.TRUE.toString());
|
||||
|
||||
private static final Pattern SERVER_PREFIX_REGEX =
|
||||
Pattern.compile('^' + SERVER_PREFIX + "[A-z0-9]{" + SERVER_UNIQUE_PART_SIZE + "}-pod-main$");
|
||||
private static final String MACHINE_NAME = "pod/main";
|
||||
|
|
@ -82,7 +87,12 @@ public class KubernetesServerExposerTest {
|
|||
KubernetesEnvironment.builder().setPods(ImmutableMap.of("pod", pod)).build();
|
||||
this.serverExposer =
|
||||
new KubernetesServerExposer<>(
|
||||
externalServerExposerStrategy, MACHINE_NAME, pod, container, kubernetesEnvironment);
|
||||
externalServerExposerStrategy,
|
||||
secureServerExposer,
|
||||
MACHINE_NAME,
|
||||
pod,
|
||||
container,
|
||||
kubernetesEnvironment);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -270,33 +280,36 @@ public class KubernetesServerExposerTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void shouldExposeInternalAndExternalServers() throws Exception {
|
||||
public void shouldExposeInternalAndExternalAndSecureServers() throws Exception {
|
||||
// given
|
||||
ServerConfigImpl secureServerConfig =
|
||||
new ServerConfigImpl("8282/tcp", "http", "/api", SECURE_SERVER_ATTRIBUTE_MAP);
|
||||
ServerConfigImpl internalServerConfig =
|
||||
new ServerConfigImpl("8080/tcp", "http", "/api", INTERNAL_SERVER_ATTRIBUTE_MAP);
|
||||
ServerConfigImpl externalServerConfig =
|
||||
new ServerConfigImpl("9090/tcp", "http", "/api", ATTRIBUTES_MAP);
|
||||
Map<String, ServerConfigImpl> serversToExpose =
|
||||
ImmutableMap.of("int-server", internalServerConfig, "ext-server", externalServerConfig);
|
||||
ImmutableMap.of(
|
||||
"int-server",
|
||||
internalServerConfig,
|
||||
"ext-server",
|
||||
externalServerConfig,
|
||||
"secure-server",
|
||||
secureServerConfig);
|
||||
|
||||
// when
|
||||
serverExposer.expose(serversToExpose);
|
||||
|
||||
// then
|
||||
assertThatInternalServerIsExposed(
|
||||
MACHINE_NAME,
|
||||
"int-server",
|
||||
"tcp",
|
||||
8080,
|
||||
new ServerConfigImpl(internalServerConfig).withAttributes(INTERNAL_SERVER_ATTRIBUTE_MAP));
|
||||
MACHINE_NAME, "int-server", "tcp", 8080, new ServerConfigImpl(internalServerConfig));
|
||||
assertThatExternalServerIsExposed(
|
||||
MACHINE_NAME,
|
||||
"tcp",
|
||||
9090,
|
||||
"ext-server",
|
||||
new ServerConfigImpl(externalServerConfig).withAttributes(ATTRIBUTES_MAP));
|
||||
MACHINE_NAME, "tcp", 9090, "ext-server", new ServerConfigImpl(externalServerConfig));
|
||||
assertThatSecureServerIsExposed(
|
||||
MACHINE_NAME, "tcp", 8282, "secure-server", new ServerConfigImpl(secureServerConfig));
|
||||
}
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
private void assertThatExternalServerIsExposed(
|
||||
String machineName,
|
||||
String portProtocol,
|
||||
|
|
@ -314,38 +327,14 @@ public class KubernetesServerExposerTest {
|
|||
Integer port,
|
||||
Map<String, ServerConfig> expectedServers) {
|
||||
// then
|
||||
assertTrue(
|
||||
container
|
||||
.getPorts()
|
||||
.stream()
|
||||
.anyMatch(
|
||||
p ->
|
||||
p.getContainerPort().equals(port)
|
||||
&& p.getProtocol().equals(portProtocol.toUpperCase())));
|
||||
assertThatContainerPortIsExposed(portProtocol, port);
|
||||
// ensure that service is created
|
||||
|
||||
Service service = null;
|
||||
for (Entry<String, Service> entry : kubernetesEnvironment.getServices().entrySet()) {
|
||||
if (SERVER_PREFIX_REGEX.matcher(entry.getKey()).matches()) {
|
||||
service = entry.getValue();
|
||||
break;
|
||||
}
|
||||
}
|
||||
Service service = findContainerRelatedService();
|
||||
assertNotNull(service);
|
||||
|
||||
// ensure that required service port is exposed
|
||||
Optional<ServicePort> servicePortOpt =
|
||||
service
|
||||
.getSpec()
|
||||
.getPorts()
|
||||
.stream()
|
||||
.filter(p -> p.getTargetPort().getIntVal().equals(port))
|
||||
.findAny();
|
||||
assertTrue(servicePortOpt.isPresent());
|
||||
ServicePort servicePort = servicePortOpt.get();
|
||||
assertEquals(servicePort.getTargetPort().getIntVal(), port);
|
||||
assertEquals(servicePort.getPort(), port);
|
||||
assertEquals(servicePort.getName(), SERVER_PREFIX + "-" + port);
|
||||
ServicePort servicePort = assertThatServicePortIsExposed(port, service);
|
||||
|
||||
Annotations.Deserializer serviceAnnotations =
|
||||
Annotations.newDeserializer(service.getMetadata().getAnnotations());
|
||||
|
|
@ -360,6 +349,37 @@ public class KubernetesServerExposerTest {
|
|||
expectedServers);
|
||||
}
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
private void assertThatSecureServerIsExposed(
|
||||
String machineName,
|
||||
String portProtocol,
|
||||
Integer port,
|
||||
String serverName,
|
||||
ServerConfig serverConfig)
|
||||
throws Exception {
|
||||
// then
|
||||
assertThatContainerPortIsExposed(portProtocol, port);
|
||||
// ensure that service is created
|
||||
|
||||
Service service = findContainerRelatedService();
|
||||
assertNotNull(service);
|
||||
|
||||
// ensure that required service port is exposed
|
||||
ServicePort servicePort = assertThatServicePortIsExposed(port, service);
|
||||
|
||||
Annotations.Deserializer serviceAnnotations =
|
||||
Annotations.newDeserializer(service.getMetadata().getAnnotations());
|
||||
assertEquals(serviceAnnotations.machineName(), machineName);
|
||||
|
||||
verify(secureServerExposer)
|
||||
.expose(
|
||||
kubernetesEnvironment,
|
||||
machineName,
|
||||
service.getMetadata().getName(),
|
||||
servicePort,
|
||||
ImmutableMap.of(serverName, serverConfig));
|
||||
}
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
private void assertThatInternalServerIsExposed(
|
||||
String machineName,
|
||||
|
|
@ -367,7 +387,26 @@ public class KubernetesServerExposerTest {
|
|||
String portProtocol,
|
||||
Integer port,
|
||||
ServerConfigImpl expected) {
|
||||
// then
|
||||
assertThatContainerPortIsExposed(portProtocol, port);
|
||||
|
||||
// ensure that service is created
|
||||
|
||||
Service service = findContainerRelatedService();
|
||||
assertNotNull(service);
|
||||
|
||||
// ensure that required service port is exposed
|
||||
assertThatServicePortIsExposed(port, service);
|
||||
|
||||
Annotations.Deserializer serviceAnnotations =
|
||||
Annotations.newDeserializer(service.getMetadata().getAnnotations());
|
||||
assertEquals(serviceAnnotations.machineName(), machineName);
|
||||
|
||||
Map<String, ServerConfigImpl> servers = serviceAnnotations.servers();
|
||||
ServerConfig serverConfig = servers.get(serverNameRegex);
|
||||
assertEquals(serverConfig, expected);
|
||||
}
|
||||
|
||||
private void assertThatContainerPortIsExposed(String portProtocol, Integer port) {
|
||||
assertTrue(
|
||||
container
|
||||
.getPorts()
|
||||
|
|
@ -376,8 +415,9 @@ public class KubernetesServerExposerTest {
|
|||
p ->
|
||||
p.getContainerPort().equals(port)
|
||||
&& p.getProtocol().equals(portProtocol.toUpperCase())));
|
||||
// ensure that service is created
|
||||
}
|
||||
|
||||
private Service findContainerRelatedService() {
|
||||
Service service = null;
|
||||
for (Entry<String, Service> entry : kubernetesEnvironment.getServices().entrySet()) {
|
||||
if (SERVER_PREFIX_REGEX.matcher(entry.getKey()).matches()) {
|
||||
|
|
@ -385,9 +425,10 @@ public class KubernetesServerExposerTest {
|
|||
break;
|
||||
}
|
||||
}
|
||||
assertNotNull(service);
|
||||
return service;
|
||||
}
|
||||
|
||||
// ensure that required service port is exposed
|
||||
private ServicePort assertThatServicePortIsExposed(Integer port, Service service) {
|
||||
Optional<ServicePort> servicePortOpt =
|
||||
service
|
||||
.getSpec()
|
||||
|
|
@ -400,13 +441,6 @@ public class KubernetesServerExposerTest {
|
|||
assertEquals(servicePort.getTargetPort().getIntVal(), port);
|
||||
assertEquals(servicePort.getPort(), port);
|
||||
assertEquals(servicePort.getName(), SERVER_PREFIX + "-" + port);
|
||||
|
||||
Annotations.Deserializer serviceAnnotations =
|
||||
Annotations.newDeserializer(service.getMetadata().getAnnotations());
|
||||
assertEquals(serviceAnnotations.machineName(), machineName);
|
||||
|
||||
Map<String, ServerConfigImpl> servers = serviceAnnotations.servers();
|
||||
ServerConfig serverConfig = servers.get(serverNameRegex);
|
||||
assertEquals(serverConfig, expected);
|
||||
return servicePort;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2018 Red Hat, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
*/
|
||||
package org.eclipse.che.workspace.infrastructure.kubernetes.server.secure;
|
||||
|
||||
import static org.testng.Assert.assertSame;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.eclipse.che.inject.ConfigurationException;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.testng.MockitoTestNGListener;
|
||||
import org.testng.annotations.Listeners;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/** @author Sergii Leshchenko */
|
||||
@Listeners(MockitoTestNGListener.class)
|
||||
public class SecureServerExposerFactoryProviderTest {
|
||||
@Mock private DefaultSecureServersFactory<KubernetesEnvironment> defaultSecureServersFactory;
|
||||
|
||||
@Mock private SecureServerExposerFactory<KubernetesEnvironment> customSecureServerExposer;
|
||||
|
||||
private Map<String, SecureServerExposerFactory<KubernetesEnvironment>> factories =
|
||||
new HashMap<>();
|
||||
|
||||
@Test
|
||||
public void shouldReturnDefaultSecureServerExposerWhenAgentAuthIsDisabled() {
|
||||
// given
|
||||
SecureServerExposerFactoryProvider<KubernetesEnvironment> factoryProvider =
|
||||
new SecureServerExposerFactoryProvider<>(
|
||||
false, "custom", defaultSecureServersFactory, factories);
|
||||
|
||||
// when
|
||||
SecureServerExposerFactory<KubernetesEnvironment> factory = factoryProvider.get();
|
||||
|
||||
// then
|
||||
assertSame(factory, defaultSecureServersFactory);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnConfiguredSecureServerExposerWhenAgentAuthIsEnabled() {
|
||||
// given
|
||||
factories.put("custom", customSecureServerExposer);
|
||||
SecureServerExposerFactoryProvider<KubernetesEnvironment> factoryProvider =
|
||||
new SecureServerExposerFactoryProvider<>(
|
||||
true, "custom", defaultSecureServersFactory, factories);
|
||||
|
||||
// when
|
||||
SecureServerExposerFactory<KubernetesEnvironment> factory = factoryProvider.get();
|
||||
|
||||
// then
|
||||
assertSame(factory, customSecureServerExposer);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = ConfigurationException.class)
|
||||
public void shouldThrowAnExceptionIfConfiguredSecureServerWasNotFound() {
|
||||
// given
|
||||
SecureServerExposerFactoryProvider<KubernetesEnvironment> factoryProvider =
|
||||
new SecureServerExposerFactoryProvider<>(
|
||||
true, "non-existing", defaultSecureServersFactory, factories);
|
||||
|
||||
// when
|
||||
factoryProvider.get();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2018 Red Hat, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
*/
|
||||
package org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
import org.testng.annotations.Test;
|
||||
import org.testng.reporters.Files;
|
||||
|
||||
/**
|
||||
* Tests {@link JwtProxyConfigBuilder}.
|
||||
*
|
||||
* @author Sergii Leshchenko
|
||||
*/
|
||||
public class JwtProxyConfigBuilderTest {
|
||||
|
||||
private JwtProxyConfigBuilder jwtProxyConfigBuilder;
|
||||
|
||||
@BeforeMethod
|
||||
public void setUp() {
|
||||
jwtProxyConfigBuilder = new JwtProxyConfigBuilder("workspace123");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldBuildJwtProxyConfigInYamlFormat() throws Exception {
|
||||
// given
|
||||
jwtProxyConfigBuilder.addVerifierProxy(8080, "http://tomcat:8080");
|
||||
jwtProxyConfigBuilder.addVerifierProxy(4101, "ws://terminal:4101");
|
||||
|
||||
// when
|
||||
String jwtProxyConfigYaml = jwtProxyConfigBuilder.build();
|
||||
|
||||
// then
|
||||
assertEquals(
|
||||
jwtProxyConfigYaml,
|
||||
Files.readFile(getClass().getClassLoader().getResourceAsStream("jwtproxy-confg.yaml")));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2018 Red Hat, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
*/
|
||||
package org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy;
|
||||
|
||||
import static org.eclipse.che.workspace.infrastructure.kubernetes.server.KubernetesServerExposer.SERVER_PREFIX;
|
||||
import static org.eclipse.che.workspace.infrastructure.kubernetes.server.KubernetesServerExposer.SERVER_UNIQUE_PART_SIZE;
|
||||
import static org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.JwtProxyProvisioner.JWT_PROXY_CONFIG_FILE;
|
||||
import static org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.JwtProxyProvisioner.JWT_PROXY_PUBLIC_KEY_FILE;
|
||||
import static org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.JwtProxyProvisioner.PUBLIC_KEY_FOOTER;
|
||||
import static org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.JwtProxyProvisioner.PUBLIC_KEY_HEADER;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertNotNull;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.ConfigMap;
|
||||
import io.fabric8.kubernetes.api.model.Pod;
|
||||
import io.fabric8.kubernetes.api.model.Service;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Base64;
|
||||
import java.util.regex.Pattern;
|
||||
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
|
||||
import org.eclipse.che.api.workspace.server.model.impl.RuntimeIdentityImpl;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalMachineConfig;
|
||||
import org.eclipse.che.multiuser.machine.authentication.server.signature.SignatureKeyManager;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.testng.MockitoTestNGListener;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
import org.testng.annotations.Listeners;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/**
|
||||
* Tests {@link JwtProxyProvisioner}.
|
||||
*
|
||||
* @author Sergii Leshchenko
|
||||
*/
|
||||
@Listeners(MockitoTestNGListener.class)
|
||||
public class JwtProxyProvisionerTest {
|
||||
|
||||
private static final String WORKSPACE_ID = "workspace123";
|
||||
private static final Pattern JWTPROXY_SERVICE_NAME_PATTERN =
|
||||
Pattern.compile(SERVER_PREFIX + "\\w{" + SERVER_UNIQUE_PART_SIZE + "}-jwtproxy");
|
||||
private final RuntimeIdentity runtimeId =
|
||||
new RuntimeIdentityImpl(WORKSPACE_ID, "env123", "owner123");
|
||||
|
||||
@Mock private SignatureKeyManager signatureKeyManager;
|
||||
private KeyPair keyPair;
|
||||
@Mock private PublicKey publicKey;
|
||||
|
||||
private JwtProxyProvisioner jwtProxyProvisioner;
|
||||
private KubernetesEnvironment k8sEnv;
|
||||
|
||||
@BeforeMethod
|
||||
public void setUp() {
|
||||
keyPair = new KeyPair(publicKey, null);
|
||||
when(signatureKeyManager.getKeyPair()).thenReturn(keyPair);
|
||||
when(publicKey.getEncoded()).thenReturn("publickey".getBytes());
|
||||
|
||||
jwtProxyProvisioner = new JwtProxyProvisioner(runtimeId, signatureKeyManager);
|
||||
k8sEnv = KubernetesEnvironment.builder().build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnGeneratedJwtProxyServiceName() {
|
||||
// when
|
||||
String jwtProxyServiceName = jwtProxyProvisioner.getServiceName();
|
||||
|
||||
// then
|
||||
assertTrue(JWTPROXY_SERVICE_NAME_PATTERN.matcher(jwtProxyServiceName).matches());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnGeneratedJwtProxyConfigMapName() {
|
||||
// when
|
||||
String jwtProxyConfigMap = jwtProxyProvisioner.getConfigMapName();
|
||||
|
||||
// then
|
||||
assertEquals(jwtProxyConfigMap, "jwtproxy-config-" + WORKSPACE_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldProvisionJwtProxyRelatedObjectsIntoKubernetesEnvironment() throws Exception {
|
||||
// when
|
||||
jwtProxyProvisioner.expose(k8sEnv, "terminal", 4401, "TCP");
|
||||
|
||||
// then
|
||||
InternalMachineConfig jwtProxyMachine =
|
||||
k8sEnv.getMachines().get(JwtProxyProvisioner.JWT_PROXY_MACHINE_NAME);
|
||||
assertNotNull(jwtProxyMachine);
|
||||
|
||||
ConfigMap configMap = k8sEnv.getConfigMaps().get(jwtProxyProvisioner.getConfigMapName());
|
||||
assertNotNull(configMap);
|
||||
assertEquals(
|
||||
configMap.getData().get(JWT_PROXY_PUBLIC_KEY_FILE),
|
||||
PUBLIC_KEY_HEADER
|
||||
+ Base64.getEncoder().encodeToString("publickey".getBytes())
|
||||
+ PUBLIC_KEY_FOOTER);
|
||||
assertNotNull(configMap.getData().get(JWT_PROXY_CONFIG_FILE));
|
||||
|
||||
Pod jwtProxyPod = k8sEnv.getPods().get("che-jwtproxy");
|
||||
assertNotNull(jwtProxyPod);
|
||||
|
||||
Service jwtProxyService = k8sEnv.getServices().get(jwtProxyProvisioner.getServiceName());
|
||||
assertNotNull(jwtProxyService);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2018 Red Hat, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
*/
|
||||
package org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import io.fabric8.kubernetes.api.model.IntOrString;
|
||||
import io.fabric8.kubernetes.api.model.ServicePort;
|
||||
import java.util.Map;
|
||||
import org.eclipse.che.api.core.model.workspace.config.ServerConfig;
|
||||
import org.eclipse.che.api.workspace.server.model.impl.ServerConfigImpl;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ExternalServerExposerStrategy;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.testng.MockitoTestNGListener;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
import org.testng.annotations.Listeners;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/**
|
||||
* Tests {@link JwtProxySecureServerExposer}
|
||||
*
|
||||
* @author Sergii Leshchenko
|
||||
*/
|
||||
@Listeners(MockitoTestNGListener.class)
|
||||
public class JwtProxySecureServerExposerTest {
|
||||
|
||||
private static final String MACHINE_SERVICE_NAME = "service123";
|
||||
private static final String MACHINE_NAME = "machine123";
|
||||
public static final String JWT_PROXY_SERVICE_NAME = "jwtProxyServiceName";
|
||||
|
||||
@Mock private KubernetesEnvironment k8sEnv;
|
||||
@Mock private JwtProxyProvisioner jwtProxyProvisioner;
|
||||
@Mock private ExternalServerExposerStrategy<KubernetesEnvironment> externalServerExposer;
|
||||
|
||||
private JwtProxySecureServerExposer<KubernetesEnvironment> secureServerExposer;
|
||||
|
||||
@BeforeMethod
|
||||
public void setUp() {
|
||||
secureServerExposer =
|
||||
new JwtProxySecureServerExposer<>(jwtProxyProvisioner, externalServerExposer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldExposeSecureServersWithNewJwtProxyServicePort() throws Exception {
|
||||
// given
|
||||
ServicePort machineServicePort = new ServicePort();
|
||||
machineServicePort.setTargetPort(new IntOrString(8080));
|
||||
machineServicePort.setProtocol("TCP");
|
||||
Map<String, ServerConfig> servers =
|
||||
ImmutableMap.of(
|
||||
"server1",
|
||||
new ServerConfigImpl("8080/tcp", "http", "/api", ImmutableMap.of("secure", "true")),
|
||||
"server2",
|
||||
new ServerConfigImpl("8080/tcp", "ws", "/connect", ImmutableMap.of("secure", "true")));
|
||||
|
||||
ServicePort jwtProxyServicePort = new ServicePort();
|
||||
doReturn(jwtProxyServicePort)
|
||||
.when(jwtProxyProvisioner)
|
||||
.expose(any(), anyString(), anyInt(), anyString());
|
||||
|
||||
when(jwtProxyProvisioner.getServiceName()).thenReturn(JWT_PROXY_SERVICE_NAME);
|
||||
|
||||
// when
|
||||
secureServerExposer.expose(
|
||||
k8sEnv, MACHINE_NAME, MACHINE_SERVICE_NAME, machineServicePort, servers);
|
||||
|
||||
// then
|
||||
verify(jwtProxyProvisioner).expose(k8sEnv, MACHINE_SERVICE_NAME, 8080, "TCP");
|
||||
verify(externalServerExposer)
|
||||
.expose(k8sEnv, MACHINE_NAME, JWT_PROXY_SERVICE_NAME, jwtProxyServicePort, servers);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
jwtproxy:
|
||||
verifier_proxies:
|
||||
- listen_addr: :8080
|
||||
verifier:
|
||||
upstream: http://tomcat:8080/
|
||||
audience: http://workspace123
|
||||
max_skew: 1m
|
||||
max_ttl: 3h
|
||||
key_server:
|
||||
type: preshared
|
||||
options:
|
||||
issuer: wsmaster
|
||||
key_id: mykey
|
||||
public_key_path: /config/mykey.pub
|
||||
claims_verifiers:
|
||||
- type: static
|
||||
options:
|
||||
iss: wsmaster
|
||||
nonce_storage:
|
||||
type: void
|
||||
- listen_addr: :4101
|
||||
verifier:
|
||||
upstream: ws://terminal:4101/
|
||||
audience: http://workspace123
|
||||
max_skew: 1m
|
||||
max_ttl: 3h
|
||||
key_server:
|
||||
type: preshared
|
||||
options:
|
||||
issuer: wsmaster
|
||||
key_id: mykey
|
||||
public_key_path: /config/mykey.pub
|
||||
claims_verifiers:
|
||||
- type: static
|
||||
options:
|
||||
iss: wsmaster
|
||||
nonce_storage:
|
||||
type: void
|
||||
signer_proxy:
|
||||
enabled: false
|
||||
|
|
@ -42,6 +42,10 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.provision.KubernetesC
|
|||
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.env.LogsRootEnvVariableProvider;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.server.ServersConverter;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.server.external.ExternalServerExposerStrategy;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.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;
|
||||
import org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironment;
|
||||
import org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironmentFactory;
|
||||
|
|
@ -97,5 +101,26 @@ public class OpenShiftInfraModule extends AbstractModule {
|
|||
MapBinder<String, WorkspaceNextApplier> wsNext =
|
||||
MapBinder.newMapBinder(binder(), String.class, WorkspaceNextApplier.class);
|
||||
wsNext.addBinding(OpenShiftEnvironment.TYPE).to(KubernetesWorkspaceNextApplier.class);
|
||||
|
||||
bind(new TypeLiteral<SecureServerExposerFactory<OpenShiftEnvironment>>() {})
|
||||
.toProvider(new TypeLiteral<SecureServerExposerFactoryProvider<OpenShiftEnvironment>>() {});
|
||||
|
||||
MapBinder<String, SecureServerExposerFactory<OpenShiftEnvironment>>
|
||||
secureServerExposerFactories =
|
||||
MapBinder.newMapBinder(
|
||||
binder(),
|
||||
new TypeLiteral<String>() {},
|
||||
new TypeLiteral<SecureServerExposerFactory<OpenShiftEnvironment>>() {});
|
||||
|
||||
secureServerExposerFactories
|
||||
.addBinding("default")
|
||||
.to(new TypeLiteral<DefaultSecureServersFactory<OpenShiftEnvironment>>() {});
|
||||
|
||||
install(
|
||||
new FactoryModuleBuilder()
|
||||
.build(new TypeLiteral<JwtProxySecureServerExposerFactory<OpenShiftEnvironment>>() {}));
|
||||
secureServerExposerFactories
|
||||
.addBinding("jwtproxy")
|
||||
.to(new TypeLiteral<JwtProxySecureServerExposerFactory<OpenShiftEnvironment>>() {});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ package org.eclipse.che.workspace.infrastructure.openshift;
|
|||
|
||||
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.Pod;
|
||||
import io.fabric8.kubernetes.api.model.Secret;
|
||||
import io.fabric8.kubernetes.api.model.Service;
|
||||
|
|
@ -98,6 +99,10 @@ public class OpenShiftInternalRuntime extends KubernetesInternalRuntime<OpenShif
|
|||
project.secrets().create(secret);
|
||||
}
|
||||
|
||||
for (ConfigMap configMap : osEnv.getConfigMaps().values()) {
|
||||
project.configMaps().create(configMap);
|
||||
}
|
||||
|
||||
List<Service> createdServices = new ArrayList<>();
|
||||
for (Service service : osEnv.getServices().values()) {
|
||||
createdServices.add(project.services().create(service));
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
*/
|
||||
package org.eclipse.che.workspace.infrastructure.openshift.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;
|
||||
|
|
@ -54,8 +55,9 @@ public class OpenShiftEnvironment extends KubernetesEnvironment {
|
|||
Map<String, Ingress> ingresses,
|
||||
Map<String, PersistentVolumeClaim> pvcs,
|
||||
Map<String, Secret> secrets,
|
||||
Map<String, ConfigMap> configMaps,
|
||||
Map<String, Route> routes) {
|
||||
super(internalRecipe, machines, warnings, pods, services, ingresses, pvcs, secrets);
|
||||
super(internalRecipe, machines, warnings, pods, services, ingresses, pvcs, secrets, configMaps);
|
||||
this.routes = routes;
|
||||
}
|
||||
|
||||
|
|
@ -117,6 +119,12 @@ public class OpenShiftEnvironment extends KubernetesEnvironment {
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder setConfigMaps(Map<String, ConfigMap> configMaps) {
|
||||
this.configMaps.putAll(configMaps);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRoutes(Map<String, Route> route) {
|
||||
this.routes.putAll(route);
|
||||
return this;
|
||||
|
|
@ -124,7 +132,16 @@ public class OpenShiftEnvironment extends KubernetesEnvironment {
|
|||
|
||||
public OpenShiftEnvironment build() {
|
||||
return new OpenShiftEnvironment(
|
||||
internalRecipe, machines, warnings, pods, services, ingresses, pvcs, secrets, routes);
|
||||
internalRecipe,
|
||||
machines,
|
||||
warnings,
|
||||
pods,
|
||||
services,
|
||||
ingresses,
|
||||
pvcs,
|
||||
secrets,
|
||||
configMaps,
|
||||
routes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import static java.lang.String.format;
|
|||
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;
|
||||
|
|
@ -68,6 +69,10 @@ public class OpenShiftEnvironmentFactory extends InternalEnvironmentFactory<Open
|
|||
static final String SECRET_IGNORED_WARNING_MESSAGE =
|
||||
"Secrets specified in OpenShift 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 OpenShiftClientFactory clientFactory;
|
||||
private final KubernetesEnvironmentValidator envValidator;
|
||||
private final String defaultMachineMemorySizeAttribute;
|
||||
|
|
@ -122,6 +127,7 @@ public class OpenShiftEnvironmentFactory extends InternalEnvironmentFactory<Open
|
|||
boolean isAnyRoutePresent = false;
|
||||
boolean isAnyPVCPresent = false;
|
||||
boolean isAnySecretPresent = false;
|
||||
boolean isAnyConfigMapPresent = false;
|
||||
for (HasMetadata object : list.getItems()) {
|
||||
if (object instanceof DeploymentConfig) {
|
||||
throw new ValidationException("Supporting of deployment configs is not implemented yet.");
|
||||
|
|
@ -137,6 +143,8 @@ public class OpenShiftEnvironmentFactory extends InternalEnvironmentFactory<Open
|
|||
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()));
|
||||
|
|
@ -155,6 +163,11 @@ public class OpenShiftEnvironmentFactory extends InternalEnvironmentFactory<Open
|
|||
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());
|
||||
|
||||
OpenShiftEnvironment osEnv =
|
||||
|
|
@ -166,6 +179,7 @@ public class OpenShiftEnvironmentFactory extends InternalEnvironmentFactory<Open
|
|||
.setServices(services)
|
||||
.setPersistentVolumeClaims(new HashMap<>())
|
||||
.setSecrets(new HashMap<>())
|
||||
.setConfigMaps(new HashMap<>())
|
||||
.setRoutes(new HashMap<>())
|
||||
.build();
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import io.fabric8.openshift.api.model.Route;
|
|||
import io.fabric8.openshift.client.OpenShiftClient;
|
||||
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesInfrastructureException;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesConfigsMaps;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesDeployments;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesIngresses;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespace;
|
||||
|
|
@ -46,8 +47,18 @@ public class OpenShiftProject extends KubernetesNamespace {
|
|||
OpenShiftRoutes routes,
|
||||
KubernetesPersistentVolumeClaims pvcs,
|
||||
KubernetesIngresses ingresses,
|
||||
KubernetesSecrets secrets) {
|
||||
super(clientFactory, workspaceId, name, deployments, services, pvcs, ingresses, secrets);
|
||||
KubernetesSecrets secrets,
|
||||
KubernetesConfigsMaps configMaps) {
|
||||
super(
|
||||
clientFactory,
|
||||
workspaceId,
|
||||
name,
|
||||
deployments,
|
||||
services,
|
||||
pvcs,
|
||||
ingresses,
|
||||
secrets,
|
||||
configMaps);
|
||||
this.clientFactory = clientFactory;
|
||||
this.routes = routes;
|
||||
}
|
||||
|
|
@ -84,7 +95,12 @@ public class OpenShiftProject extends KubernetesNamespace {
|
|||
|
||||
/** Removes all object except persistent volume claims inside project. */
|
||||
public void cleanUp() throws InfrastructureException {
|
||||
doRemove(routes::delete, services()::delete, deployments()::delete, secrets()::delete);
|
||||
doRemove(
|
||||
routes::delete,
|
||||
services()::delete,
|
||||
deployments()::delete,
|
||||
secrets()::delete,
|
||||
configMaps()::delete);
|
||||
}
|
||||
|
||||
private void create(String projectName, KubernetesClient kubeClient, OpenShiftClient ocClient)
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import static org.testng.Assert.fail;
|
|||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import io.fabric8.kubernetes.api.model.ConfigMap;
|
||||
import io.fabric8.kubernetes.api.model.Container;
|
||||
import io.fabric8.kubernetes.api.model.ContainerPort;
|
||||
import io.fabric8.kubernetes.api.model.ContainerPortBuilder;
|
||||
|
|
@ -70,6 +71,7 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.bootstrapper.Kubernet
|
|||
import org.eclipse.che.workspace.infrastructure.kubernetes.bootstrapper.KubernetesBootstrapperFactory;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.cache.KubernetesMachineCache;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.cache.KubernetesRuntimeStateCache;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesConfigsMaps;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesDeployments;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesSecrets;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesServices;
|
||||
|
|
@ -125,6 +127,7 @@ public class OpenShiftInternalRuntimeTest {
|
|||
@Mock private OpenShiftProject project;
|
||||
@Mock private KubernetesServices services;
|
||||
@Mock private KubernetesSecrets secrets;
|
||||
@Mock private KubernetesConfigsMaps configMaps;
|
||||
@Mock private OpenShiftRoutes routes;
|
||||
@Mock private KubernetesDeployments deployments;
|
||||
@Mock private KubernetesBootstrapper bootstrapper;
|
||||
|
|
@ -195,6 +198,7 @@ public class OpenShiftInternalRuntimeTest {
|
|||
when(project.services()).thenReturn(services);
|
||||
when(project.routes()).thenReturn(routes);
|
||||
when(project.secrets()).thenReturn(secrets);
|
||||
when(project.configMaps()).thenReturn(configMaps);
|
||||
when(project.deployments()).thenReturn(deployments);
|
||||
when(bootstrapperFactory.create(any(), anyList(), any(), any(), any()))
|
||||
.thenReturn(bootstrapper);
|
||||
|
|
@ -218,6 +222,7 @@ public class OpenShiftInternalRuntimeTest {
|
|||
when(osEnv.getRoutes()).thenReturn(allRoutes);
|
||||
when(osEnv.getPods()).thenReturn(allPods);
|
||||
when(osEnv.getSecrets()).thenReturn(ImmutableMap.of("secret", new Secret()));
|
||||
when(osEnv.getConfigMaps()).thenReturn(ImmutableMap.of("configMap", new ConfigMap()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -234,6 +239,7 @@ public class OpenShiftInternalRuntimeTest {
|
|||
verify(routes).create(any());
|
||||
verify(services).create(any());
|
||||
verify(secrets).create(any());
|
||||
verify(configMaps).create(any());
|
||||
|
||||
verify(project.deployments(), times(2)).watchEvents(any());
|
||||
verify(eventService, times(2)).publish(any());
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ import static java.util.Collections.emptyMap;
|
|||
import static java.util.Collections.singletonList;
|
||||
import static org.eclipse.che.api.core.model.workspace.config.MachineConfig.MEMORY_LIMIT_ATTRIBUTE;
|
||||
import static org.eclipse.che.workspace.infrastructure.kubernetes.Constants.MACHINE_NAME_ANNOTATION_FMT;
|
||||
import static org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironmentFactory.CONFIG_MAP_IGNORED_WARNING_CODE;
|
||||
import static org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironmentFactory.CONFIG_MAP_IGNORED_WARNING_MESSAGE;
|
||||
import static org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironmentFactory.PVC_IGNORED_WARNING_CODE;
|
||||
import static org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironmentFactory.PVC_IGNORED_WARNING_MESSAGE;
|
||||
import static org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironmentFactory.ROUTES_IGNORED_WARNING_MESSAGE;
|
||||
|
|
@ -33,6 +35,7 @@ import static org.testng.Assert.assertTrue;
|
|||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import io.fabric8.kubernetes.api.model.ConfigMap;
|
||||
import io.fabric8.kubernetes.api.model.Container;
|
||||
import io.fabric8.kubernetes.api.model.DoneableKubernetesList;
|
||||
import io.fabric8.kubernetes.api.model.HasMetadata;
|
||||
|
|
@ -59,6 +62,7 @@ import org.eclipse.che.api.workspace.server.model.impl.WarningImpl;
|
|||
import org.eclipse.che.api.workspace.server.spi.environment.InternalEnvironment;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalMachineConfig;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalRecipe;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironmentValidator;
|
||||
import org.eclipse.che.workspace.infrastructure.openshift.OpenShiftClientFactory;
|
||||
import org.mockito.Mock;
|
||||
|
|
@ -137,7 +141,7 @@ public class OpenShiftEnvironmentFactoryTest {
|
|||
final OpenShiftEnvironment parsed =
|
||||
osEnvironmentFactory.doCreate(internalRecipe, emptyMap(), emptyList());
|
||||
|
||||
assertTrue(parsed.getRoutes().isEmpty());
|
||||
assertTrue(parsed.getPersistentVolumeClaims().isEmpty());
|
||||
assertEquals(parsed.getWarnings().size(), 1);
|
||||
assertEquals(
|
||||
parsed.getWarnings().get(0),
|
||||
|
|
@ -152,13 +156,28 @@ public class OpenShiftEnvironmentFactoryTest {
|
|||
final OpenShiftEnvironment parsed =
|
||||
osEnvironmentFactory.doCreate(internalRecipe, emptyMap(), emptyList());
|
||||
|
||||
assertTrue(parsed.getRoutes().isEmpty());
|
||||
assertTrue(parsed.getSecrets().isEmpty());
|
||||
assertEquals(parsed.getWarnings().size(), 1);
|
||||
assertEquals(
|
||||
parsed.getWarnings().get(0),
|
||||
new WarningImpl(SECRET_IGNORED_WARNING_CODE, SECRET_IGNORED_WARNING_MESSAGE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ignoreConfigMapsWhenRecipeContainsThem() throws Exception {
|
||||
final List<HasMetadata> recipeObjects = singletonList(new ConfigMap());
|
||||
when(validatedObjects.getItems()).thenReturn(recipeObjects);
|
||||
|
||||
final KubernetesEnvironment parsed =
|
||||
osEnvironmentFactory.doCreate(internalRecipe, emptyMap(), emptyList());
|
||||
|
||||
assertTrue(parsed.getConfigMaps().isEmpty());
|
||||
assertEquals(parsed.getWarnings().size(), 1);
|
||||
assertEquals(
|
||||
parsed.getWarnings().get(0),
|
||||
new WarningImpl(CONFIG_MAP_IGNORED_WARNING_CODE, CONFIG_MAP_IGNORED_WARNING_MESSAGE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetsRamLimitAttributeFromOpenShiftResource() throws Exception {
|
||||
final long firstMachineRamLimit = 3072 * BYTES_IN_MB;
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import io.fabric8.openshift.api.model.ProjectRequestFluent.MetadataNested;
|
|||
import io.fabric8.openshift.client.OpenShiftClient;
|
||||
import io.fabric8.openshift.client.dsl.ProjectRequestOperation;
|
||||
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesConfigsMaps;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesDeployments;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesIngresses;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesPersistentVolumeClaims;
|
||||
|
|
@ -61,6 +62,7 @@ public class OpenShiftProjectTest {
|
|||
@Mock private KubernetesPersistentVolumeClaims pvcs;
|
||||
@Mock private KubernetesIngresses ingresses;
|
||||
@Mock private KubernetesSecrets secrets;
|
||||
@Mock private KubernetesConfigsMaps configsMaps;
|
||||
@Mock private OpenShiftClientFactory clientFactory;
|
||||
@Mock private OpenShiftClient openShiftClient;
|
||||
@Mock private KubernetesClient kubernetesClient;
|
||||
|
|
@ -93,7 +95,8 @@ public class OpenShiftProjectTest {
|
|||
routes,
|
||||
pvcs,
|
||||
ingresses,
|
||||
secrets);
|
||||
secrets,
|
||||
configsMaps);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -133,6 +136,7 @@ public class OpenShiftProjectTest {
|
|||
verify(services).delete();
|
||||
verify(deployments).delete();
|
||||
verify(secrets).delete();
|
||||
verify(configsMaps).delete();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -12,12 +12,18 @@
|
|||
"wsagent/http": {
|
||||
"port": "4401/tcp",
|
||||
"protocol": "http",
|
||||
"path" : "/api"
|
||||
"path" : "/api",
|
||||
"attributes": {
|
||||
"secure": "true"
|
||||
}
|
||||
},
|
||||
"wsagent/ws": {
|
||||
"port": "4401/tcp",
|
||||
"protocol": "ws",
|
||||
"path" : "/wsagent"
|
||||
"path" : "/wsagent",
|
||||
"attributes": {
|
||||
"secure": "true"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,12 +12,18 @@
|
|||
"wsagent/http": {
|
||||
"port": "4401/tcp",
|
||||
"protocol": "http",
|
||||
"path" : "/api"
|
||||
"path" : "/api",
|
||||
"attributes": {
|
||||
"secure": "true"
|
||||
}
|
||||
},
|
||||
"wsagent/ws": {
|
||||
"port": "4401/tcp",
|
||||
"protocol": "ws",
|
||||
"path" : "/wsagent"
|
||||
"path" : "/wsagent",
|
||||
"attributes": {
|
||||
"secure": "true"
|
||||
}
|
||||
},
|
||||
"wsagent-debug": {
|
||||
"port": "4403/tcp",
|
||||
|
|
|
|||
|
|
@ -12,12 +12,18 @@
|
|||
"wsagent/http": {
|
||||
"port": "4401/tcp",
|
||||
"protocol": "http",
|
||||
"path" : "/api"
|
||||
"path" : "/api",
|
||||
"attributes": {
|
||||
"secure": "true"
|
||||
}
|
||||
},
|
||||
"wsagent/ws": {
|
||||
"port": "4401/tcp",
|
||||
"protocol": "ws",
|
||||
"path" : "/wsagent"
|
||||
"path" : "/wsagent",
|
||||
"attributes": {
|
||||
"secure": "true"
|
||||
}
|
||||
},
|
||||
"wsagent-debug": {
|
||||
"port": "4403/tcp",
|
||||
|
|
|
|||
|
|
@ -9,12 +9,18 @@
|
|||
"wsagent/http": {
|
||||
"port": "4401/tcp",
|
||||
"protocol": "http",
|
||||
"path" : "/api"
|
||||
"path" : "/api",
|
||||
"attributes": {
|
||||
"secure": "true"
|
||||
}
|
||||
},
|
||||
"wsagent/ws": {
|
||||
"port": "4401/tcp",
|
||||
"protocol": "ws",
|
||||
"path" : "/wsagent"
|
||||
"path" : "/wsagent",
|
||||
"attributes": {
|
||||
"secure": "true"
|
||||
}
|
||||
},
|
||||
"wsagent-debug": {
|
||||
"port": "4403/tcp",
|
||||
|
|
|
|||
Loading…
Reference in New Issue