diff --git a/pom.xml b/pom.xml
index 3694c40f..87c83cef 100644
--- a/pom.xml
+++ b/pom.xml
@@ -42,6 +42,7 @@
UTF-8
+ 8.2.0
@@ -196,6 +197,16 @@
archetype-packaging
2.4
+
+ io.github.hakky54
+ sslcontext-kickstart
+ ${ssl-kickstart.version}
+
+
+ io.github.hakky54
+ sslcontext-kickstart-for-pem
+ ${ssl-kickstart.version}
+
diff --git a/xrootd4j-standalone/pom.xml b/xrootd4j-standalone/pom.xml
index f9e27353..5424dc57 100644
--- a/xrootd4j-standalone/pom.xml
+++ b/xrootd4j-standalone/pom.xml
@@ -60,6 +60,16 @@
org.dcache
xrootd4j
+
+ io.github.hakky54
+ sslcontext-kickstart
+
+
+
+ io.github.hakky54
+ sslcontext-kickstart-for-pem
+
+
diff --git a/xrootd4j-standalone/src/main/java/org/dcache/xrootd/standalone/DataServerChannelInitializer.java b/xrootd4j-standalone/src/main/java/org/dcache/xrootd/standalone/DataServerChannelInitializer.java
index b08bc045..71afcddf 100644
--- a/xrootd4j-standalone/src/main/java/org/dcache/xrootd/standalone/DataServerChannelInitializer.java
+++ b/xrootd4j-standalone/src/main/java/org/dcache/xrootd/standalone/DataServerChannelInitializer.java
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2011-2023 dCache.org
+ * Copyright (C) 2011-2024 dCache.org
*
* This file is part of xrootd4j.
*
@@ -23,12 +23,16 @@
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.logging.LoggingHandler;
+import io.netty.handler.ssl.SslContext;
+import io.netty.handler.ssl.SslContextBuilder;
+import nl.altindag.ssl.pem.util.PemUtils;
import org.dcache.xrootd.core.XrootdAuthenticationHandler;
import org.dcache.xrootd.core.XrootdDecoder;
import org.dcache.xrootd.core.XrootdEncoder;
import org.dcache.xrootd.core.XrootdHandshakeHandler;
import org.dcache.xrootd.core.XrootdSessionHandler;
import org.dcache.xrootd.plugins.ChannelHandlerFactory;
+import org.dcache.xrootd.plugins.tls.SSLHandlerFactory;
import org.dcache.xrootd.security.SigningPolicy;
import org.dcache.xrootd.security.TLSSessionInfo;
import org.dcache.xrootd.stream.ChunkedResponseWriteHandler;
@@ -36,6 +40,12 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.X509ExtendedKeyManager;
+import java.nio.file.Paths;
+import java.util.Properties;
+import java.util.function.Supplier;
+
public class DataServerChannelInitializer extends ChannelInitializer {
private static final Logger logger = LoggerFactory.getLogger(
@@ -60,9 +70,25 @@ protected void initChannel(SocketChannel ch) throws Exception {
/*
* Placeholders, no Sigver and no TLS support yet.
*/
+
SigningPolicy signingPolicy = new SigningPolicy();
ServerProtocolFlags flags = new ServerProtocolFlags(0);
+
+ SSLHandlerFactory tlsFactory = null;
+ if (_options.withTls) {
+ tlsFactory = new LocalPemTlsHandler(_options.hostCert, _options.hostKey);
+ tlsFactory.initialize(new Properties(), true);
+
+ flags.setMode(ServerProtocolFlags.TlsMode.OPTIONAL);
+ flags.setRequiresTLSForSession(true);
+ flags.setRequiresTLSForLogin(true);
+ flags.setRequiresTLSForData(true);
+ flags.setSupportsTLS(true);
+ }
+
+
TLSSessionInfo tlsSessionInfo = new TLSSessionInfo(flags);
+ tlsSessionInfo.setServerSslHandlerFactory(tlsFactory);
XrootdSessionHandler sessionHandler = new XrootdSessionHandler();
/*
@@ -95,4 +121,35 @@ protected void initChannel(SocketChannel ch) throws Exception {
signingPolicy);
pipeline.addLast("data-server", dataServerHandler);
}
+
+ private static class LocalPemTlsHandler extends SSLHandlerFactory {
+
+ /**
+ * Netty SSL context for the TLS handler.
+ */
+ private final SslContext sslContext;
+
+ /**
+ * Create a new instance of the TLS handler.
+ * @param hostcert Path to host certificate file.
+ * @param hostkey Path to host key file.
+ * @throws SSLException
+ */
+ LocalPemTlsHandler(String hostcert, String hostkey) throws SSLException {
+ X509ExtendedKeyManager keyManager =
+ PemUtils.loadIdentityMaterial(Paths.get(hostcert), Paths.get(hostkey));
+ sslContext = SslContextBuilder.forServer(keyManager).startTls(true).build();
+ }
+
+ @Override
+ protected Supplier buildContextSupplier(Properties properties) {
+ return () -> sslContext;
+ }
+
+ @Override
+ public String getName() {
+ return SERVER_TLS;
+ }
+ }
+
}
diff --git a/xrootd4j-standalone/src/main/java/org/dcache/xrootd/standalone/DataServerConfiguration.java b/xrootd4j-standalone/src/main/java/org/dcache/xrootd/standalone/DataServerConfiguration.java
index fdfb8db6..63ffbf80 100644
--- a/xrootd4j-standalone/src/main/java/org/dcache/xrootd/standalone/DataServerConfiguration.java
+++ b/xrootd4j-standalone/src/main/java/org/dcache/xrootd/standalone/DataServerConfiguration.java
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2011-2023 dCache.org
+ * Copyright (C) 2011-2024 dCache.org
*
* This file is part of xrootd4j.
*
@@ -58,6 +58,10 @@ public class DataServerConfiguration {
public final List channelHandlerPlugins;
public final boolean useZeroCopy;
+ public final boolean withTls;
+ public final String hostCert;
+ public final String hostKey;
+
public final List channelHandlerFactories;
public DataServerConfiguration(DataServerOptionParser parser, OptionSet options)
@@ -67,6 +71,9 @@ public DataServerConfiguration(DataServerOptionParser parser, OptionSet options)
pluginPath = options.valuesOf(parser.pluginPath);
channelHandlerPlugins = options.valuesOf(parser.handlerPlugins);
useZeroCopy = options.has(parser.zeroCopy);
+ withTls = options.has(parser.withTls);
+ hostCert = options.valueOf(parser.hostCert);
+ hostKey = options.valueOf(parser.hostKey);
_pluginDefaults = loadDefaultProperties(pluginPath);
diff --git a/xrootd4j-standalone/src/main/java/org/dcache/xrootd/standalone/DataServerOptionParser.java b/xrootd4j-standalone/src/main/java/org/dcache/xrootd/standalone/DataServerOptionParser.java
index 4f870d96..f113c90f 100644
--- a/xrootd4j-standalone/src/main/java/org/dcache/xrootd/standalone/DataServerOptionParser.java
+++ b/xrootd4j-standalone/src/main/java/org/dcache/xrootd/standalone/DataServerOptionParser.java
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2011-2023 dCache.org
+ * Copyright (C) 2011-2024 dCache.org
*
* This file is part of xrootd4j.
*
@@ -30,6 +30,9 @@ public class DataServerOptionParser extends OptionParser {
public final OptionSpec handlerPlugins;
public final OptionSpec pluginPath;
public final OptionSpec zeroCopy;
+ public final OptionSpec withTls;
+ public final OptionSpec hostCert;
+ public final OptionSpec hostKey;
{
port = acceptsAll(asList("p", "port"))
@@ -55,5 +58,16 @@ public class DataServerOptionParser extends OptionParser {
.describedAs("url")
.ofType(File.class);
zeroCopy = acceptsAll(asList("z", "zerocopy"), "Use zero copy reads");
+ withTls = acceptsAll(asList("tls"), "Enable TLS for session and data protection");
+ hostCert = acceptsAll(asList("hostcert"), "Path to the host certificate file")
+ .withRequiredArg()
+ .describedAs("path")
+ .ofType(String.class)
+ .defaultsTo("hostcert.pem");
+ hostKey = acceptsAll(asList("hostkey"), "Path to the host key file")
+ .withRequiredArg()
+ .describedAs("path")
+ .ofType(String.class)
+ .defaultsTo("hostkey.pem");
}
}