-
Notifications
You must be signed in to change notification settings - Fork 3.7k
[fix][ws] Fix WebSocket authorization issue due to originalPrincipal must be provided #24533
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[fix][ws] Fix WebSocket authorization issue due to originalPrincipal must be provided #24533
Conversation
## Summary This PR fixes an authorization issue where WebSocket connections through Pulsar proxy would fail when both authentication and authorization were enabled. ## Changes - Added WEBSOCKET_DUMMY_ORIGINAL_PRINCIPLE constant for WebSocket connections - Modified AuthorizationService to handle WebSocket dummy principal cases - Updated WebSocketService to set dummy original principal for internal client - Added originalPrincipal method to ClientBuilderImpl - Updated authorization checks across multiple broker components - Added comprehensive integration test WebSocketProxyAuthIntegrationTest ## Problem When WebSocket connections were made through a Pulsar proxy with both authentication and authorization enabled, the authorization logic would fail because: 1. The system detected the client role as a proxy role (e.g., "websocket_proxy") 2. For proxy roles, the system requires both the proxy role AND the original client role to be authorized 3. WebSocket connections didn't have a proper original principal set, causing authorization to fail ## Solution Introduced a "dummy original principal" concept specifically for WebSocket connections: - WebSocket service sets a dummy original principal when creating its internal client - Authorization service detects this dummy principal and skips dual authorization - Only applies single authorization (just the proxy role) for WebSocket connections - Maintains security by still authorizing the proxy role itself ## Test Plan - Added WebSocketProxyAuthIntegrationTest with JWT token authentication - Tests WebSocket proxy with authentication and authorization enabled - Validates produce/consume operations work correctly with different tokens - Ensures proper authorization flow with dummy principal handling 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Added UNAUTHORIZED_TOKEN for testing authorization failures - Added testWebSocketProxyWithUnauthorizedToken method to verify that tokens without proper permissions are rejected - Enhanced test coverage to include both positive and negative authorization scenarios - Updated class documentation to reflect the new test coverage This ensures the WebSocket dummy principal fix maintains proper authorization while allowing legitimate connections. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
|
@codelipenghui Please add the following content to your PR description and select a checkbox: |
- Remove trailing whitespace from lines 313, 319, 321, 337, 343, 345 - Clean up formatting to meet checkstyle requirements 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
|
/pulsarbot run-failure-checks |
lhotari
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Introduced a "dummy original principal" concept specifically for WebSocket connections:
- WebSocket service sets a dummy original principal when creating its internal client since WebSocket already done the authorization
- Authorization service detects this dummy principal and skips dual authorization
- Only applies single authorization (just the proxy role) for WebSocket connections
- Maintains security by still authorizing the proxy role itself
I haven't checked the details yet, but I'm just wondering why couldn't the WebSocket service set the original principal to the actual value of the authenticated principal instead of using a "dummy value" so that WebSocket wouldn't be an exception in how authorization works via the proxy. The downside of this dummy value solution is that WebSocket connections would all have the authorizations of the proxy role which is usually a super user.
Thank you for the excellent question! Let me explain the two major technical reasons why WebSocket proxy cannot use the same authorization approach as the regular Pulsar proxy:
The WebSocket proxy is already performing authorization when clients connect and attempt operations. Requiring the broker to re-authorize the same client would be Redundant and Inefficient. pulsar/pulsar-websocket/src/main/java/org/apache/pulsar/websocket/AbstractWebSocketHandler.java Lines 124 to 150 in 702c73c
This is the crucial technical constraint. WebSocket proxy uses a single shared Pulsar client for all WebSocket connections. pulsar/pulsar-websocket/src/main/java/org/apache/pulsar/websocket/WebSocketService.java Lines 186 to 223 in 702c73c
I will also add the detailed context to the PR description. |
|
/pulsarbot run-failure-checks |
1 similar comment
|
/pulsarbot run-failure-checks |
|
/pulsarbot run-failure-checks |
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #24533 +/- ##
============================================
+ Coverage 73.57% 74.27% +0.70%
+ Complexity 32624 32549 -75
============================================
Files 1877 1869 -8
Lines 139502 146095 +6593
Branches 15299 16759 +1460
============================================
+ Hits 102638 108518 +5880
- Misses 28908 28958 +50
- Partials 7956 8619 +663
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
…must be provided (apache#24533) (cherry picked from commit e916457) (cherry picked from commit e9c1ad0)
…must be provided (apache#24533) (cherry picked from commit e916457) (cherry picked from commit e9c1ad0)
PR apache#24533 introduced incorrect AuthData handling in proxy scenarios, where the AuthorizationService.allowTopicOperationAsync method was being called with wrong parameters, losing the separation between proxy auth data and original client auth data. This fix: - Adds a new 6-parameter allowTopicOperationAsync method that properly separates originalAuthData and authData validation - Updates ServerCnx to use the new method with correct parameters - Fixes authorization logic to validate both proxy and original client permissions independently - Updates all affected test verification calls to match new method signatures 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This is a follow-up fix to PR apache#24533 which addressed WebSocket authorization issues by introducing a dummy original principal for binary protocol connections, but missed the case where WebSocket proxy uses HTTP admin API calls (web service URLs). Issue: When WebSocket proxy is configured to use web service URLs instead of broker service URLs, it makes HTTP admin API calls for operations like topic lookup. These HTTP requests were missing the X-Original-Principal header, causing authorization failures with the error: "Illegal combination of role [websocket_proxy] and originalPrincipal [null]: originalPrincipal must be provided when connecting with a proxy role." Root Cause: The originalPrincipal was correctly set for binary protocol connections via ClientBuilderImpl.originalPrincipal(), but HttpClient did not include this information as an X-Original-Principal header in HTTP requests to admin APIs. Fix: - Modified HttpClient to store ClientConfigurationData and automatically include X-Original-Principal header when originalPrincipal is configured - Added ProxyRoleAuthWebServiceURLTest to verify the fix for web service URL scenarios The fix ensures WebSocket proxy authentication works correctly for both: 1. Binary protocol (broker service URLs) - already fixed by apache#24533 2. HTTP admin API (web service URLs) - now fixed by this change 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Summary
This PR fixes an authorization issue where WebSocket connections through Pulsar proxy would fail when both authentication and authorization were enabled.
Here is the warning log from the broker
Problem
When WebSocket connections were made through a Pulsar proxy with both authentication and authorization enabled, the authorization logic would fail because:
Solution
Introduced a "dummy original principal" concept specifically for WebSocket connections:
Why not passing the original principle to broker for authorization, just keep it consistent with Pulsar proxy?
The Websocket proxy is working differently with Pulsar proxy
The WebSocket proxy is already performing authorization when clients connect and attempt operations. Requiring the broker to re-authorize the same client would be Redundant and Inefficient.
pulsar/pulsar-websocket/src/main/java/org/apache/pulsar/websocket/AbstractWebSocketHandler.java
Lines 124 to 150 in 702c73c
This is the crucial technical constraint. WebSocket proxy uses a single shared Pulsar client for all WebSocket connections.
pulsar/pulsar-websocket/src/main/java/org/apache/pulsar/websocket/WebSocketService.java
Lines 186 to 223 in 702c73c
To use original principals, we would need:
// This would require a completely different architecture
Map<String, PulsarClient> clientsPerPrincipal = new HashMap<>();
// For each WebSocket client with different principal
PulsarClient clientForPrincipal = PulsarClient.builder()
.authentication(clientPrincipal) // Different auth for each client
.build();
Why This Architectural Change is Problematic
Resource Overhead
Complexity
Performance Impact
The Dummy Principal Solution
Given these constraints, the dummy principal approach:
Security Considerations
The security model becomes:
This is actually a cleaner separation of concerns - the proxy handles client authorization, and the broker trusts the proxy's decisions.
A concern for client side to assign the dummy principle to get rid of the authorization?
Key Changes
Core Authorization Logic (
AuthorizationService.java)WebSocket Service (
WebSocketService.java)Test Plan
WebSocketProxyAuthIntegrationTestwith JWT token authenticationADMIN_TOKEN: Used by admin client for setup operationsPROXY_TOKEN: Used by WebSocket proxy's internal clientCLIENT_TOKEN: Used by WebSocket clientsVerification
The integration test confirms:
WebSocket connections can authenticate through proxy with proper tokens
Authorization works correctly with the dummy principal approach
Messages can be successfully produced and consumed
No regression in existing proxy authorization behavior
docdoc-requireddoc-not-neededdoc-complete🤖 Generated with Claude Code