Skip to content

Commit

Permalink
MULE-8958: Allow insecure HTTPS connections
Browse files Browse the repository at this point in the history
  • Loading branch information
afelisatti committed Nov 23, 2015
1 parent 766e622 commit 1813124
Show file tree
Hide file tree
Showing 17 changed files with 420 additions and 27 deletions.
Expand Up @@ -383,11 +383,17 @@ public String[] getEnabledProtocols()

public SSLContext getSslContext() throws NoSuchAlgorithmException, KeyManagementException
{
KeyManager[] keyManagers =
null == getKeyManagerFactory() ? null : getKeyManagerFactory().getKeyManagers();
TrustManager[] trustManagers =
null == getTrustManagerFactory() ? null : getTrustManagerFactory().getTrustManagers();

return getSslContext(trustManagers);
}

public SSLContext getSslContext(TrustManager[] trustManagers) throws NoSuchAlgorithmException, KeyManagementException
{
KeyManager[] keyManagers =
null == getKeyManagerFactory() ? null : getKeyManagerFactory().getKeyManagers();

SSLContext context = SSLContext.getInstance(getSslType());
// TODO - nice to have a configurable random number source set here
context.init(keyManagers, trustManagers, null);
Expand Down
Expand Up @@ -108,6 +108,7 @@ public void initialise() throws InitialisationException
.setUsePersistentConnections(usePersistentConnections)
.setConnectionIdleTimeout(connectionIdleTimeout)
.setThreadNamePrefix(threadNamePrefix)
.setOwnerName(name)
.build();

httpClient = new GrizzlyHttpClient(configuration);
Expand Down
Expand Up @@ -29,6 +29,7 @@
import org.mule.module.http.internal.request.HttpClient;
import org.mule.module.http.internal.request.NtlmProxyConfig;
import org.mule.transport.ssl.api.TlsContextFactory;
import org.mule.transport.ssl.api.TlsContextTrustStoreConfiguration;
import org.mule.transport.tcp.TcpClientSocketProperties;
import org.mule.util.IOUtils;
import org.mule.util.StringUtils;
Expand Down Expand Up @@ -73,6 +74,7 @@ public class GrizzlyHttpClient implements HttpClient
private boolean usePersistentConnections;
private int connectionIdleTimeout;
private String threadNamePrefix;
private String ownerName;

private AsyncHttpClient asyncHttpClient;
private SSLContext sslContext;
Expand All @@ -86,6 +88,7 @@ public GrizzlyHttpClient(GrizzlyHttpClientConfiguration config)
this.usePersistentConnections = config.isUsePersistentConnections();
this.connectionIdleTimeout = config.getConnectionIdleTimeout();
this.threadNamePrefix = config.getThreadNamePrefix();
this.ownerName = config.getOwnerName();
}

@Override
Expand Down Expand Up @@ -131,7 +134,13 @@ private void configureTlsContext(AsyncHttpClientConfig.Builder builder) throws I
{
builder.setEnabledProtocols(tlsContextFactory.getEnabledProtocols());
}

TlsContextTrustStoreConfiguration trustStoreConfiguration = tlsContextFactory.getTrustStoreConfiguration();
if(trustStoreConfiguration != null && trustStoreConfiguration.isInsecure())
{
logger.warn(String.format("TLS configuration for requester %s has been set to use an insecure trust store. This means no certificate validations will be performed, rendering connections vulnerable to attacks. Use at own risk.", ownerName));
//This disables hostname verification
builder.setAcceptAnyCertificate(true);
}
}
}

Expand Down
Expand Up @@ -20,9 +20,10 @@ public class GrizzlyHttpClientConfiguration
private final boolean usePersistentConnections;
private final int connectionIdleTimeout;
private final String threadNamePrefix;
private final String ownerName;

This comment has been minimized.

Copy link
@pablolagreca

pablolagreca Nov 23, 2015

Contributor

what's the purpose of this attribute?

This comment has been minimized.

Copy link
@afelisatti

afelisatti Nov 24, 2015

Author Contributor

To indicate which requester config owns the HttpClient. That way we can tell which requester has been configured to use an insecure client and alert users.


private GrizzlyHttpClientConfiguration(TlsContextFactory tlsContextFactory, ProxyConfig proxyConfig, TcpClientSocketProperties clientSocketProperties,
int maxConnections, boolean usePersistentConnections, int connectionIdleTimeout, String threadNamePrefix)
int maxConnections, boolean usePersistentConnections, int connectionIdleTimeout, String threadNamePrefix, String ownerName)
{
this.tlsContextFactory = tlsContextFactory;
this.proxyConfig = proxyConfig;
Expand All @@ -31,6 +32,7 @@ private GrizzlyHttpClientConfiguration(TlsContextFactory tlsContextFactory, Prox
this.usePersistentConnections = usePersistentConnections;
this.connectionIdleTimeout = connectionIdleTimeout;
this.threadNamePrefix = threadNamePrefix;
this.ownerName = ownerName;
}

public TlsContextFactory getTlsContextFactory()
Expand Down Expand Up @@ -68,6 +70,11 @@ public String getThreadNamePrefix()
return threadNamePrefix;
}

public String getOwnerName()
{
return ownerName;
}

public static class Builder
{
private TlsContextFactory tlsContextFactory;
Expand All @@ -77,6 +84,7 @@ public static class Builder
private boolean usePersistentConnections;
private int connectionIdleTimeout;
private String threadNamePrefix;
private String ownerName;

public Builder setTlsContextFactory(TlsContextFactory tlsContextFactory)
{
Expand Down Expand Up @@ -120,10 +128,16 @@ public Builder setThreadNamePrefix(String threadNamePrefix)
return this;
}

public Builder setOwnerName(String ownerName)
{
this.ownerName = ownerName;
return this;
}

public GrizzlyHttpClientConfiguration build()
{
return new GrizzlyHttpClientConfiguration(tlsContextFactory, proxyConfig, clientSocketProperties, maxConnections,
usePersistentConnections, connectionIdleTimeout, threadNamePrefix);
usePersistentConnections, connectionIdleTimeout, threadNamePrefix, ownerName);
}
}
}
@@ -0,0 +1,64 @@
/*
* Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
* The software in this package is published under the terms of the CPAL v1.0
* license, a copy of which has been included with this distribution in the
* LICENSE.txt file.
*/
package org.mule.module.http.functional.listener;

import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.isA;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
import static org.junit.internal.matchers.ThrowableMessageMatcher.hasMessage;
import static org.junit.rules.ExpectedException.none;
import org.mule.api.MessagingException;
import org.mule.api.MuleEvent;
import org.mule.construct.Flow;
import org.mule.tck.junit4.FunctionalTestCase;
import org.mule.tck.junit4.rule.DynamicPort;

import java.io.IOException;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

/**
* Sets up two HTTPS servers with regular trust-stores, except one is insecure.
* Verifies that a request using a certificate not present in the trust-store
* only works for the insecure server.
*/
public class HttpListenerTlsInsecureTestCase extends FunctionalTestCase
{
@Rule
public DynamicPort port1 = new DynamicPort("port1");
@Rule
public DynamicPort port2 = new DynamicPort("port2");
@Rule
public ExpectedException expectedException = none();

@Override
protected String getConfigFile()
{
return "http-listener-insecure-config.xml";
}

@Test
public void acceptsInvalidCertificateIfInsecure() throws Exception
{
Flow flow = (Flow) getFlowConstruct("testRequestToInsecure");
final MuleEvent res = flow.process(getTestEvent(TEST_PAYLOAD));
assertThat(res.getMessage().getPayloadAsString(), is(TEST_PAYLOAD));
}

@Test
public void rejectsInvalidCertificateIfSecure() throws Exception
{
Flow flow = (Flow) getFlowConstruct("testRequestToSecure");
expectedException.expect(MessagingException.class);
expectedException.expectCause(isA(IOException.class));
expectedException.expectCause(hasMessage(containsString("Remotely close")));
flow.process(getTestEvent("data"));
}
}
@@ -0,0 +1,83 @@
/*
* Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
* The software in this package is published under the terms of the CPAL v1.0
* license, a copy of which has been included with this distribution in the
* LICENSE.txt file.
*/
package org.mule.module.http.functional.requester;

import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.isA;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
import static org.junit.internal.matchers.ThrowableMessageMatcher.hasMessage;
import static org.junit.rules.ExpectedException.none;
import org.mule.api.MessagingException;
import org.mule.api.MuleEvent;
import org.mule.construct.Flow;
import org.mule.tck.junit4.FunctionalTestCase;
import org.mule.tck.junit4.rule.DynamicPort;
import org.mule.tck.junit4.rule.SystemProperty;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

/**
* Sets up two HTTPS clients using a regular trust-store, but one of them insecure.
* Then two HTTPS servers: one will return a certificate present in the trust-store
* but with an invalid SAN extension, the other will return a certificate that's not in the trust-store.
* Verifies that only the insecure client is successful.
*/
@RunWith(Parameterized.class)
public class HttpRequestTlsInsecureTestCase extends FunctionalTestCase
{
@Parameterized.Parameter
public String config;

@Rule
public DynamicPort port1 = new DynamicPort("port1");
@Rule
public SystemProperty insecure = new SystemProperty("insecure", "true");
@Rule
public ExpectedException expectedException = none();

@Override
protected String getConfigFile()
{
return config;
}

@Parameterized.Parameters
public static Collection<Object[]> parameters()
{
return Arrays.asList(new Object[][] {
{"http-request-insecure-hostname-config.xml"},
{"http-request-insecure-certificate-config.xml"}});
}

@Test
public void insecureRequest() throws Exception
{
Flow flow = (Flow) getFlowConstruct("testInsecureRequest");
final MuleEvent res = flow.process(getTestEvent(TEST_PAYLOAD));
assertThat(res.getMessage().getPayloadAsString(), is(TEST_PAYLOAD));
}

@Test
public void secureRequest() throws Exception
{
Flow flow = (Flow) getFlowConstruct("testSecureRequest");
expectedException.expect(MessagingException.class);
expectedException.expectCause(isA(IOException.class));
expectedException.expectCause(hasMessage(containsString("General SSLEngine problem")));
flow.process(getTestEvent(TEST_PAYLOAD));
}

}
51 changes: 51 additions & 0 deletions modules/http/src/test/resources/http-listener-insecure-config.xml
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns="http://www.mulesoft.org/schema/mule/core"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:http="http://www.mulesoft.org/schema/mule/http"
xmlns:tls="http://www.mulesoft.org/schema/mule/tls"
xsi:schemaLocation="
http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
http://www.mulesoft.org/schema/mule/tls http://www.mulesoft.org/schema/mule/tls/current/mule-tls.xsd
http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd">

<tls:context name="globalTlsContext" >
<tls:trust-store path="ssltest-cacerts.jks" password="changeit"/>
<tls:key-store path="serverKeystore" keyPassword="mulepassword" password="mulepassword"/>
</tls:context>

<http:listener-config name="insecureConfig" protocol="HTTPS" host="localhost" port="${port1}">
<tls:context>
<tls:trust-store path="ssltest-cacerts.jks" password="changeit" insecure="true"/>
<tls:key-store path="ssltest-keystore.jks" keyPassword="changeit" password="changeit"/>
</tls:context>
</http:listener-config>

<http:listener-config name="secureConfig" protocol="HTTPS" host="localhost" port="${port2}">
<tls:context>
<tls:trust-store path="ssltest-cacerts.jks" password="changeit" />
<tls:key-store path="ssltest-keystore.jks" keyPassword="changeit" password="changeit"/>
</tls:context>
</http:listener-config>

<http:request-config name="clientGlobalConfig" protocol="HTTPS" host="localhost" port="${port}" tlsContext-ref="globalTlsContext" />

<flow name="testInsecureServer">
<http:listener config-ref="insecureConfig" path="/"/>
<echo-component/>
</flow>

<flow name="testSecureServer">
<http:listener config-ref="secureConfig" path="/"/>
<echo-component/>
</flow>


<flow name="testRequestToInsecure">
<http:request config-ref="clientGlobalConfig" port="${port1}" path="/" method="POST" />
</flow>

<flow name="testRequestToSecure">
<http:request config-ref="clientGlobalConfig" port="${port2}" path="/" method="POST" />
</flow>

</mule>
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns="http://www.mulesoft.org/schema/mule/core"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:http="http://www.mulesoft.org/schema/mule/http"
xmlns:spring="http://www.springframework.org/schema/beans"
xmlns:tls="http://www.mulesoft.org/schema/mule/tls"
xsi:schemaLocation="
http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd
http://www.mulesoft.org/schema/mule/tls http://www.mulesoft.org/schema/mule/tls/current/mule-tls.xsd
http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd">

<tls:context name="globalTlsContext">
<tls:key-store path="serverKeystore" keyPassword="mulepassword" password="mulepassword"/>
</tls:context>

<http:listener-config name="hostnameListener" protocol="HTTPS" host="localhost" port="${port1}" tlsContext-ref="globalTlsContext"/>

<http:request-config name="insecureClient" protocol="HTTPS" host="localhost" port="${port1}">
<tls:context>
<tls:trust-store insecure="true"/>
</tls:context>
</http:request-config>

<http:request-config name="secureClient" protocol="HTTPS" host="localhost" port="${port1}">
<tls:context>
<tls:trust-store path="ssltest-cacerts.jks" password="changeit" insecure="false"/>
</tls:context>
</http:request-config>

<flow name="testFlowNestedContext">
<http:listener config-ref="hostnameListener" path="/" allowedMethods="POST"/>
<echo-component/>
</flow>

<flow name="testInsecureRequest">
<http:request config-ref="insecureClient" path="/" method="POST" />
</flow>

<flow name="testSecureRequest">
<http:request config-ref="secureClient" path="/" method="POST" />
</flow>
</mule>

0 comments on commit 1813124

Please sign in to comment.