Skip to content

ARTEMIS-3915 Support PROXY protocol#5908

Merged
tabish121 merged 1 commit into
apache:mainfrom
jbertram:ARTEMIS-3915
Sep 29, 2025
Merged

ARTEMIS-3915 Support PROXY protocol#5908
tabish121 merged 1 commit into
apache:mainfrom
jbertram:ARTEMIS-3915

Conversation

@jbertram

@jbertram jbertram commented Sep 4, 2025

Copy link
Copy Markdown
Contributor

No description provided.

Comment thread docs/user-manual/proxy-protocol.adoc Outdated
@gemmellr gemmellr marked this pull request as draft September 5, 2025 09:13
Comment thread tests/e2e-tests/pom.xml Outdated
@jbertram jbertram force-pushed the ARTEMIS-3915 branch 6 times, most recently from cd703ee to f58d312 Compare September 6, 2025 02:57
Comment thread docs/user-manual/versions.adoc Outdated
@jbertram jbertram force-pushed the ARTEMIS-3915 branch 10 times, most recently from 68bfce7 to f00076b Compare September 9, 2025 16:34
@jbertram jbertram requested review from brusdev and gemmellr September 9, 2025 16:37
@jbertram

jbertram commented Sep 9, 2025

Copy link
Copy Markdown
Contributor Author

FYI - test-suite is good on this.

@jbertram jbertram marked this pull request as ready for review September 10, 2025 13:14
@gemmellr

Copy link
Copy Markdown
Member

I havent had a chance to look properly at this after all the changes yet...but skimming the tests, I only see spotted positive tests where it was expected to work and did. We should have negative tests for this, verifying connecting doesnt work when proxyProtocolEnabled=true and a connection connects without the header and vice versa fails when proxyProtocolEnabled=false (/defaulted) a connection with the proxy-protocol header comes in.

@jbertram

Copy link
Copy Markdown
Contributor Author

I added negative tests as well in org.apache.activemq.artemis.tests.e2e.proxy_protocol.HAProxyTest:

  • testNonProxiedConnectionToProxyAcceptor
  • testProxiedConnectionToNonProxyAcceptor

Both fail successfully.

Comment thread docs/user-manual/proxy-protocol.adoc Outdated
Comment thread tests/e2e-tests/pom.xml Outdated
@jbertram jbertram force-pushed the ARTEMIS-3915 branch 2 times, most recently from f15cf79 to e570761 Compare September 13, 2025 04:50
@jbertram jbertram marked this pull request as draft September 18, 2025 14:23
@jbertram jbertram force-pushed the ARTEMIS-3915 branch 3 times, most recently from 52cc708 to 4ba54cd Compare September 23, 2025 21:33
@jbertram jbertram marked this pull request as ready for review September 23, 2025 21:33
@jbertram jbertram force-pushed the ARTEMIS-3915 branch 2 times, most recently from 1a3cd20 to 3a0f965 Compare September 24, 2025 18:59
@jbertram

Copy link
Copy Markdown
Contributor Author

FWIW, the test-suite is green on this.

@jbertram jbertram force-pushed the ARTEMIS-3915 branch 2 times, most recently from 63c4d35 to dcb3025 Compare September 25, 2025 17:02
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf in = (ByteBuf) msg;
if (in.readableBytes() < 4) {

@tabish121 tabish121 Sep 25, 2025

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've realized there is a bug here now that this is acting as a shared static handler that likely means you would want to revert to using new instances with this being a ByteToMessageDecoder subclass. The problem would arise on both proxy enabled and not enabled cases but likely is more of an issue in the non-proxy enabled case where the needed bytes don't arrive in the first packet leaded to dropping the first bytes sent on the connection. I created a test case to add to HAProxyTest that shows this:


diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/proxyprotocol/HAProxyTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/proxyprotocol/HAProxyTest.java
index f877dea06b..f15878897e 100644
--- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/proxyprotocol/HAProxyTest.java
+++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/proxyprotocol/HAProxyTest.java
@@ -51,6 +51,8 @@ import org.apache.activemq.artemis.tests.integration.jms.multiprotocol.Multiprot
 import org.apache.activemq.artemis.utils.RandomUtil;
 import org.apache.activemq.artemis.utils.Wait;
 import org.apache.activemq.transport.netty.NettyHAProxyServer;
+import org.apache.qpid.protonj2.test.driver.ProtonTestClient;
+import org.apache.qpid.protonj2.test.driver.codec.security.SaslCode;
 import org.eclipse.paho.mqttv5.client.IMqttToken;
 import org.eclipse.paho.mqttv5.client.MqttCallback;
 import org.eclipse.paho.mqttv5.client.MqttClient;
@@ -61,6 +63,7 @@ import org.eclipse.paho.mqttv5.common.MqttException;
 import org.eclipse.paho.mqttv5.common.MqttMessage;
 import org.eclipse.paho.mqttv5.common.packet.MqttProperties;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -90,7 +93,7 @@ public class HAProxyTest extends MultiprotocolJMSClientTestSupport {
    protected ActiveMQServer createServer() throws Exception {
       server = createServer(false, createDefaultNettyConfig()
          .clearAcceptorConfigurations()
-         .addAcceptorConfiguration("standard", "tcp://127.0.0.1:" + BROKER_STANDARD_PORT + "?protocols=CORE")
+         .addAcceptorConfiguration("standard", "tcp://127.0.0.1:" + BROKER_STANDARD_PORT + "?protocols=CORE,AMQP")
          .addAcceptorConfiguration("proxyEnabled", "tcp://127.0.0.1:" + BROKER_PROXY_PORT + "?proxyProtocolEnabled=true")
          .addAcceptorConfiguration("proxyAndSslEnabled", "tcp://127.0.0.1:" + BROKER_PROXY_SSL_PORT + "?proxyProtocolEnabled=true;sslEnabled=true;protocols=CORE,AMQP,MQTT,OPENWIRE;supportAdvisory=false;suppressInternalManagementObjects=true;keyStorePath=server-keystore.jks;keyStorePassword=securepass"));
 
@@ -331,6 +334,37 @@ public class HAProxyTest extends MultiprotocolJMSClientTestSupport {
       producer.disconnect();
    }
 
+   @Test
+   @Timeout(30)
+   public void testBrokerHandlesOutOfOrderDeliveryIdInTransfer() throws Exception {
+      try (ProtonTestClient receivingPeer = new ProtonTestClient()) {
+         receivingPeer.connect("localhost", BROKER_STANDARD_PORT);
+         receivingPeer.waitForScriptToComplete(5, TimeUnit.SECONDS);
+
+         receivingPeer.expectSASLHeader();
+         receivingPeer.expectSaslMechanisms().withSaslServerMechanism("ANONYMOUS");
+         receivingPeer.remoteSaslInit().withMechanism("ANONYMOUS").queue();
+         receivingPeer.expectSaslOutcome().withCode(SaslCode.OK);
+         receivingPeer.remoteAMQPHeader().queue();
+         receivingPeer.expectAMQPHeader();
+
+         receivingPeer.remoteBytes().withBytes(new byte[] {'A', 'M'}).now();
+         receivingPeer.remoteBytes().withBytes(new byte[] {'Q', 'P', 3, 1, 0, 0}).later(10);
+
+         receivingPeer.waitForScriptToComplete(5, TimeUnit.SECONDS);
+
+         // Broker response after SASL anonymous connect
+         receivingPeer.expectOpen();
+         receivingPeer.expectBegin();
+
+         // Create basic connection with session
+         receivingPeer.remoteOpen().withContainerId("test-sender").now();
+         receivingPeer.remoteBegin().withNextOutgoingId(100).now();
+
+         receivingPeer.waitForScriptToComplete();
+      }
+   }
+
    private int startV1Proxy() {
       return startProxy(HAProxyProtocolVersion.V1, BROKER_PROXY_PORT);
    }

The AMQP Header arrives in fragments which leads the initial bit to get dropped leading to the broker never completing the open phase of the AMQP connection.

This causes me to ask why the enforcer is needed in the negative case and if there's some other simple changes in the protocol handler that could negate that need as it would be nice to not add more overhead to connections that never expect to have a proxy header as I'd assume is how most folks operate. I'd guess in the majority of cases the connection establishment would fail if the proxy message arrives at the front of the connection request.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything will still work without the enforcer. I mainly added it to fail fast based on this feedback. The tests just take longer to run without fa fail-fast mechanism.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check out the latest push. I added your test, and I removed the enforcer object for the normal (i.e. proxyProtocolEnabled=false) case although I still check in the ProtocolDecoder so we can fail fast.


@Test
@Timeout(30)
public void testBrokerHandlesOutOfOrderDeliveryIdInTransfer() throws Exception {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test name is not right for what its doing, I should have renamed it when I commented about it, maybe

testBrokerHandlesSplitAMQPHeaderBytesDuringConnectWithNoProxyHeader

I started from an existing test and forgot to change the name

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't even look at the name. 😆

Fixed now with your suggestion.

@jbertram jbertram force-pushed the ARTEMIS-3915 branch 2 times, most recently from fc82521 to 1a89cd6 Compare September 29, 2025 21:09
This commit implements support the PROXY protocol so that the broker
will be able to determine a client's original IP address despite the
client's connection coming through a reverse proxy (e.g. HAProxy,
ngingx, etc.). Changes include:

 - A new Netty handler to extract relevant details out of PROXY Protocol
   messages and make them available to the broker
 - A new Netty handler to enforce the acceptor's PROXY Protocol config
 - A new chapter in the user manual
 - Updated logging to use this new data
 - Expose this new data via management
 - Disambiguate some variables names related to SOCKS proxy support
 - Embedded Netty-based PROXY server implementation for testing
@tabish121 tabish121 merged commit e1e79f6 into apache:main Sep 29, 2025
6 checks passed
@jbertram jbertram deleted the ARTEMIS-3915 branch May 27, 2026 19:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants