fix(rocketmq): route tunneled traffic through SOCKS5

Closes #5742
This commit is contained in:
t8y2 committed 2026-08-11 03:16:08 +08:00
1 parent 2e9ed477a4
commit 5351fc5066
11 files changed
+883 -26

No files matched your search

@@ -3,6 +3,7 @@ package com.dbx.agent.rocketmq;
import com.google.gson.*;
import org.apache.rocketmq.acl.common.AclClientRPCHook;
import org.apache.rocketmq.acl.common.SessionCredentials;
import org.apache.rocketmq.client.ClientConfig;
import org.apache.rocketmq.client.QueryResult;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
@@ -2623,6 +2624,7 @@ public final class RocketMqAgent {
? new DefaultMQAdminExt(rpcHook, timeoutMs)
: new DefaultMQAdminExt(timeoutMs);
admin.setNamesrvAddr(namesrvAddr(conn));
applySocksProxy(admin, conn);
admin.setAdminExtGroup("_DBX_ROCKETMQ_ADMIN_" + UUID.randomUUID());
admin.setInstanceName("DBX_" + UUID.randomUUID());
admin.start();
@@ -2635,6 +2637,7 @@ public final class RocketMqAgent {
? new DefaultMQProducer("_DBX_ROCKETMQ_PRODUCER", rpcHook)
: new DefaultMQProducer("_DBX_ROCKETMQ_PRODUCER");
nextProducer.setNamesrvAddr(namesrvAddr(conn));
applySocksProxy(nextProducer, conn);
nextProducer.setInstanceName("DBX_" + UUID.randomUUID());
// Follow Advanced query timeout — SDK default sendMsgTimeout is only 3s.
nextProducer.setSendMsgTimeout(operationBudgetMsAsInt(conn));
@@ -2648,6 +2651,7 @@ public final class RocketMqAgent {
? new DefaultLitePullConsumer(rpcHook)
: new DefaultLitePullConsumer();
consumer.setNamesrvAddr(namesrvAddr(conn));
applySocksProxy(consumer, conn);
consumer.setConsumerGroup("_DBX_PEEK_" + UUID.randomUUID());
consumer.setInstanceName("DBX_" + UUID.randomUUID());
consumer.setAutoCommit(false);
@@ -2660,6 +2664,7 @@ public final class RocketMqAgent {
? new DefaultMQPullConsumer(MixAll.TOOLS_CONSUMER_GROUP, rpcHook)
: new DefaultMQPullConsumer(MixAll.TOOLS_CONSUMER_GROUP);
consumer.setNamesrvAddr(namesrvAddr(conn));
applySocksProxy(consumer, conn);
consumer.setInstanceName("DBX_" + UUID.randomUUID());
return consumer;
}
@@ -2684,6 +2689,33 @@ public final class RocketMqAgent {
return addr;
}
static boolean applySocksProxy(ClientConfig client, JsonObject conn) {
if (!conn.has("socks_proxy") || !conn.get("socks_proxy").isJsonObject()) {
return false;
}
JsonObject proxy = conn.getAsJsonObject("socks_proxy");
String host = stringOrEmpty(proxy, "host");
int port = intOrDefault(proxy, "port", 0);
if (host.isBlank() || port <= 0 || port > 65_535) {
throw new IllegalArgumentException("socks_proxy host and port are required");
}
JsonObject route = new JsonObject();
route.addProperty("addr", formatSocketAddress(host, Integer.toString(port)));
String username = stringOrEmpty(proxy, "username");
String password = stringOrEmpty(proxy, "password");
if (!username.isBlank()) {
route.addProperty("username", username);
}
if (!password.isBlank()) {
route.addProperty("password", password);
}
JsonObject routes = new JsonObject();
routes.add("0.0.0.0/0", route);
client.setSocksProxyConfig(GSON.toJson(routes));
return true;
}
private static TopicList fetchTopicList(DefaultMQAdminExt admin, String cluster) throws Exception {
if (cluster != null && !cluster.isBlank()) {
return admin.fetchTopicsByCLuster(cluster);
@@ -3146,6 +3178,9 @@ public final class RocketMqAgent {
if (!explicit.isBlank()) {
return explicit;
}
if (conn.has("socks_proxy") && conn.get("socks_proxy").isJsonObject()) {
return brokerAddr;
}
String brokerHost = parseHostFromSocketAddress(brokerAddr);
String brokerPort = parsePortFromSocketAddress(brokerAddr);
if (brokerPort.isBlank()) {
@@ -23,6 +23,7 @@ import org.apache.rocketmq.remoting.protocol.body.ConsumerConnection;
import org.apache.rocketmq.remoting.protocol.route.QueueData;
import org.apache.rocketmq.remoting.protocol.route.TopicRouteData;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.util.ArrayList;
@@ -71,6 +72,28 @@ class RocketMqAgentTest {
);
}
@Test
void applySocksProxyConfiguresAllRocketMqDestinations() {
JsonObject conn = JsonParser.parseString("""
{
"socks_proxy": {
"host": "127.0.0.1",
"port": 41080,
"username": "proxy-user",
"password": "proxy-secret"
}
}
""").getAsJsonObject();
DefaultMQAdminExt admin = new DefaultMQAdminExt();
assertTrue(RocketMqAgent.applySocksProxy(admin, conn));
JsonObject routes = JsonParser.parseString(admin.getSocksProxyConfig()).getAsJsonObject();
assertEquals("127.0.0.1:41080", routes.getAsJsonObject("0.0.0.0/0").get("addr").getAsString());
assertEquals("proxy-user", routes.getAsJsonObject("0.0.0.0/0").get("username").getAsString());
assertEquals("proxy-secret", routes.getAsJsonObject("0.0.0.0/0").get("password").getAsString());
}
@Test
void isUserTopicFiltersRocketMqSystemTopics() {
assertEquals(false, RocketMqAgent.isUserTopic("RMQ_SYS_TRANS_HALF_TOPIC"));
@@ -139,6 +162,18 @@ class RocketMqAgentTest {
assertEquals("172.32.0.1:10911", RocketMqAgent.remapBrokerAddrForClient("172.32.0.1:10911", conn));
}
@Test
void remapBrokerAddrPreservesAdvertisedAddressWhenSocksProxyIsActive() {
JsonObject conn = JsonParser.parseString("""
{
"namesrv_addr": "172.19.191.166:9876",
"socks_proxy": {"host":"127.0.0.1","port":41080}
}
""").getAsJsonObject();
assertEquals("172.18.0.3:10911", RocketMqAgent.remapBrokerAddrForClient("172.18.0.3:10911", conn));
}
@Test
void isRfc1918PrivateIpv4MatchesOnlyPrivateRanges() {
assertTrue(RocketMqAgent.isRfc1918PrivateIpv4("10.0.0.1"));