Allow LFS-over-SSH created auth pass through ContainerAuthFilter
Issue: When LFS operation is initiated through the SSH LFS client receives auth token and uses it to perform requested operation e.g.: POST /gerrit/test-org/test-no-block/info/lfs/objects/batch Authorization: Ssh: ... Content-Type: application/vnd.git-lfs+json; charset=utf-8 { "operation":"upload","objects"... } ContainerAuthFilter searches for existing user but none of the containers can perform successful LFS auth (as it is deeply buried in the plugin internals) therefore typically it is configured to let it go through to eventually fail in the filter with: 403 Forbidden Solution: Modify ContainerAuthFilter so that it returns 'true' when Content-Type indicates LFS request is against LFS and Authorization header value starts with "Ssh: " string (similarly to ProjectBasicAuthFilter when it doesn't start with "Basic "). Rationale: ContainerAuthFilter is installed for requests that either go through /a/* (authorised path) or to LFS (note that LFS over HTTP sends auth token even when request is not `/a/` prefixed - hence user can be obtained from request with the first call without sending 401 back and re-sending request with `/a/` prefix. In terms of LFS over SSH it is again request against LFS but in this case it has `Ssh: ` based auth token that is not recognized by filter and results in 403. This change is safe as it introduces exception only for LFS requests that rely on internal LFS auth anyway. Change-Id: Ia886dc284c8ded9c21a5b9f57628f228c1e691f0 Signed-off-by: Jacek Centkowski <jcentkowski@collab.net>
This commit is contained in:
parent
e238e4ebe1
commit
82cd64b404
@ -17,9 +17,12 @@ package com.google.gerrit.httpd;
|
|||||||
import static com.google.common.base.MoreObjects.firstNonNull;
|
import static com.google.common.base.MoreObjects.firstNonNull;
|
||||||
import static com.google.common.base.Strings.emptyToNull;
|
import static com.google.common.base.Strings.emptyToNull;
|
||||||
import static com.google.common.net.HttpHeaders.AUTHORIZATION;
|
import static com.google.common.net.HttpHeaders.AUTHORIZATION;
|
||||||
|
import static com.google.gerrit.extensions.api.lfs.LfsDefinitions.CONTENTTYPE_VND_GIT_LFS_JSON;
|
||||||
|
import static com.google.gerrit.httpd.GerritAuthModule.NOT_AUTHORIZED_LFS_URL_REGEX;
|
||||||
import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
|
import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
|
||||||
import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
|
import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
|
||||||
|
|
||||||
|
import com.google.common.base.Strings;
|
||||||
import com.google.gerrit.extensions.registration.DynamicItem;
|
import com.google.gerrit.extensions.registration.DynamicItem;
|
||||||
import com.google.gerrit.httpd.restapi.RestApiServlet;
|
import com.google.gerrit.httpd.restapi.RestApiServlet;
|
||||||
import com.google.gerrit.server.AccessPath;
|
import com.google.gerrit.server.AccessPath;
|
||||||
@ -31,6 +34,7 @@ import com.google.inject.Inject;
|
|||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
import javax.servlet.Filter;
|
import javax.servlet.Filter;
|
||||||
import javax.servlet.FilterChain;
|
import javax.servlet.FilterChain;
|
||||||
import javax.servlet.FilterConfig;
|
import javax.servlet.FilterConfig;
|
||||||
@ -54,6 +58,9 @@ import org.eclipse.jgit.lib.Config;
|
|||||||
*/
|
*/
|
||||||
@Singleton
|
@Singleton
|
||||||
class ContainerAuthFilter implements Filter {
|
class ContainerAuthFilter implements Filter {
|
||||||
|
private static final String LFS_AUTH_PREFIX = "Ssh: ";
|
||||||
|
private static final Pattern LFS_ENDPOINT = Pattern.compile(NOT_AUTHORIZED_LFS_URL_REGEX);
|
||||||
|
|
||||||
private final DynamicItem<WebSession> session;
|
private final DynamicItem<WebSession> session;
|
||||||
private final AccountCache accountCache;
|
private final AccountCache accountCache;
|
||||||
private final Config config;
|
private final Config config;
|
||||||
@ -92,6 +99,11 @@ class ContainerAuthFilter implements Filter {
|
|||||||
private boolean verify(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
|
private boolean verify(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
|
||||||
String username = RemoteUserUtil.getRemoteUser(req, loginHttpHeader);
|
String username = RemoteUserUtil.getRemoteUser(req, loginHttpHeader);
|
||||||
if (username == null) {
|
if (username == null) {
|
||||||
|
if (isLfsOverSshRequest(req)) {
|
||||||
|
// LFS-over-SSH auth request cannot be authorized by container
|
||||||
|
// therefore let it go through the filter
|
||||||
|
return true;
|
||||||
|
}
|
||||||
rsp.sendError(SC_FORBIDDEN);
|
rsp.sendError(SC_FORBIDDEN);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -109,4 +121,12 @@ class ContainerAuthFilter implements Filter {
|
|||||||
ws.setAccessPathOk(AccessPath.REST_API, true);
|
ws.setAccessPathOk(AccessPath.REST_API, true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isLfsOverSshRequest(HttpServletRequest req) {
|
||||||
|
String hdr = req.getHeader(AUTHORIZATION);
|
||||||
|
return CONTENTTYPE_VND_GIT_LFS_JSON.equals(req.getContentType())
|
||||||
|
&& !Strings.isNullOrEmpty(hdr)
|
||||||
|
&& hdr.startsWith(LFS_AUTH_PREFIX)
|
||||||
|
&& LFS_ENDPOINT.matcher(req.getRequestURI()).matches();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ import javax.servlet.Filter;
|
|||||||
|
|
||||||
/** Configures filter for authenticating REST requests. */
|
/** Configures filter for authenticating REST requests. */
|
||||||
public class GerritAuthModule extends ServletModule {
|
public class GerritAuthModule extends ServletModule {
|
||||||
private static final String NOT_AUTHORIZED_LFS_URL_REGEX = "^(?:(?!/a/))" + LFS_URL_WO_AUTH_REGEX;
|
static final String NOT_AUTHORIZED_LFS_URL_REGEX = "^(?:(?!/a/))" + LFS_URL_WO_AUTH_REGEX;
|
||||||
private final AuthConfig authConfig;
|
private final AuthConfig authConfig;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
|
Loading…
Reference in New Issue
Block a user