{"record":{"id":"37eceb5e9aacf919","repo":"apache/pulsar","slug":"proxy-not-authorized-to-access-resource-proxy-s","errorCode":null,"errorMessage":"Proxy not authorized to access resource (proxy:%s,original:%s)","messagePattern":"Proxy not authorized to access resource \\(proxy:(.+?),original:(.+?)\\)","errorType":"http","errorClass":"RestException","httpStatus":401,"severity":"error","filePath":"pulsar-broker/src/main/java/org/apache/pulsar/broker/web/PulsarWebResource.java","lineNumber":358,"sourceCode":"                    if (pulsar.getConfiguration().isAuthenticationEnabled() && pulsar.getConfiguration()\n                            .isAuthorizationEnabled()) {\n                        if (!isClientAuthenticated(clientAppId)) {\n                            throw new RestException(Status.FORBIDDEN, \"Need to authenticate to perform the request\");\n                        }\n                        validateOriginalPrincipal(clientAppId, originalPrincipal);\n                        if (pulsar.getConfiguration().getProxyRoles().contains(clientAppId)) {\n                            AuthorizationService authorizationService =\n                                    pulsar.getBrokerService().getAuthorizationService();\n                            return authorizationService.isTenantAdmin(tenant, clientAppId, tenantInfo,\n                                            authenticationData)\n                                .thenCompose(isTenantAdmin -> {\n                                    if (!isTenantAdmin) {\n                                            return authorizationService.isSuperUser(clientAppId, authenticationData)\n                                                .thenCombine(authorizationService.isSuperUser(originalPrincipal,\n                                                             authenticationData),\n                                                     (proxyAuthorized, originalPrincipalAuthorized) -> {\n                                                         if (!proxyAuthorized || !originalPrincipalAuthorized) {\n                                                             throw new RestException(Status.UNAUTHORIZED,\n                                                                     String.format(\"Proxy not authorized to access \"\n                                                                                     + \"resource (proxy:%s,original:%s)\"\n                                                                             , clientAppId, originalPrincipal));\n                                                         } else {\n                                                                 log.debug()\n                                                                         .attr(\"principal\", originalPrincipal)\n                                                                         .attr(\"proxy\", clientAppId)\n                                                                         .attr(\"tenant\", tenant)\n                                                                         .log(\"Authorized on tenant\");\n                                                                 return null;\n                                                         }\n                                                     });\n                                    } else {\n                                            log.debug().attr(\"principal\", originalPrincipal)\n                                                    .attr(\"proxy\", clientAppId)\n                                                    .attr(\"tenant\", tenant)\n                                                    .log(\"Successfully authorized on tenant\");\n                                                                                return CompletableFuture","sourceCodeStart":340,"sourceCodeEnd":376,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker/src/main/java/org/apache/pulsar/broker/web/PulsarWebResource.java#L340-L376","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Add the proxy role (clientAppId) to the broker's superUserRoles, or grant it tenant admin via the authorization provider","Ensure the proxy forwards the original principal header and that the original principal is a super user or tenant admin","Bypass the proxy and call the target broker directly with valid superuser credentials to confirm the proxy config is the problem","Check the configured AuthorizationProvider logic for isSuperUser/isTenantAdmin against your auth source"],"exampleFix":"// before (proxy.conf)\nsuperUserRoles=my-admin\nproxyRoles=Pulsar-Proxy\n// after\nsuperUserRoles=my-admin,Pulsar-Proxy","handlingStrategy":"validation","validationCode":"// Verify proxy and original principal are superusers before issuing admin calls via proxy\nSet<String> superUsers = getBrokerConfig().getSuperUserRoles();\nString proxyRole = getClientRole();\nString originalPrincipal = request.getHeader(\"X-Original-Principal\");\nif (!superUsers.contains(proxyRole) || originalPrincipal == null || !superUsers.contains(originalPrincipal)) {\n    throw new IllegalStateException(\"proxy/original principal not in superUserRoles: \" + proxyRole + \", \" + originalPrincipal);\n}","typeGuard":"boolean isAuthorizedProxy(String proxyRole, String originalPrincipal, Set<String> superUsers, Set<String> tenantAdmins) {\n    return superUsers.contains(proxyRole) || tenantAdmins.contains(proxyRole)\n        ? superUsers.contains(originalPrincipal) || tenantAdmins.contains(originalPrincipal)\n        : false;\n}","tryCatchPattern":null,"preventionTips":["Keep the proxy role in superUserRoles on the backing broker","Always forward X-Original-Principal (and original auth data) through the proxy","Test proxy-based admin calls after every authorizationProvider/config change"],"tags":["authorization","http-401","proxy","pulsar-admin"],"backgroundTag":"proxy-not-authorized","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}