[Bug] Broker container never initializes the AuthConfig identity fields, so ACL 2.0 data goes to a relative "null" path and cluster-scoped admin requests become Cluster:null
Maintainers usually reply within 1 day
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 25/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- java
- Domain
- authentication, authorization
Research direction
Compare the three identity-field assignments in broker/src/main/java/org/apache/rocketmq/broker/BrokerStartup.java (around lines 241-243) with the two container entry points, BrokerContainerStartup.java (createAndInitializeBroker) and BrokerContainerProcessor.java (the addBroker path). Check auth/src/main/java/org/apache/rocketmq/auth/config/AuthConfig.java for the fields that stay null. Done means a broker started through the container reports the same configName, clusterName and authConfigPath as standalone, with a regression test. Pull request #11409 is already open against this issue, so check with the author before duplicating the work.
Written by the indexing model from the issue text.
Description
Before Creating the Bug Report
- I found a bug, not just asking a question, which should be created in GitHub Discussions.
- I have searched the GitHub Issues and GitHub Discussions of this repository and believe that this is not a duplicate.
- I have confirmed that this bug belongs to the current repository, not other repositories of RocketMQ.
Runtime platform environment
OS: any (broker container mode, started with mqbrokercontainer, with ACL 2.0 enabled on the inner brokers)
RocketMQ version
branch: develop
Git commit id: a7edeecfb
JDK Version
Compiler: Temurin 1.8.0_504 (target 1.8, same as CI)
Describe the Bug
A standalone broker derives the three identity fields of AuthConfig from the broker configuration it just loaded (broker/src/main/java/org/apache/rocketmq/broker/BrokerStartup.java:241-243):
authConfig.setConfigName(brokerConfig.getBrokerName());
authConfig.setClusterName(brokerConfig.getBrokerClusterName());
authConfig.setAuthConfigPath(messageStoreConfig.getStorePathRootDir() + File.separator + "config");
The proxy does the same for the first two (proxy/src/main/java/org/apache/rocketmq/proxy/config/Configuration.java:50-51). The two places that start a broker inside a broker container never do it: they build the AuthConfig from the per-broker properties and stop there.
container/src/main/java/org/apache/rocketmq/container/BrokerContainerStartup.java:134-140(createAndInitializeBroker)container/src/main/java/org/apache/rocketmq/container/BrokerContainerProcessor.java:120-123(themqadmin addBrokerRPC path)
AuthConfig.configName, AuthConfig.clusterName and AuthConfig.authConfigPath have no defaults (auth/src/main/java/org/apache/rocketmq/auth/config/AuthConfig.java:26-30), so all three stay null. That shows up in three concrete ways:
- The ACL 2.0 user/ACL database is created at a relative path.
LocalAuthenticationMetadataProvider.initializeandLocalAuthorizationMetadataProvider.initializeopen RocksDB atauthConfig.getAuthConfigPath() + File.separator + "users"/"acls". With a null path this is the literal relative pathnull/users/null/acls, created under the process working directory, instead of<storePathRootDir>/config/usersnext to the broker's store. Restarting the container from a different working directory silently comes up with an empty user/ACL store. - Cluster-scoped admin requests are authorized as
Cluster:null.DefaultAuthorizationContextBuilderbuilds those resources fromauthConfig.getClusterName()(auth/.../DefaultAuthorizationContextBuilder.java:465,553,579,672) forUPDATE_AND_CREATE_TOPIC_LIST,GET_ALL_TOPIC_CONFIG,DELETE_TOPIC_IN_BROKER_LISTandDELETE_SUBSCRIPTION_GROUP_LIST. With a null cluster name the resource key isCluster:null, so a policy that grants a concrete cluster (Cluster:<clusterName>,Resource.isMatchcompares the names literally) never matches: cluster-widemqadmin updateTopic -c .../deleteTopic -c ...are rejected withNO_PERMISSIONon brokers started by a container. - All inner brokers share one set of auth singletons.
AuthenticationFactory/AuthorizationFactorycache providers/evaluators in a static map keyed byPROVIDER_/METADATA_PROVIDER_/EVALUATOR_+config.getConfigName()(AuthenticationFactory.java:51,:78,:95;AuthorizationFactory.java:52,:80,:97). With a null name every broker started by the container uses the..._nullentry, so the second and later brokers reuse the instances created for the first one — including a metadata provider configured for only one of them — instead of each broker owning its own;AuthenticationMetadataManagerImpl.shutdown()of one broker also closes that shared provider for the others.
The standalone broker is not affected because it always fills the three fields before the BrokerController is constructed.
Steps to Reproduce
- Prepare two per-broker configuration files that enable ACL 2.0 (as the ACL 2.0 setup requires):
authenticationEnabled=true,authenticationMetadataProvider=org.apache.rocketmq.auth.authentication.provider.LocalAuthenticationMetadataProvider,authorizationEnabled=true,authorizationMetadataProvider=org.apache.rocketmq.auth.authorization.provider.LocalAuthorizationMetadataProvider, plusbrokerName,brokerClusterNameandstorePathRootDir. - Start a container with those brokers:
mqbrokercontainer -c <container.conf>(or start the container and add a broker withmqadmin addBroker -b <broker.conf>). - Observe that the user/ACL data is created under
<cwd>/null/usersand<cwd>/null/aclsinstead of<storePathRootDir>/config/usersand.../acls, thatgetAuthConfig().getClusterName()is null, and that aCluster:<clusterName>grant does not authorizemqadmin updateTopic -c <cluster>. - At the unit level the same state is reproduced deterministically by starting a broker through the container and checking the
AuthConfigthe container put on it:createAndInitializeBroker(...)→brokerContainer.addBroker(...)→innerBrokerController.getAuthConfig(), i.e. the three fields must equal the valuesBrokerStartupwould have set.
What Did You Expect to See?
A broker started by the container behaves like the same broker started by mqbroker: configName = brokerName, clusterName = brokerClusterName, authConfigPath = <storePathRootDir>/config, so ACL data lives next to the broker store, cluster-scoped authorization sees the real cluster name, and each broker owns its own auth provider/evaluator instances.
What Did You See Instead?
All three fields stay null (and are silently accepted), so the auth database is written to a working-directory-relative null/ directory, cluster-scoped policies are evaluated against Cluster:null, and the inner brokers collapse onto shared *_null factory entries. Nothing in the shipped configurations or documentation asks a container user to set these fields by hand (grep -r authConfigPath distribution/ docs/ finds nothing), so there is no way to avoid it other than knowing the setter-derived key names that MixAll.properties2Object accepts.
Additional Context
Minimal fix: mirror BrokerStartup.java:241-243 in both container entry points (a shared helper so the two paths cannot drift apart again). Scope note: a master and a slave that share one brokerName inside a single container still share the per-name factory entries, which matches the standalone semantics where each of them is its own JVM; the fix deliberately keeps that parity instead of inventing a different key.
I will open a PR with the change plus a regression test that starts a broker through the container and asserts the three fields against the values the standalone startup would have derived (red before the change, green after).
- Dominant language
- Java
- Stars
- 22.7k
- Forks
- 12k
- Avg merge
- 3d 18h
- Merged PRs (30d)
- 17
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from apache/rocketmq
-
[Bug] RemotingHelper.connect swaps the socket send and receive buffer configurationPossibly taken @amalsaidov claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
Maintainers usually reply within 1 day
-
CompressionType.of fails under the Turkish localePossibly taken @sharanggupta claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 83/100
Maintainers usually reply within 1 day
-
[rocketmq-client-python] TransactionMQProducer allocates an unused producer during initializationOpen
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
Maintainers usually reply within 1 day
-
[Bug] notifyMasterOnline aborts the long-polling wake-up with a NullPointerException when an arrival drains a bucket firstPossibly taken @iloveeyjafjalla claimed this 29 days ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
apache/rocketmq#11441 · 1 comment ·
Maintainers usually reply within 1 day
-
[Enhancement] FilterFactory duplicate-registration and unregister semantics lack testsPossibly taken @chennaji9 claimed this 34 days ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
apache/rocketmq#11340 · 2 comments ·
Maintainers usually reply within 1 day
Similar issues
-
bug emitter:client:csharp
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
microsoft/typespec#12572 · 1 comment · 1 reaction ·
Maintainers usually reply within 1 day
-
GCC 15.2: -Wcast-qual warning - cast discards const qualifier from pointer target type (H5trace.c)Open
Difficulty 2/5 1-3 hours Newbie friendliness 73/100
Maintainers usually reply within 1 day
-
needs-area-label
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 2 days
-
area/ai area/backend component/zeebe kind/task
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
spAnser/reacharound#38 ·