Router: Authorize permissionless internal requests. (#16419)

* Router: Authorize permissionless internal requests.

Router-internal requests like /proxy/enabled and errors for invalid
requests should not require permissions, but they still need to be
authorized in order to satisfy the PreResponseAuthorizationCheckFilter.
This patch adds authorization checks that do not require any particular
permissions.

* Fix tests.
This commit is contained in:
Gian Merlino 2024-06-05 15:31:02 -07:00 committed by GitHub
parent 1040a29bc5
commit 717e634156
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 26 additions and 3 deletions

View File

@ -32,6 +32,8 @@ import org.apache.druid.guice.http.DruidHttpClientConfig;
import org.apache.druid.java.util.common.StringUtils; import org.apache.druid.java.util.common.StringUtils;
import org.apache.druid.server.initialization.jetty.StandardResponseHeaderFilterHolder; import org.apache.druid.server.initialization.jetty.StandardResponseHeaderFilterHolder;
import org.apache.druid.server.security.AuthConfig; import org.apache.druid.server.security.AuthConfig;
import org.apache.druid.server.security.AuthorizationUtils;
import org.apache.druid.server.security.AuthorizerMapper;
import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Response;
@ -41,6 +43,7 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.util.Collections;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public class AsyncManagementForwardingServlet extends AsyncProxyServlet public class AsyncManagementForwardingServlet extends AsyncProxyServlet
@ -71,6 +74,7 @@ public class AsyncManagementForwardingServlet extends AsyncProxyServlet
private final DruidHttpClientConfig httpClientConfig; private final DruidHttpClientConfig httpClientConfig;
private final DruidLeaderSelector coordLeaderSelector; private final DruidLeaderSelector coordLeaderSelector;
private final DruidLeaderSelector overlordLeaderSelector; private final DruidLeaderSelector overlordLeaderSelector;
private final AuthorizerMapper authorizerMapper;
@Inject @Inject
public AsyncManagementForwardingServlet( public AsyncManagementForwardingServlet(
@ -78,7 +82,8 @@ public class AsyncManagementForwardingServlet extends AsyncProxyServlet
@Global Provider<HttpClient> httpClientProvider, @Global Provider<HttpClient> httpClientProvider,
@Global DruidHttpClientConfig httpClientConfig, @Global DruidHttpClientConfig httpClientConfig,
@Coordinator DruidLeaderSelector coordLeaderSelector, @Coordinator DruidLeaderSelector coordLeaderSelector,
@IndexingService DruidLeaderSelector overlordLeaderSelector @IndexingService DruidLeaderSelector overlordLeaderSelector,
AuthorizerMapper authorizerMapper
) )
{ {
this.jsonMapper = jsonMapper; this.jsonMapper = jsonMapper;
@ -86,6 +91,7 @@ public class AsyncManagementForwardingServlet extends AsyncProxyServlet
this.httpClientConfig = httpClientConfig; this.httpClientConfig = httpClientConfig;
this.coordLeaderSelector = coordLeaderSelector; this.coordLeaderSelector = coordLeaderSelector;
this.overlordLeaderSelector = overlordLeaderSelector; this.overlordLeaderSelector = overlordLeaderSelector;
this.authorizerMapper = authorizerMapper;
} }
@Override @Override
@ -110,9 +116,11 @@ public class AsyncManagementForwardingServlet extends AsyncProxyServlet
request.getRequestURI().substring(ARBITRARY_OVERLORD_BASE_PATH.length()) request.getRequestURI().substring(ARBITRARY_OVERLORD_BASE_PATH.length())
); );
} else if (ENABLED_PATH.equals(requestURI)) { } else if (ENABLED_PATH.equals(requestURI)) {
authorizeNoPermissionsNeeded(request);
handleEnabledRequest(response); handleEnabledRequest(response);
return; return;
} else { } else {
authorizeNoPermissionsNeeded(request);
handleInvalidRequest( handleInvalidRequest(
response, response,
StringUtils.format("Unsupported proxy destination[%s]", request.getRequestURI()), StringUtils.format("Unsupported proxy destination[%s]", request.getRequestURI()),
@ -122,6 +130,7 @@ public class AsyncManagementForwardingServlet extends AsyncProxyServlet
} }
if (currentLeader == null) { if (currentLeader == null) {
authorizeNoPermissionsNeeded(request);
handleInvalidRequest( handleInvalidRequest(
response, response,
StringUtils.format( StringUtils.format(
@ -191,6 +200,14 @@ public class AsyncManagementForwardingServlet extends AsyncProxyServlet
super.onServerResponseHeaders(clientRequest, proxyResponse, serverResponse); super.onServerResponseHeaders(clientRequest, proxyResponse, serverResponse);
} }
/**
* Authorizes router-internal requests that do not require any permissions. (But do require an authenticated user.)
*/
private void authorizeNoPermissionsNeeded(HttpServletRequest request)
{
AuthorizationUtils.authorizeAllResourceActions(request, Collections.emptyList(), authorizerMapper);
}
private void handleInvalidRequest(HttpServletResponse response, String errorMessage, int statusCode) throws IOException private void handleInvalidRequest(HttpServletResponse response, String errorMessage, int statusCode) throws IOException
{ {
if (!response.isCommitted()) { if (!response.isCommitted()) {

View File

@ -39,6 +39,10 @@ import org.apache.druid.server.initialization.BaseJettyTest;
import org.apache.druid.server.initialization.ServerConfig; import org.apache.druid.server.initialization.ServerConfig;
import org.apache.druid.server.initialization.jetty.JettyServerInitUtils; import org.apache.druid.server.initialization.jetty.JettyServerInitUtils;
import org.apache.druid.server.initialization.jetty.JettyServerInitializer; import org.apache.druid.server.initialization.jetty.JettyServerInitializer;
import org.apache.druid.server.security.AllowAllAuthenticator;
import org.apache.druid.server.security.AllowAllAuthorizer;
import org.apache.druid.server.security.AuthenticationUtils;
import org.apache.druid.server.security.AuthorizerMapper;
import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
@ -321,7 +325,7 @@ public class AsyncManagementForwardingServletTest extends BaseJettyTest
} }
@Test @Test
public void testProxyEnebledCheck() throws Exception public void testProxyEnabledCheck() throws Exception
{ {
HttpURLConnection connection = ((HttpURLConnection) HttpURLConnection connection = ((HttpURLConnection)
new URL(StringUtils.format("http://localhost:%d/proxy/enabled", port)).openConnection()); new URL(StringUtils.format("http://localhost:%d/proxy/enabled", port)).openConnection());
@ -491,7 +495,8 @@ public class AsyncManagementForwardingServletTest extends BaseJettyTest
injector.getProvider(HttpClient.class), injector.getProvider(HttpClient.class),
injector.getInstance(DruidHttpClientConfig.class), injector.getInstance(DruidHttpClientConfig.class),
coordinatorLeaderSelector, coordinatorLeaderSelector,
overlordLeaderSelector overlordLeaderSelector,
new AuthorizerMapper(ImmutableMap.of("allowAll", new AllowAllAuthorizer()))
) )
); );
@ -502,6 +507,7 @@ public class AsyncManagementForwardingServletTest extends BaseJettyTest
root.addServlet(holder, "/druid/indexer/*"); root.addServlet(holder, "/druid/indexer/*");
root.addServlet(holder, "/proxy/*"); root.addServlet(holder, "/proxy/*");
AuthenticationUtils.addAuthenticationFilterChain(root, ImmutableList.of(new AllowAllAuthenticator()));
JettyServerInitUtils.addExtensionFilters(root, injector); JettyServerInitUtils.addExtensionFilters(root, injector);
final HandlerList handlerList = new HandlerList(); final HandlerList handlerList = new HandlerList();