-
Notifications
You must be signed in to change notification settings - Fork 3.7k
[fix][broker] Broker incorrectly validates proxy role for http request #23036
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
Conversation
When using a proxy, the broker must check the proxy's principal name and original principal name, regardless of the protocol you are using.
Do you mean that the client uses the proxy's principal name?
I forgot why I approved to #19557, which maybe introduces the security risk on http-proxy authorization. This PR changes the authorization logic, right now you only check the original principal name, and the proxy's principal name was ignored. |
Of course, yes but proxy role must not be part of the namespace policy authorization and client doesn't have to explicitly grant permission to proxy-role. Broker checks proxy role by checking authenticating proxy request and proxy's principal name must be part of |
agree, could you help add a test for this ? |
|
Sounds good, but if someone uses the proxy to control access, this will break the user behavior. the pulsar manager uses multiple proxies to control access, for example: proxy1 can produce, but not consume, and proxy2 can consume, but not produce. |
| topicName, operation, originalRole, authData); | ||
| return isRoleAuthorizedFuture.thenCombine(isOriginalAuthorizedFuture, | ||
| (isRoleAuthorized, isOriginalAuthorized) -> isRoleAuthorized && isOriginalAuthorized); | ||
| return allowTopicOperationAsync(topicName, operation, originalRole, authData); |
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.
I think that the original change was made by @michaeljmarshall together with other changes with the intention to fix a CVE, possibly https://pulsar.apache.org/security/CVE-2023-30429/.
@michaeljmarshall do you have a chance to review?
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.
It's not clear to me why this PR only proposes changing this block of code. All 9 times we use the isProxyRole method in the AuthorizationService class, we validate that both the proxy's role and the originalRole have permission to perform a given action. @rdhabalia - why would we only modify this one?
Regarding the change itself, it was introduced in #7788. I remember reading somewhere that the idea was you could have dedicated proxy's for a given set of clients and then you could choose more granular permissions on a per proxy basis.
If you want the proxy to be a super user and want to functionally skip the permission check, just add it to the super user list.
How does it matter? |
michaeljmarshall
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.
I don't think this is a valid change. If I understand it correctly, it implicitly elevates all proxy roles to super user privilege level. Users can currently configure proxy roles to lesser privileges, so that would present a problem for those users.
We verify both the proxy and the original role permission level on the binary protocol here:
pulsar/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/ServerCnx.java
Lines 468 to 494 in 53df683
| private CompletableFuture<Boolean> isTopicOperationAllowed(TopicName topicName, TopicOperation operation, | |
| AuthenticationDataSource authDataSource, AuthenticationDataSource originalAuthDataSource) { | |
| if (!service.isAuthorizationEnabled()) { | |
| return CompletableFuture.completedFuture(true); | |
| } | |
| CompletableFuture<Boolean> isProxyAuthorizedFuture; | |
| if (originalPrincipal != null) { | |
| isProxyAuthorizedFuture = service.getAuthorizationService().allowTopicOperationAsync( | |
| topicName, operation, originalPrincipal, | |
| originalAuthDataSource != null ? originalAuthDataSource : authDataSource); | |
| } else { | |
| isProxyAuthorizedFuture = CompletableFuture.completedFuture(true); | |
| } | |
| CompletableFuture<Boolean> isAuthorizedFuture = service.getAuthorizationService().allowTopicOperationAsync( | |
| topicName, operation, authRole, authDataSource); | |
| return isProxyAuthorizedFuture.thenCombine(isAuthorizedFuture, (isProxyAuthorized, isAuthorized) -> { | |
| if (!isProxyAuthorized) { | |
| log.warn("OriginalRole {} is not authorized to perform operation {} on topic {}", | |
| originalPrincipal, operation, topicName); | |
| } | |
| if (!isAuthorized) { | |
| log.warn("Role {} is not authorized to perform operation {} on topic {}", | |
| authRole, operation, topicName); | |
| } | |
| return isProxyAuthorized && isAuthorized; | |
| }); | |
| } |
If you found a way to work around that, I would consider that a vulnerability, and it would be best to discuss the details on the private mailing list to correctly identify the next steps without any accidental/premature exposure.
| topicName, operation, originalRole, authData); | ||
| return isRoleAuthorizedFuture.thenCombine(isOriginalAuthorizedFuture, | ||
| (isRoleAuthorized, isOriginalAuthorized) -> isRoleAuthorized && isOriginalAuthorized); | ||
| return allowTopicOperationAsync(topicName, operation, originalRole, authData); |
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.
It's not clear to me why this PR only proposes changing this block of code. All 9 times we use the isProxyRole method in the AuthorizationService class, we validate that both the proxy's role and the originalRole have permission to perform a given action. @rdhabalia - why would we only modify this one?
Regarding the change itself, it was introduced in #7788. I remember reading somewhere that the idea was you could have dedicated proxy's for a given set of clients and then you could choose more granular permissions on a per proxy basis.
If you want the proxy to be a super user and want to functionally skip the permission check, just add it to the super user list.
No, that is incorrect, we are not proposing proxy-roles to be super-user. Proxy role has to be authenticated at Broker first and also bring the original principal role which must pass the authorization. Super user role doesn't have to authenticate client and doesn't have to pass original principal as well. Here, fundamental thing is broken where client has to explicitly authorize proxy role to the namespace policy which is not secured, transparent to users and also not compatible behavior with existing binary protocol. |
I don't see any issue with this PR because super-user can also go via proxy and that should be taken care by above PR. |
No, that is incorrect as well. you can check the code and you will see that |
The names of the variables appear inverted, but the logic confirms that both the |
Proxy cases:
Your PR breaks the case2, and introduces the security risk:
|
Motivation
When Proxy and appropriate Proxy authentication is introduced in the Broker, the Proxy first authenticates the request coming from the client and retrieves its principal name, and passes it to the broker as an original-principal name along with its certificates so, the broker can authenticate the proxy with the proxy's certs and broker can also get an original-principal name in the same Connect request. Broker performs the authorization on the original-principal name and validates that proxy role exists into
ServiceConfigiration::proxyRoles. With this auth model, the client can manage its principal-name into authorization policies and still can access broker API via proxy without any extra policy management and it works seamlessly.However, the above auth model was implemented correctly for binary protocol but it was broken in HTTP authentication which is used for HTTP admin requests. In case of HTTP reverse proxy authentication, Broker performs authorization for both proxy's principal name and original principal name and both principal names must be present in namespace authorization policy. It means if client wants to access Pulsar-Broker via proxy then proxy's role must be added into namespace policies and if proxy server's certs are compromised then client data can be in risk. This behavior is incorrect, insecure and not compatible with existing binary authentication.
Broker should perform policy-authorization on original principal and proxy authorization should be done via proxy-roles provided into broker's configurations.
Modifications
This PR fixes the security risk of http-proxy authentication and makes it compatible with binary protocol.
Verifying this change
(Please pick either of the following options)
This change is a trivial rework / code cleanup without any test coverage.
(or)
This change is already covered by existing tests, such as (please describe tests).
(or)
This change added tests and can be verified as follows:
(example:)
Does this pull request potentially affect one of the following parts:
If the box was checked, please highlight the changes
Documentation
docdoc-requireddoc-not-neededdoc-completeMatching PR in forked repository
PR in forked repository: