suexec: Honor Run As capability like HTTP REST API
This makes the behavior of the two APIs more consistent. suexec is still allowed for the magical "Gerrit Code Review" PeerDaemonUser, but is now also permitted if the Run As capability has been granted. Change-Id: I9acd6467d9e02084519b30e0c4a3d08065e74484
This commit is contained in:
parent
a931fe1bad
commit
08ae5775ea
@ -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…
Reference in New Issue
Block a user