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