apache/pulsar · error · RestException

Proxy not authorized to access resource (proxy:%s,original:%

Error message

Proxy not authorized to access resource (proxy:%s,original:%s)

What it means

When a request arrives through a broker configured as a proxy role, Pulsar checks admin access for both the proxy identity (clientAppId) and the forwarded original principal. If neither the proxy nor the original principal is a super user (and the proxy is not a tenant admin), validateAdminAccessForTenantAsync throws 401 UNAUTHORIZED with this message. This is a deliberate anti-spoofing check on the proxy header chain.

Source

Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/web/PulsarWebResource.java:358

                    if (pulsar.getConfiguration().isAuthenticationEnabled() && pulsar.getConfiguration()
                            .isAuthorizationEnabled()) {
                        if (!isClientAuthenticated(clientAppId)) {
                            throw new RestException(Status.FORBIDDEN, "Need to authenticate to perform the request");
                        }
                        validateOriginalPrincipal(clientAppId, originalPrincipal);
                        if (pulsar.getConfiguration().getProxyRoles().contains(clientAppId)) {
                            AuthorizationService authorizationService =
                                    pulsar.getBrokerService().getAuthorizationService();
                            return authorizationService.isTenantAdmin(tenant, clientAppId, tenantInfo,
                                            authenticationData)
                                .thenCompose(isTenantAdmin -> {
                                    if (!isTenantAdmin) {
                                            return authorizationService.isSuperUser(clientAppId, authenticationData)
                                                .thenCombine(authorizationService.isSuperUser(originalPrincipal,
                                                             authenticationData),
                                                     (proxyAuthorized, originalPrincipalAuthorized) -> {
                                                         if (!proxyAuthorized || !originalPrincipalAuthorized) {
                                                             throw new RestException(Status.UNAUTHORIZED,
                                                                     String.format("Proxy not authorized to access "
                                                                                     + "resource (proxy:%s,original:%s)"
                                                                             , clientAppId, originalPrincipal));
                                                         } else {
                                                                 log.debug()
                                                                         .attr("principal", originalPrincipal)
                                                                         .attr("proxy", clientAppId)
                                                                         .attr("tenant", tenant)
                                                                         .log("Authorized on tenant");
                                                                 return null;
                                                         }
                                                     });
                                    } else {
                                            log.debug().attr("principal", originalPrincipal)
                                                    .attr("proxy", clientAppId)
                                                    .attr("tenant", tenant)
                                                    .log("Successfully authorized on tenant");
                                                                                return CompletableFuture

View on GitHub (pinned to 820761864e)

Solutions

  1. Add the proxy role (clientAppId) to the broker's superUserRoles, or grant it tenant admin via the authorization provider
  2. Ensure the proxy forwards the original principal header and that the original principal is a super user or tenant admin
  3. Bypass the proxy and call the target broker directly with valid superuser credentials to confirm the proxy config is the problem
  4. Check the configured AuthorizationProvider logic for isSuperUser/isTenantAdmin against your auth source

Example fix

// before (proxy.conf)
superUserRoles=my-admin
proxyRoles=Pulsar-Proxy
// after
superUserRoles=my-admin,Pulsar-Proxy
Defensive patterns

Strategy: validation

Validate before calling

// Verify proxy and original principal are superusers before issuing admin calls via proxy
Set<String> superUsers = getBrokerConfig().getSuperUserRoles();
String proxyRole = getClientRole();
String originalPrincipal = request.getHeader("X-Original-Principal");
if (!superUsers.contains(proxyRole) || originalPrincipal == null || !superUsers.contains(originalPrincipal)) {
    throw new IllegalStateException("proxy/original principal not in superUserRoles: " + proxyRole + ", " + originalPrincipal);
}

Type guard

boolean isAuthorizedProxy(String proxyRole, String originalPrincipal, Set<String> superUsers, Set<String> tenantAdmins) {
    return superUsers.contains(proxyRole) || tenantAdmins.contains(proxyRole)
        ? superUsers.contains(originalPrincipal) || tenantAdmins.contains(originalPrincipal)
        : false;
}

Prevention

When it happens

Trigger: Calling any tenant-admin REST API through a proxy-role broker where: the proxy role is not a super user, and the original principal (from the X-Original-Principal header) is not a super user, and the proxy role is not a tenant admin of the target tenant. E.g. pulsar-admin with a proxy broker configured via brokerClientAuthenticationParameters pointing at a tenant the proxy was never granted admin on.

Common situations: Proxy/broker misconfiguration where getProxyRoles includes the frontend broker but superusers does not list the proxy role or the forwarded principal; missing X-Original-Principal forwarding so the original principal is null/unauthenticated; after tightening authorizationProvider config the proxy lost superuser status.

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/37eceb5e9aacf919. Report an issue: GitHub.