Merge "suexec: Honor Run As capability like HTTP REST API"
This commit is contained in:
commit
665c2a88d3
@ -1270,9 +1270,10 @@ command, but also to the web UI results pagination size.
|
||||
Run As
|
||||
~~~~~~
|
||||
|
||||
Allow users to impersonate any other user with the X-Gerrit-RunAs
|
||||
HTTP header on REST API calls. Site administrators do not inherit
|
||||
this capability; it must be granted explicitly.
|
||||
Allow users to impersonate any other user with the X-Gerrit-RunAs HTTP
|
||||
header on REST API calls or the link:cmd-suexec.html[suexec] SSH
|
||||
command. Site administrators do not inherit this capability; it must
|
||||
be granted explicitly.
|
||||
|
||||
|
||||
[[capability_runGC]]
|
||||
|
@ -19,10 +19,14 @@ SYNOPSIS
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
The suexec command can only be invoked by the magic user `Gerrit
|
||||
Code Review` and permits executing any other command as any other
|
||||
The suexec command permits executing any other command as any other
|
||||
registered user account.
|
||||
|
||||
suexec can only be invoked by the magic user `Gerrit Code Review`,
|
||||
or any user granted granted the link:access-control.html#capability_runAs[Run As]
|
||||
capability. The run as capability is permitted to be used only if
|
||||
link:config-gerrit.html[auth.enableRunAs] is true.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
|
||||
@ -39,7 +43,8 @@ COMMAND::
|
||||
ACCESS
|
||||
------
|
||||
Caller must be the magic user Gerrit Code Review using the SSH
|
||||
daemon's host key or a key on this daemon's peer host key ring.
|
||||
daemon's host key, or a key on this daemon's peer host key ring,
|
||||
or a user granted the Run As capability.
|
||||
|
||||
SCRIPTING
|
||||
---------
|
||||
|
@ -21,14 +21,13 @@ import com.google.gerrit.httpd.restapi.RestApiServlet;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.account.AccountResolver;
|
||||
import com.google.gerrit.server.config.GerritServerConfig;
|
||||
import com.google.gerrit.server.config.AuthConfig;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
import com.google.inject.servlet.ServletModule;
|
||||
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -61,10 +60,10 @@ class RunAsFilter implements Filter {
|
||||
private final AccountResolver accountResolver;
|
||||
|
||||
@Inject
|
||||
RunAsFilter(@GerritServerConfig Config config,
|
||||
RunAsFilter(AuthConfig config,
|
||||
Provider<WebSession> session,
|
||||
AccountResolver accountResolver) {
|
||||
this.enabled = config.getBoolean("auth", null, "enableRunAs", true);
|
||||
this.enabled = config.isRunAsEnabled();
|
||||
this.session = session;
|
||||
this.accountResolver = accountResolver;
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ public class AuthConfig {
|
||||
private final AuthType authType;
|
||||
private final String httpHeader;
|
||||
private final boolean trustContainerAuth;
|
||||
private final boolean enableRunAs;
|
||||
private final boolean userNameToLowerCase;
|
||||
private final boolean gitBasicAuth;
|
||||
private final String logoutUrl;
|
||||
@ -64,6 +65,7 @@ public class AuthConfig {
|
||||
cookiePath = cfg.getString("auth", null, "cookiepath");
|
||||
cookieSecure = cfg.getBoolean("auth", "cookiesecure", false);
|
||||
trustContainerAuth = cfg.getBoolean("auth", "trustContainerAuth", false);
|
||||
enableRunAs = cfg.getBoolean("auth", null, "enableRunAs", true);
|
||||
gitBasicAuth = cfg.getBoolean("auth", "gitBasicAuth", false);
|
||||
userNameToLowerCase = cfg.getBoolean("auth", "userNameToLowerCase", false);
|
||||
|
||||
@ -164,6 +166,11 @@ public class AuthConfig {
|
||||
return trustContainerAuth;
|
||||
}
|
||||
|
||||
/** @return true if users with Run As capability can impersonate others. */
|
||||
public boolean isRunAsEnabled() {
|
||||
return enableRunAs;
|
||||
}
|
||||
|
||||
/** Whether user name should be converted to lower-case before validation */
|
||||
public boolean isUserNameToLowerCase() {
|
||||
return userNameToLowerCase;
|
||||
|
@ -19,6 +19,7 @@ import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.PeerDaemonUser;
|
||||
import com.google.gerrit.server.config.AuthConfig;
|
||||
import com.google.gerrit.sshd.SshScope.Context;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
@ -45,6 +46,7 @@ public final class SuExec extends BaseCommand {
|
||||
private final SshScope sshScope;
|
||||
private final DispatchCommandProvider dispatcher;
|
||||
|
||||
private boolean enableRunAs;
|
||||
private Provider<CurrentUser> caller;
|
||||
private Provider<SshSession> session;
|
||||
private IdentifiedUser.GenericFactory userFactory;
|
||||
@ -66,36 +68,34 @@ public final class SuExec extends BaseCommand {
|
||||
@CommandName(Commands.ROOT) final DispatchCommandProvider dispatcher,
|
||||
final Provider<CurrentUser> caller, final Provider<SshSession> session,
|
||||
final IdentifiedUser.GenericFactory userFactory,
|
||||
final SshScope.Context callingContext) {
|
||||
final SshScope.Context callingContext,
|
||||
AuthConfig config) {
|
||||
this.sshScope = sshScope;
|
||||
this.dispatcher = dispatcher;
|
||||
this.caller = caller;
|
||||
this.session = session;
|
||||
this.userFactory = userFactory;
|
||||
this.callingContext = callingContext;
|
||||
this.enableRunAs = config.isRunAsEnabled();
|
||||
atomicCmd = Atomics.newReference();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(Environment env) throws IOException {
|
||||
try {
|
||||
if (caller.get() instanceof PeerDaemonUser) {
|
||||
parseCommandLine();
|
||||
checkCanRunAs();
|
||||
parseCommandLine();
|
||||
|
||||
final Context ctx = callingContext.subContext(newSession(), join(args));
|
||||
final Context old = sshScope.set(ctx);
|
||||
try {
|
||||
final BaseCommand cmd = dispatcher.get();
|
||||
cmd.setArguments(args.toArray(new String[args.size()]));
|
||||
provideStateTo(cmd);
|
||||
atomicCmd.set(cmd);
|
||||
cmd.start(env);
|
||||
} finally {
|
||||
sshScope.set(old);
|
||||
}
|
||||
|
||||
} else {
|
||||
throw new UnloggedFailure(1, "fatal: Not a peer daemon");
|
||||
final Context ctx = callingContext.subContext(newSession(), join(args));
|
||||
final Context old = sshScope.set(ctx);
|
||||
try {
|
||||
final BaseCommand cmd = dispatcher.get();
|
||||
cmd.setArguments(args.toArray(new String[args.size()]));
|
||||
provideStateTo(cmd);
|
||||
atomicCmd.set(cmd);
|
||||
cmd.start(env);
|
||||
} finally {
|
||||
sshScope.set(old);
|
||||
}
|
||||
} catch (UnloggedFailure e) {
|
||||
String msg = e.getMessage();
|
||||
@ -108,6 +108,17 @@ public final class SuExec extends BaseCommand {
|
||||
}
|
||||
}
|
||||
|
||||
private void checkCanRunAs() throws UnloggedFailure {
|
||||
if (caller.get() instanceof PeerDaemonUser) {
|
||||
// OK.
|
||||
} else if (!enableRunAs) {
|
||||
throw new UnloggedFailure(1,
|
||||
"fatal: suexec disabled by auth.enableRunAs = false");
|
||||
} else if (!caller.get().getCapabilities().canRunAs()) {
|
||||
throw new UnloggedFailure(1, "fatal: suexec not permitted");
|
||||
}
|
||||
}
|
||||
|
||||
private SshSession newSession() {
|
||||
final SocketAddress peer;
|
||||
if (peerAddress == null) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user