Skip to content

Commit

Permalink
Merge pull request #398 from rabbitmq/rabbitmq-java-client-394-tls-ho…
Browse files Browse the repository at this point in the history
…stname-verification

Add opt-in to enable server hostname verification
  • Loading branch information
michaelklishin authored Aug 24, 2018
2 parents dc3454f + b5079b4 commit 694e520
Show file tree
Hide file tree
Showing 18 changed files with 654 additions and 33 deletions.
52 changes: 45 additions & 7 deletions src/main/java/com/rabbitmq/client/ConnectionFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@

package com.rabbitmq.client;

import static java.util.concurrent.TimeUnit.*;

import com.rabbitmq.client.impl.AMQConnection;
import com.rabbitmq.client.impl.ConnectionParams;
import com.rabbitmq.client.impl.CredentialsProvider;
Expand All @@ -32,6 +30,10 @@
import com.rabbitmq.client.impl.recovery.RetryHandler;
import com.rabbitmq.client.impl.recovery.TopologyRecoveryFilter;

import javax.net.SocketFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
Expand All @@ -50,10 +52,8 @@
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeoutException;
import java.util.function.Predicate;
import javax.net.SocketFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;

import static java.util.concurrent.TimeUnit.MINUTES;

/**
* Convenience factory class to facilitate opening a {@link Connection} to a RabbitMQ node.
Expand Down Expand Up @@ -132,7 +132,7 @@ public class ConnectionFactory implements Cloneable {
// connections uses, see rabbitmq/rabbitmq-java-client#86
private ExecutorService shutdownExecutor;
private ScheduledExecutorService heartbeatExecutor;
private SocketConfigurator socketConf = new DefaultSocketConfigurator();
private SocketConfigurator socketConf = SocketConfigurators.defaultConfigurator();
private ExceptionHandler exceptionHandler = new DefaultExceptionHandler();
private CredentialsProvider credentialsProvider = new DefaultCredentialsProvider(DEFAULT_USER, DEFAULT_PASS);

Expand Down Expand Up @@ -729,6 +729,44 @@ public void useSslProtocol(SSLContext context) {
setSocketFactory(context.getSocketFactory());
}

/**
* Enable server hostname verification for TLS connections.
* <p>
* This enables hostname verification regardless of the IO mode
* used (blocking or non-blocking IO).
* <p>
* This can be called typically after setting the {@link SSLContext}
* with one of the <code>useSslProtocol</code> methods.
*
* @see NioParams#enableHostnameVerification()
* @see NioParams#setSslEngineConfigurator(SslEngineConfigurator)
* @see SslEngineConfigurators#ENABLE_HOSTNAME_VERIFICATION
* @see SocketConfigurators#ENABLE_HOSTNAME_VERIFICATION
* @see ConnectionFactory#useSslProtocol(String)
* @see ConnectionFactory#useSslProtocol(SSLContext)
* @see ConnectionFactory#useSslProtocol()
* @see ConnectionFactory#useSslProtocol(String, TrustManager)
*/
public void enableHostnameVerification() {
enableHostnameVerificationForNio();
enableHostnameVerificationForBlockingIo();
}

protected void enableHostnameVerificationForNio() {
if (this.nioParams == null) {
this.nioParams = new NioParams();
}
this.nioParams = this.nioParams.enableHostnameVerification();
}

protected void enableHostnameVerificationForBlockingIo() {
if (this.socketConf == null) {
this.socketConf = SocketConfigurators.builder().defaultConfigurator().enableHostnameVerification().build();
} else {
this.socketConf = this.socketConf.andThen(SocketConfigurators.enableHostnameVerification());
}
}

public static String computeDefaultTlsProcotol(String[] supportedProtocols) {
if(supportedProtocols != null) {
for (String supportedProtocol : supportedProtocols) {
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/com/rabbitmq/client/SocketChannelConfigurator.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@

import java.io.IOException;
import java.nio.channels.SocketChannel;
import java.util.Objects;

@FunctionalInterface
public interface SocketChannelConfigurator {

/**
Expand All @@ -26,4 +28,18 @@ public interface SocketChannelConfigurator {
*/
void configure(SocketChannel socketChannel) throws IOException;

/**
* Returns a composed configurator that performs, in sequence, this
* operation followed by the {@code after} operation.
*
* @param after the operation to perform after this operation
* @return a composed configurator that performs in sequence this
* operation followed by the {@code after} operation
* @throws NullPointerException if {@code after} is null
*/
default SocketChannelConfigurator andThen(SocketChannelConfigurator after) {
Objects.requireNonNull(after);
return t -> { configure(t); after.configure(t); };
}

}
111 changes: 111 additions & 0 deletions src/main/java/com/rabbitmq/client/SocketChannelConfigurators.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Copyright (c) 2018 Pivotal Software, Inc. All rights reserved.
//
// This software, the RabbitMQ Java client library, is triple-licensed under the
// Mozilla Public License 1.1 ("MPL"), the GNU General Public License version 2
// ("GPL") and the Apache License version 2 ("ASL"). For the MPL, please see
// LICENSE-MPL-RabbitMQ. For the GPL, please see LICENSE-GPL2. For the ASL,
// please see LICENSE-APACHE2.
//
// This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND,
// either express or implied. See the LICENSE file for specific language governing
// rights and limitations of this software.
//
// If you have any questions regarding licensing, please contact us at
// info@rabbitmq.com.

package com.rabbitmq.client;

/**
* Ready-to-use instances and builder for {@link SocketChannelConfigurator}.
* <p>
* Note {@link SocketChannelConfigurator}s can be combined with
* {@link SocketChannelConfigurator#andThen(SocketChannelConfigurator)}.
*
* @since 5.4.0
*/
public abstract class SocketChannelConfigurators {

/**
* Disable Nagle's algorithm.
*/
public static final SocketChannelConfigurator DISABLE_NAGLE_ALGORITHM =
socketChannel -> SocketConfigurators.DISABLE_NAGLE_ALGORITHM.configure(socketChannel.socket());

/**
* Default {@link SocketChannelConfigurator} that disables Nagle's algorithm.
*/
public static final SocketChannelConfigurator DEFAULT = DISABLE_NAGLE_ALGORITHM;

/**
* The default {@link SocketChannelConfigurator} that disables Nagle's algorithm.
*
* @return
*/
public static SocketChannelConfigurator defaultConfigurator() {
return DEFAULT;
}

/**
* {@link SocketChannelConfigurator} that disables Nagle's algorithm.
*
* @return
*/
public static SocketChannelConfigurator disableNagleAlgorithm() {
return DISABLE_NAGLE_ALGORITHM;
}

/**
* Builder to configure and creates a {@link SocketChannelConfigurator} instance.
*
* @return
*/
public static SocketChannelConfigurators.Builder builder() {
return new SocketChannelConfigurators.Builder();
}

public static class Builder {

private SocketChannelConfigurator configurator = channel -> {
};

/**
* Set default configuration.
*
* @return
*/
public Builder defaultConfigurator() {
configurator = configurator.andThen(DEFAULT);
return this;
}

/**
* Disable Nagle's Algorithm.
*
* @return
*/
public Builder disableNagleAlgorithm() {
configurator = configurator.andThen(DISABLE_NAGLE_ALGORITHM);
return this;
}

/**
* Add an extra configuration step.
*
* @param extraConfiguration
* @return
*/
public Builder add(SocketChannelConfigurator extraConfiguration) {
configurator = configurator.andThen(extraConfiguration);
return this;
}

/**
* Return the configured {@link SocketConfigurator}.
*
* @return
*/
public SocketChannelConfigurator build() {
return configurator;
}
}
}
20 changes: 20 additions & 0 deletions src/main/java/com/rabbitmq/client/SocketConfigurator.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,31 @@

import java.io.IOException;
import java.net.Socket;
import java.util.Objects;

@FunctionalInterface
public interface SocketConfigurator {

/**
* Provides a hook to insert custom configuration of the sockets
* used to connect to an AMQP server before they connect.
*/
void configure(Socket socket) throws IOException;

/**
* Returns a composed configurator that performs, in sequence, this
* operation followed by the {@code after} operation.
*
* @param after the operation to perform after this operation
* @return a composed configurator that performs in sequence this
* operation followed by the {@code after} operation
* @throws NullPointerException if {@code after} is null
*/
default SocketConfigurator andThen(SocketConfigurator after) {
Objects.requireNonNull(after);
return t -> {
configure(t);
after.configure(t);
};
}
}
Loading

0 comments on commit 694e520

Please sign in to comment.