RockteMQ-AI commented on PR #11179:
URL: https://github.com/apache/rocketmq/pull/11179#issuecomment-5741516622

   Verified the claims against the codebase (base is newer than this clone's 
`develop`, so I inspected the nearest available commit plus the diff). The 
vulnerability analysis is confirmed: 
`DefaultAuthorizationContextBuilder.build(ChannelHandlerContext, 
RemotingCommand)` sets the subject directly from `extFields[AccessKey]` with no 
verification, and `UserAuthorizationHandler` short-circuits `SUPER` users to 
allow.
   
   ---
   
   ## Review
   
   Overall the fix is correct in intent and minimal. `AuthConfig.validate()` is 
the right predicate: authorization derives identity from an unverified 
`AccessKey`, so authZ without authN is not a meaningful configuration. However, 
placement, coverage, and the tests need work.
   
   ### 1. Guard runs too late in broker startup — `BrokerController.java:1147`
   `initialRequestPipeline()` is the last step of `recoverAndInitService()`, 
after `messageStore.load()` (commit-log recovery can take minutes), 
`initializeRemotingServer()`, `initializeResources()` and 
`initializeScheduledTasks()`. A misconfigured broker does all that work, then 
throws `IllegalArgumentException` out of `initialize()`. 
`BrokerStartup.createBrokerController` catches `Throwable` with 
`e.printStackTrace(); System.exit(-1)` — and unlike the `!initResult` path, it 
skips `controller.shutdown()`, so executors/threads created moments earlier are 
abandoned to the exiting JVM. For a pure configuration error this is poor 
operator UX. Suggest validating where the config is bound, e.g. in 
`BrokerStartup.buildBrokerController()` immediately after `properties2Object` 
(clear log + deliberate exit), or at the top of `BrokerController.initialize()` 
before store load.
   
   ### 2. Coverage: only the broker Remoting path is guarded
   The proxy owns a separate `AuthConfig` 
(`ConfigurationManager.getAuthConfig()`) and wires the *same* 
`AuthorizationPipeline`/context builder for gRPC 
(`GrpcMessagingApplication.create`) and Remoting (`RemotingProtocolServer`). 
The guard as written does nothing for those endpoints. If an equivalent check 
already exists for the "gRPC admin surface", please confirm the proxy Remoting 
path is covered too; otherwise moving `validate()` into 
`AuthorizationPipeline`'s constructor (all three components construct it during 
startup) would close all paths with one change.
   
   ### 3. Whitelist re-opens the hole even with authN enabled — 
`AuthConfig.java` (`isAuthenticationRequired`)
   Whitelisted RPCs skip authentication, but `AuthorizationPipeline` does not 
consult the whitelist and still builds the subject from 
`command.getExtFields()`. So `authenticationWhitelist=<rpc>` + authZ reproduces 
exactly the reported impersonation. The durable fix is authorization-layer: 
derive the subject only from a verified authentication result (channel 
attribute/`AuthenticationContext` set after successful verification), never 
from raw extFields, treating a null subject as anonymous-deny. Consider 
tracking this as a follow-up; the startup guard is at best defense-in-depth.
   
   ### 4. Tests do not exercise the fix — `AccessKeySpoofingReproTest.java`
   The repro test calls 
`DefaultAuthorizationContextBuilder`/`AuthorizationFactory` directly, bypassing 
`validate()` and `BrokerController` entirely. It passes identically with and 
without this PR, so it provides zero regression protection — and it permanently 
encodes "forged AccessKey is authorized" as expected behavior. Also 
`Assert.assertTrue("forged access key was authorized", true)` (~L131) is a 
tautology. Please add a test that hits the guard (BrokerController-level, or at 
minimum that `initialRequestPipeline` rejects the bad combination) and either 
delete the repro or clearly mark it as a characterization test of the unguarded 
lower layers.
   
   ### 5. Test hygiene (minor)
   - `Files.createTempDirectory("rmq-auth-repro")` (~L88) creates a new RocksDB 
store per test; `tearDown` only calls `shutdown()`, so temp dirs accumulate 
across runs. Use `TemporaryFolder` or delete in `tearDown`.
   - `MockitoJUnitRunner.Silent` hides unnecessary stubbing; the ~90 lines of 
`ChannelId`/`Attribute` stubs could reuse `AuthTestHelper`/existing test 
scaffolding.
   - `AuthConfigTest` mixes `org.junit.Assert` with the file's AssertJ style; 
`assertThatThrownBy(...).isInstanceOf(...).hasMessageContaining(...)` would 
also lock in the message.
   
   ### Not an issue
   No performance impact (O(1) startup check), no API/protocol break 
(`validate()` is additive), and `IllegalArgumentException` is caught before the 
remoting server starts accepting traffic, so there is no window where a broker 
serves with the unsafe config. The four-way truth-table tests in 
`AuthConfigTest` are appropriate.
   
   **Verdict:** security rationale is sound, but please move the check earlier 
(Finding 1), confirm proxy coverage (Finding 2), and add a test that actually 
fails without the guard (Finding 4) before merging.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to