Skip to content

Instantly share code, notes, and snippets.

@hazcod
Created September 24, 2020 11:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hazcod/1ab0f308e4a9bde057c686123e9dde7d to your computer and use it in GitHub Desktop.
Save hazcod/1ab0f308e4a9bde057c686123e9dde7d to your computer and use it in GitHub Desktop.
package tls.netty;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class MTLSBackendHandler
extends ChannelInboundHandlerAdapter
{
private final Channel inboundChannel;
public MTLSBackendHandler(Channel inboundChannel) {
this.inboundChannel = inboundChannel;
}
@Override
public void channelActive(ChannelHandlerContext ctx) {
ctx.read();
}
@Override
public void channelRead(final ChannelHandlerContext ctx, Object msg) {
inboundChannel.writeAndFlush(msg).addListener((ChannelFutureListener) future -> {
if (future.isSuccess()) {
System.out.println("backendhandler reading from inboundChannel");
ctx.channel().read();
} else {
System.err.println("could not read from inboundChannel on backend handler");
future.channel().close();
}
});
}
@Override
public void channelInactive(ChannelHandlerContext ctx) {
MTLSFrontendHandler.closeOnFlush(inboundChannel);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
System.err.println(cause.toString());
MTLSFrontendHandler.closeOnFlush(ctx.channel());
}
}
package tls.netty;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.ssl.ClientAuth;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLException;
public class MTLSBackendInitializer
extends ChannelInitializer<SocketChannel>
{
private final KeyManager[] sslContext;
private final Channel inboundChannel;
public MTLSBackendInitializer(Channel inboundChannel, KeyManager[] sslContext) {
this.inboundChannel = inboundChannel;
this.sslContext = sslContext;
}
@Override
public void initChannel(SocketChannel ch)
{
try {
SslContext nettySSLContext = SslContextBuilder.forClient()
.clientAuth(ClientAuth.REQUIRE)
.trustManager(InsecureTrustManagerFactory.INSTANCE)
.keyManager(this.sslContext[0])
.protocols("TLSv1.1", "TLSv1.2")
.build();
ch.pipeline().addLast(
nettySSLContext.newHandler(ch.alloc()),
new MTLSBackendHandler(inboundChannel)
);
} catch (SSLException e) {
System.err.println(e);
}
}
}
package tls.netty;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import javax.net.ssl.KeyManager;
public class MTLSFrontendHandler
extends ChannelInboundHandlerAdapter
{
private final String remoteHost;
private final int remotePort;
private KeyManager[] sslContext;
// As we use inboundChannel.eventLoop() when building the Bootstrap this does not need to be volatile as
// the outboundChannel will use the same EventLoop (and therefore Thread) as the inboundChannel.
private Channel outboundChannel;
public MTLSFrontendHandler(String remoteHost, int remotePort, KeyManager[] ctx) {
this.remoteHost = remoteHost;
this.remotePort = remotePort;
this.sslContext = ctx;
}
@Override
public void channelActive(ChannelHandlerContext ctx) {
final Channel inboundChannel = ctx.channel();
// Start the connection attempt.
Bootstrap b = new Bootstrap();
b.group(inboundChannel.eventLoop())
.channel(ctx.channel().getClass())
.handler(new MTLSBackendInitializer(inboundChannel, this.sslContext))
.option(ChannelOption.AUTO_READ, true);
System.out.println("Connecting to " + remoteHost + ":" + remotePort);
ChannelFuture f = b.connect(remoteHost, remotePort);
outboundChannel = f.channel();
f.addListener((ChannelFutureListener) future -> {
if (future.isSuccess()) {
// connection complete start to read first data
System.out.println("connected to " + remoteHost + ":" + remotePort);
inboundChannel.read();
}
else {
// Close the connection if the connection attempt has failed.
System.err.println("connection attempt failed");
inboundChannel.close();
}
});
}
@Override
public void channelRead(final ChannelHandlerContext ctx, Object msg) {
if (outboundChannel.isActive()) {
outboundChannel.writeAndFlush(msg).addListener((ChannelFutureListener) future -> {
if (future.isSuccess()) {
// was able to flush out data, start to read the next chunk
System.out.println("frontend handler reading from outboundChannel");
ctx.channel().read();
}
else {
System.err.println("frontend handler could not read from outboundChannel");
future.channel().close();
}
});
}
}
@Override
public void channelInactive(ChannelHandlerContext ctx) {
if (outboundChannel != null) {
closeOnFlush(outboundChannel);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
System.err.println(cause.toString());
closeOnFlush(ctx.channel());
}
/**
* Closes the specified channel after all queued write requests are flushed.
*/
static void closeOnFlush(Channel ch) {
if (ch.isActive()) {
ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}
}
}
package tls.netty;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import javax.net.ssl.KeyManager;
public class MTLSFrontendInitializer
extends ChannelInitializer<SocketChannel> {
private final String remoteHost;
private final int remotePort;
private KeyManager[] sslContext;
public MTLSFrontendInitializer(String remoteHost, int remotePort, KeyManager[] ctx) {
this.remoteHost = remoteHost;
this.remotePort = remotePort;
this.sslContext = ctx;
}
@Override
public void initChannel(SocketChannel ch) {
ch.pipeline().addLast(
new LoggingHandler(LogLevel.INFO),
new MTLSFrontendHandler(remoteHost, remotePort, sslContext));
}
}
package tls.netty;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import javax.net.ssl.KeyManager;
public class MTLSInstance
implements Runnable
{
private int listenPort;
private String host;
private int port;
private KeyManager[] sslContext;
public MTLSInstance(int p, String h, int rp, KeyManager[] sslContext)
{
this.listenPort = p;
this.host = h;
this.port = rp;
this.sslContext = sslContext;
}
@Override
public void run()
{
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
try {
System.out.println("starting mtls proxy listener on: " + this.listenPort);
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new MTLSFrontendInitializer(this.host, this.port, this.sslContext))
.childOption(ChannelOption.AUTO_READ, false)
.bind(this.listenPort).sync().channel().closeFuture().sync();
} finally {
System.out.println("closing mtls proxy");
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
} catch (InterruptedException e) {
System.err.println(e.toString());
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment