This is an automated email from the ASF dual-hosted git repository. btellier pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/james-project.git
The following commit(s) were added to refs/heads/master by this push: new 52bad9af3a JAMES-3788 Allow configuring if Proxy or SSL frames should be handled… (#2634) 52bad9af3a is described below commit 52bad9af3add74332d583632ccc5bca3727c1474 Author: Benoit TELLIER <btell...@linagora.com> AuthorDate: Tue Feb 11 08:43:08 2025 +0100 JAMES-3788 Allow configuring if Proxy or SSL frames should be handled… (#2634) - SSL aware load balancer with an SSL backend would generally establish SSL termination first then handover proxy information, for example secure setup in a third party data center. - Simpler setup in just TCP transparent and (eg HAProxy in TCP mode) and would only append the proxy frames prior to the SSL handshake. We need a mode to distinguish both setups. --- docs/modules/servers/partials/configure/imap.adoc | 5 +++++ docs/modules/servers/partials/configure/smtp.adoc | 5 +++++ .../netty/AbstractSSLAwareChannelPipelineFactory.java | 12 +++++++----- .../java/org/apache/james/protocols/netty/NettyServer.java | 2 ++ .../java/org/apache/james/imapserver/netty/IMAPServer.java | 2 +- .../protocols/lib/netty/AbstractConfigurableAsyncServer.java | 5 ++++- src/site/xdoc/server/config-imap4.xml | 7 ++++++- src/site/xdoc/server/config-smtp-lmtp.xml | 5 +++++ 8 files changed, 35 insertions(+), 8 deletions(-) diff --git a/docs/modules/servers/partials/configure/imap.adoc b/docs/modules/servers/partials/configure/imap.adoc index 0fe943cb8a..b6df670256 100644 --- a/docs/modules/servers/partials/configure/imap.adoc +++ b/docs/modules/servers/partials/configure/imap.adoc @@ -118,6 +118,11 @@ Integer, defaults to 4096, must be positive, 0 means no queue. with other proxies (e.g. traefik). If enabled, it is *required* to initiate the connection using HAProxy's proxy protocol. +| proxyFirst +| Whether proxy frames should be handled before SSL handshakes. This allows setting either the loadbalancer in TCP mode +(so transparent for SSL then Proxy frames needs to be handled first) or set up SSL termination between proxy and server +(more suited for some cloud vendors). Defaults to true (TCP transparent). + | bossWorkerCount | Set the maximum count of boss threads. Boss threads are responsible for accepting incoming IMAP connections and initializing associated resources. Optional integer, by default, boss threads are not used and this responsibility is being dealt with diff --git a/docs/modules/servers/partials/configure/smtp.adoc b/docs/modules/servers/partials/configure/smtp.adoc index d2d8d519c4..1a68a0094f 100644 --- a/docs/modules/servers/partials/configure/smtp.adoc +++ b/docs/modules/servers/partials/configure/smtp.adoc @@ -57,6 +57,11 @@ this case, if nobody is present, the value "localhost" will be used. with other proxies (e.g. traefik). If enabled, it is *required* to initiate the connection using HAProxy's proxy protocol. +| proxyFirst +| Whether proxy frames should be handled before SSL handshakes. This allows setting either the loadbalancer in TCP mode +(so transparent for SSL then Proxy frames needs to be handled first) or set up SSL termination between proxy and server +(more suited for some cloud vendors). Defaults to true (TCP transparent). + | authRequired | (deprecated) use auth.announce instead. diff --git a/protocols/netty/src/main/java/org/apache/james/protocols/netty/AbstractSSLAwareChannelPipelineFactory.java b/protocols/netty/src/main/java/org/apache/james/protocols/netty/AbstractSSLAwareChannelPipelineFactory.java index 7fe9804fdb..0b1c340280 100644 --- a/protocols/netty/src/main/java/org/apache/james/protocols/netty/AbstractSSLAwareChannelPipelineFactory.java +++ b/protocols/netty/src/main/java/org/apache/james/protocols/netty/AbstractSSLAwareChannelPipelineFactory.java @@ -32,21 +32,23 @@ import io.netty.util.concurrent.EventExecutorGroup; @ChannelHandler.Sharable public abstract class AbstractSSLAwareChannelPipelineFactory<C extends SocketChannel> extends AbstractChannelPipelineFactory<C> { private final boolean proxyRequired; + private final boolean proxyFirst; private Supplier<Encryption> secure; public AbstractSSLAwareChannelPipelineFactory(int timeout, int maxConnections, int maxConnectsPerIp, - boolean proxyRequired, + boolean proxyRequired, boolean proxyFirst, ChannelHandlerFactory frameHandlerFactory, EventExecutorGroup eventExecutorGroup) { super(timeout, maxConnections, maxConnectsPerIp, proxyRequired, frameHandlerFactory, eventExecutorGroup); this.proxyRequired = proxyRequired; + this.proxyFirst = proxyFirst; } public AbstractSSLAwareChannelPipelineFactory(int timeout, - int maxConnections, int maxConnectsPerIp, boolean proxyRequired, Supplier<Encryption> secure, - ChannelHandlerFactory frameHandlerFactory, EventExecutorGroup eventExecutorGroup) { - this(timeout, maxConnections, maxConnectsPerIp, proxyRequired, frameHandlerFactory, eventExecutorGroup); + int maxConnections, int maxConnectsPerIp, boolean proxyRequired, boolean proxyFirst, Supplier<Encryption> secure, + ChannelHandlerFactory frameHandlerFactory, EventExecutorGroup eventExecutorGroup) { + this(timeout, maxConnections, maxConnectsPerIp, proxyRequired, proxyFirst, frameHandlerFactory, eventExecutorGroup); this.secure = secure; } @@ -56,7 +58,7 @@ public abstract class AbstractSSLAwareChannelPipelineFactory<C extends SocketCha super.initChannel(channel); if (isSSLSocket()) { - if (proxyRequired) { + if (proxyRequired && proxyFirst) { channel.pipeline().addAfter(HandlerConstants.PROXY_HANDLER, HandlerConstants.SSL_HANDLER, secure.get().sslHandler()); } else { channel.pipeline().addFirst(HandlerConstants.SSL_HANDLER, secure.get().sslHandler()); diff --git a/protocols/netty/src/main/java/org/apache/james/protocols/netty/NettyServer.java b/protocols/netty/src/main/java/org/apache/james/protocols/netty/NettyServer.java index bd7a7d62d8..7e69363d84 100644 --- a/protocols/netty/src/main/java/org/apache/james/protocols/netty/NettyServer.java +++ b/protocols/netty/src/main/java/org/apache/james/protocols/netty/NettyServer.java @@ -123,11 +123,13 @@ public class NettyServer extends AbstractAsyncServer { @Override protected AbstractChannelPipelineFactory createPipelineFactory() { + boolean proxyFirst = true; return new AbstractSSLAwareChannelPipelineFactory( getTimeout(), maxCurConnections, maxCurConnectionsPerIP, proxyRequired, + proxyFirst, () -> secure, getFrameHandlerFactory(), new DefaultEventLoopGroup(16)) { diff --git a/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/IMAPServer.java b/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/IMAPServer.java index 9b0af883a3..c76b826d75 100644 --- a/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/IMAPServer.java +++ b/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/IMAPServer.java @@ -321,7 +321,7 @@ public class IMAPServer extends AbstractConfigurableAsyncServer implements ImapC Encryption secure = getEncryption(); if (secure != null && !secure.isStartTLS()) { - if (proxyRequired) { + if (proxyRequired && proxyFirst) { channel.pipeline().addAfter("proxyInformationHandler", SSL_HANDLER, secure.sslHandler()); } else { channel.pipeline().addFirst(SSL_HANDLER, secure.sslHandler()); diff --git a/server/protocols/protocols-library/src/main/java/org/apache/james/protocols/lib/netty/AbstractConfigurableAsyncServer.java b/server/protocols/protocols-library/src/main/java/org/apache/james/protocols/lib/netty/AbstractConfigurableAsyncServer.java index 1be20371d1..8508006ef9 100644 --- a/server/protocols/protocols-library/src/main/java/org/apache/james/protocols/lib/netty/AbstractConfigurableAsyncServer.java +++ b/server/protocols/protocols-library/src/main/java/org/apache/james/protocols/lib/netty/AbstractConfigurableAsyncServer.java @@ -90,6 +90,7 @@ public abstract class AbstractConfigurableAsyncServer /** The name of the parameter defining the service hello name. */ public static final String PROXY_REQUIRED = "proxyRequired"; + public static final String PROXY_FIRST = "proxyFirst"; public static final int DEFAULT_MAX_EXECUTOR_COUNT = 16; @@ -98,6 +99,7 @@ public abstract class AbstractConfigurableAsyncServer private boolean enabled; protected boolean proxyRequired; + protected boolean proxyFirst; protected int connPerIP; @@ -251,6 +253,7 @@ public abstract class AbstractConfigurableAsyncServer Optional.ofNullable(config.getBoolean("useEpoll", null)).ifPresent(this::setUseEpoll); proxyRequired = config.getBoolean(PROXY_REQUIRED, false); + proxyFirst = config.getBoolean(PROXY_FIRST, false); doConfigure(config); @@ -488,7 +491,7 @@ public abstract class AbstractConfigurableAsyncServer @Override protected AbstractChannelPipelineFactory createPipelineFactory() { return new AbstractSSLAwareChannelPipelineFactory<>(getTimeout(), connectionLimit, connPerIP, - proxyRequired, + proxyRequired, proxyFirst, this::getEncryption, getFrameHandlerFactory(), getExecutorGroup()) { @Override diff --git a/src/site/xdoc/server/config-imap4.xml b/src/site/xdoc/server/config-imap4.xml index b935701f46..f1ee2717b2 100644 --- a/src/site/xdoc/server/config-imap4.xml +++ b/src/site/xdoc/server/config-imap4.xml @@ -86,13 +86,18 @@ <dt><strong>maxQueueSize</strong></dt> <dd>Upper bound to the IMAP throttler queue. Upon burst, requests that cannot be queued are rejected and not executed. Integer, defaults to 4096, must be positive, 0 means no queue.</dd> - <dt><strong>handler.proxyRequired</strong></dt> + <dt><strong>proxyRequired</strong></dt> <dd> Enables proxy support for this service for incoming connections. HAProxy's protocol (https://www.haproxy.org/download/2.7/doc/proxy-protocol.txt) is used and might be compatible with other proxies (e.g. traefik). If enabled, it is *required* to initiate the connection using HAProxy's proxy protocol. </dd> + <dt><strong>proxyFirst</strong></dt> + <dd>Whether proxy frames should be handled before SSL handshakes. This allows setting either the loadbalancer in TCP mode + (so transparent for SSL then Proxy frames needs to be handled first) or set up SSL termination between proxy and server + (more suited for some cloud vendors). Defaults to true (TCP transparent). + </dd> <dt><strong>handler.handlerchain</strong></dt> <dd>This loads the core CommandHandlers. Only remove this if you really know what you are doing</dd> diff --git a/src/site/xdoc/server/config-smtp-lmtp.xml b/src/site/xdoc/server/config-smtp-lmtp.xml index 39575fa106..4ffdd2f049 100644 --- a/src/site/xdoc/server/config-smtp-lmtp.xml +++ b/src/site/xdoc/server/config-smtp-lmtp.xml @@ -81,6 +81,11 @@ with other proxies (e.g. traefik). If enabled, it is *required* to initiate the connection using HAProxy's proxy protocol. </dd> + <dt><strong>proxyFirst</strong></dt> + <dd>Whether proxy frames should be handled before SSL handshakes. This allows setting either the loadbalancer in TCP mode + (so transparent for SSL then Proxy frames needs to be handled first) or set up SSL termination between proxy and server + (more suited for some cloud vendors). Defaults to true (TCP transparent). + </dd> <dt><strong>authRequired</strong></dt> <dd>(deprecated) use auth.announce instead. This is an optional tag with a boolean body. If true, then the server will --------------------------------------------------------------------- To unsubscribe, e-mail: notifications-unsubscr...@james.apache.org For additional commands, e-mail: notifications-h...@james.apache.org