Skip to content

Commit

Permalink
Make Kubernetes infrastructure propagate TLS secret if configured
Browse files Browse the repository at this point in the history
Signed-off-by: Sergii Leshchenko <sleshche@redhat.com>
  • Loading branch information
sleshchenko committed Jul 25, 2019
1 parent 4e3e16a commit 760e882
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,12 @@ che.infra.kubernetes.tls_enabled=false
# Ignored by OpenShift infrastructure
che.infra.kubernetes.tls_secret=

# Data for TLS Secret that should be used for workspaces Ingresses
# cert and key should be encoded with Base64 algorithm
# This properties are ignored by OpenShift infrastructure
che.infra.kubernetes.tls_key=NULL
che.infra.kubernetes.tls_cert=NULL

# Defines the period with which runtimes consistency checks will be performed.
# If runtime has inconsistent state then runtime will be stopped automatically.
# Value must be more than 0 or `-1`, where `-1` means that checks won't be performed at all.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,23 @@

import static com.google.common.base.Strings.isNullOrEmpty;

import io.fabric8.kubernetes.api.model.Secret;
import io.fabric8.kubernetes.api.model.SecretBuilder;
import io.fabric8.kubernetes.api.model.extensions.Ingress;
import io.fabric8.kubernetes.api.model.extensions.IngressTLS;
import io.fabric8.kubernetes.api.model.extensions.IngressTLSBuilder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Named;
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
import org.eclipse.che.api.workspace.server.model.impl.ServerConfigImpl;
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.inject.ConfigurationException;
import org.eclipse.che.workspace.infrastructure.kubernetes.Annotations;
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesInfrastructureException;
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
Expand All @@ -35,16 +40,28 @@
* @author Guy Daich
*/
public class IngressTlsProvisioner implements ConfigurationProvisioner<KubernetesEnvironment> {
static final String TLS_SECRET_TYPE = "kubernetes.io/tls";

private final boolean isTlsEnabled;
private final String tlsSecretName;
private final String tlsCert;
private final String tlsKey;

@Inject
public IngressTlsProvisioner(
@Named("che.infra.kubernetes.tls_enabled") boolean isTlsEnabled,
@Named("che.infra.kubernetes.tls_secret") String tlsSecretName) {
@Named("che.infra.kubernetes.tls_secret") String tlsSecretName,
@Nullable @Named("che.infra.kubernetes.tls_cert") String tlsCert,
@Nullable @Named("che.infra.kubernetes.tls_key") String tlsKey) {
this.isTlsEnabled = isTlsEnabled;
this.tlsSecretName = tlsSecretName;

if (isNullOrEmpty(tlsCert) != isNullOrEmpty(tlsKey)) {
throw new ConfigurationException("None or both of `che.infra.kubernetes.tls_cert` and "
+ "`che.infra.kubernetes.tls_key` must be configured with non-null value.");
}
this.tlsCert = tlsCert;
this.tlsKey = tlsKey;
}

@Override
Expand All @@ -54,13 +71,32 @@ public void provision(KubernetesEnvironment k8sEnv, RuntimeIdentity identity)
return;
}

if (!isNullOrEmpty(tlsCert) && !isNullOrEmpty(tlsKey)) {
provisionTlsSecret(k8sEnv);
}

Collection<Ingress> ingresses = k8sEnv.getIngresses().values();
for (Ingress ingress : ingresses) {
useSecureProtocolForServers(ingress);
enableTLS(ingress);
}
}

private void provisionTlsSecret(KubernetesEnvironment k8sEnv) {
Map<String, String> data = new HashMap<>();
data.put("tls.crt", tlsCert);
data.put("tls.key", tlsKey);

Secret tlsSecret = new SecretBuilder().withNewMetadata()
.withName(tlsSecretName)
.endMetadata()
.withStringData(data)
.withType(TLS_SECRET_TYPE)
.build();

k8sEnv.getSecrets().put(tlsSecretName, tlsSecret);
}

private void enableTLS(Ingress ingress) {
String host = ingress.getSpec().getRules().get(0).getHost();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,22 @@

import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonMap;
import static org.eclipse.che.workspace.infrastructure.kubernetes.provision.IngressTlsProvisioner.TLS_SECRET_TYPE;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;

import com.google.common.collect.ImmutableMap;
import io.fabric8.kubernetes.api.model.Secret;
import io.fabric8.kubernetes.api.model.extensions.Ingress;
import java.util.HashMap;
import java.util.Map;
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.model.impl.ServerConfigImpl;
import org.eclipse.che.inject.ConfigurationException;
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.ExternalServerIngressBuilder;
Expand Down Expand Up @@ -76,7 +80,7 @@ public class IngressTlsProvisionerTest {
@Test
public void doNothingWhenTlsDisabled() throws Exception {
// given
IngressTlsProvisioner ingressTlsProvisioner = new IngressTlsProvisioner(false, "");
IngressTlsProvisioner ingressTlsProvisioner = new IngressTlsProvisioner(false, "", "", "");

// when
ingressTlsProvisioner.provision(k8sEnv, runtimeIdentity);
Expand All @@ -86,9 +90,9 @@ public void doNothingWhenTlsDisabled() throws Exception {
}

@Test
public void provisionTlsForRoutesWhenTlsEnabledAndSecretProvided() throws Exception {
public void provisionTlsForIngressesWhenTlsEnabledAndSecretProvided() throws Exception {
// given
IngressTlsProvisioner ingressTlsProvisioner = new IngressTlsProvisioner(true, "secretname");
IngressTlsProvisioner ingressTlsProvisioner = new IngressTlsProvisioner(true, "secretname", "", "");

Map<String, Ingress> ingresses = new HashMap<>();
ingresses.put("ingress", ingress);
Expand All @@ -107,9 +111,9 @@ public void provisionTlsForRoutesWhenTlsEnabledAndSecretProvided() throws Except
}

@Test
public void provisionTlsForRoutesWhenTlsEnabledAndSecretEmpty() throws Exception {
public void provisionTlsForIngressesWhenTlsEnabledAndSecretEmpty() throws Exception {
// given
IngressTlsProvisioner ingressTlsProvisioner = new IngressTlsProvisioner(true, "");
IngressTlsProvisioner ingressTlsProvisioner = new IngressTlsProvisioner(true, "", "", "");

Map<String, Ingress> ingresses = new HashMap<>();
ingresses.put("ingress", ingress);
Expand All @@ -128,9 +132,9 @@ public void provisionTlsForRoutesWhenTlsEnabledAndSecretEmpty() throws Exception
}

@Test
public void provisionTlsForRoutesWhenTlsEnabledAndSecretNull() throws Exception {
public void provisionTlsForIngressesWhenTlsEnabledAndSecretNull() throws Exception {
// given
IngressTlsProvisioner ingressTlsProvisioner = new IngressTlsProvisioner(true, null);
IngressTlsProvisioner ingressTlsProvisioner = new IngressTlsProvisioner(true, null, "", "");

Map<String, Ingress> ingresses = new HashMap<>();
ingresses.put("ingress", ingress);
Expand All @@ -148,6 +152,36 @@ public void provisionTlsForRoutesWhenTlsEnabledAndSecretNull() throws Exception
verifyIngressAndServersTLS();
}

@Test
public void provisionTlsSecretWhenTlsCertAndKeyAreSpecified() throws Exception {
// given
IngressTlsProvisioner ingressTlsProvisioner = new IngressTlsProvisioner(true, "ws-tls-secret", "cert", "key");

Map<String, Secret> secrets = new HashMap<>();
when(k8sEnv.getSecrets()).thenReturn(secrets);

// when
ingressTlsProvisioner.provision(k8sEnv, runtimeIdentity);

// then
assertEquals(k8sEnv.getSecrets().size(), 1);
Secret tlsSecret = k8sEnv.getSecrets().get("ws-tls-secret");
assertNotNull(tlsSecret);
assertEquals(tlsSecret.getStringData().get("tls.crt"), "cert");
assertEquals(tlsSecret.getStringData().get("tls.key"), "key");

assertEquals(tlsSecret.getType(), TLS_SECRET_TYPE);
verifyIngressAndServersTLS();
}

@Test(expectedExceptions = ConfigurationException.class,
expectedExceptionsMessageRegExp = "None or both of `che.infra.kubernetes.tls_cert` and "
+ "`che.infra.kubernetes.tls_key` must be configured with non-null value\\.")
public void shouldThrowAnExceptionIfOnlyOneIfTlsCertOrKeyIsConfigured() {
// given
new IngressTlsProvisioner(true, "secret", "test", "");
}

private void verifyIngressAndServersTLS() {
Map<String, ServerConfigImpl> ingressServers =
Annotations.newDeserializer(ingress.getMetadata().getAnnotations()).servers();
Expand Down

0 comments on commit 760e882

Please sign in to comment.