diff --git a/src/applications/config/option/PhabricatorPHDConfigOptions.php b/src/applications/config/option/PhabricatorPHDConfigOptions.php index ae3f7f52b7..934684b91b 100644 --- a/src/applications/config/option/PhabricatorPHDConfigOptions.php +++ b/src/applications/config/option/PhabricatorPHDConfigOptions.php @@ -41,6 +41,14 @@ final class PhabricatorPHDConfigOptions "of output, but can help debug issues. Daemons launched in debug ". "mode with 'phd debug' are always launched in verbose mode. See ". "also 'phd.trace'.")), + $this->newOption('phd.user', 'string', null) + ->setSummary(pht("System user to run daemons as.")) + ->setDescription( + pht( + "Specify a system user to run the daemons as. Primarily, this ". + "user will own the working copies of any repositories that ". + "Phabricator imports or manages. This option is new and ". + "experimental.")), $this->newOption('phd.trace', 'bool', false) ->setBoolOptions( array( diff --git a/src/applications/diffusion/controller/DiffusionServeController.php b/src/applications/diffusion/controller/DiffusionServeController.php index c009fbe83e..7bb19e47ce 100644 --- a/src/applications/diffusion/controller/DiffusionServeController.php +++ b/src/applications/diffusion/controller/DiffusionServeController.php @@ -326,7 +326,10 @@ final class DiffusionServeController extends DiffusionController { $input = PhabricatorStartup::getRawInput(); - list($err, $stdout, $stderr) = id(new ExecFuture('%s', $bin)) + $command = csprintf('%s', $bin); + $command = PhabricatorDaemon::sudoCommandAsDaemonUser($command); + + list($err, $stdout, $stderr) = id(new ExecFuture('%C', $command)) ->setEnv($env, true) ->write($input) ->resolve(); @@ -422,7 +425,10 @@ final class DiffusionServeController extends DiffusionController { $input = strlen($input)."\n".$input."0\n"; } - list($err, $stdout, $stderr) = id(new ExecFuture('%s serve --stdio', $bin)) + $command = csprintf('%s serve --stdio', $bin); + $command = PhabricatorDaemon::sudoCommandAsDaemonUser($command); + + list($err, $stdout, $stderr) = id(new ExecFuture('%C', $command)) ->setEnv($env, true) ->setCWD($repository->getLocalPath()) ->write("{$cmd}\n{$args}{$input}") @@ -545,5 +551,6 @@ final class DiffusionServeController extends DiffusionController { return $has_pack && $is_hangup; } + } diff --git a/src/applications/diffusion/ssh/DiffusionSSHGitReceivePackWorkflow.php b/src/applications/diffusion/ssh/DiffusionSSHGitReceivePackWorkflow.php index fb5607792c..4c379c4638 100644 --- a/src/applications/diffusion/ssh/DiffusionSSHGitReceivePackWorkflow.php +++ b/src/applications/diffusion/ssh/DiffusionSSHGitReceivePackWorkflow.php @@ -22,9 +22,10 @@ final class DiffusionSSHGitReceivePackWorkflow // This is a write, and must have write access. $this->requireWriteAccess(); - $future = new ExecFuture( - 'git-receive-pack %s', - $repository->getLocalPath()); + $command = csprintf('git-receive-pack %s', $repository->getLocalPath()); + $command = PhabricatorDaemon::sudoCommandAsDaemonUser($command); + + $future = new ExecFuture('%C', $command); $err = $this->newPassthruCommand() ->setIOChannel($this->getIOChannel()) diff --git a/src/applications/diffusion/ssh/DiffusionSSHGitUploadPackWorkflow.php b/src/applications/diffusion/ssh/DiffusionSSHGitUploadPackWorkflow.php index 35026b4bf8..0ccfb95508 100644 --- a/src/applications/diffusion/ssh/DiffusionSSHGitUploadPackWorkflow.php +++ b/src/applications/diffusion/ssh/DiffusionSSHGitUploadPackWorkflow.php @@ -19,7 +19,10 @@ final class DiffusionSSHGitUploadPackWorkflow $path = head($args->getArg('dir')); $repository = $this->loadRepository($path); - $future = new ExecFuture('git-upload-pack %s', $repository->getLocalPath()); + $command = csprintf('git-upload-pack -- %s', $repository->getLocalPath()); + $command = PhabricatorDaemon::sudoCommandAsDaemonUser($command); + + $future = new ExecFuture('%C', $command); $err = $this->newPassthruCommand() ->setIOChannel($this->getIOChannel()) diff --git a/src/applications/diffusion/ssh/DiffusionSSHMercurialServeWorkflow.php b/src/applications/diffusion/ssh/DiffusionSSHMercurialServeWorkflow.php index 603fdaa7d0..46eee57610 100644 --- a/src/applications/diffusion/ssh/DiffusionSSHMercurialServeWorkflow.php +++ b/src/applications/diffusion/ssh/DiffusionSSHMercurialServeWorkflow.php @@ -39,12 +39,12 @@ final class DiffusionSSHMercurialServeWorkflow throw new Exception("Expected `hg ... serve`!"); } - $future = new ExecFuture( - 'hg -R %s serve --stdio', - $repository->getLocalPath()); + $command = csprintf('hg -R %s serve --stdio', $repository->getLocalPath()); + $command = PhabricatorDaemon::sudoCommandAsDaemonUser($command); + + $future = new ExecFuture('%C', $command); $io_channel = $this->getIOChannel(); - $protocol_channel = new DiffusionSSHMercurialWireClientProtocolChannel( $io_channel); diff --git a/src/applications/diffusion/ssh/DiffusionSSHSubversionServeWorkflow.php b/src/applications/diffusion/ssh/DiffusionSSHSubversionServeWorkflow.php index 1a0ad4e06f..23e5c6f28e 100644 --- a/src/applications/diffusion/ssh/DiffusionSSHSubversionServeWorkflow.php +++ b/src/applications/diffusion/ssh/DiffusionSSHSubversionServeWorkflow.php @@ -38,7 +38,10 @@ final class DiffusionSSHSubversionServeWorkflow throw new Exception("Expected `svnserve -t`!"); } - $future = new ExecFuture('svnserve -t'); + $command = csprintf('svnserve -t'); + $command = PhabricatorDaemon::sudoCommandAsDaemonUser($command); + + $future = new ExecFuture('%C', $command); $this->inProtocol = new DiffusionSubversionWireProtocol(); $this->outProtocol = new DiffusionSubversionWireProtocol(); diff --git a/src/applications/diffusion/ssh/DiffusionSSHWorkflow.php b/src/applications/diffusion/ssh/DiffusionSSHWorkflow.php index c04334441b..954e01fd1a 100644 --- a/src/applications/diffusion/ssh/DiffusionSSHWorkflow.php +++ b/src/applications/diffusion/ssh/DiffusionSSHWorkflow.php @@ -120,5 +120,4 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow { return $this->hasWriteAccess; } - } diff --git a/src/infrastructure/daemon/PhabricatorDaemon.php b/src/infrastructure/daemon/PhabricatorDaemon.php index b68518b07d..349e0f0ffe 100644 --- a/src/infrastructure/daemon/PhabricatorDaemon.php +++ b/src/infrastructure/daemon/PhabricatorDaemon.php @@ -19,4 +19,35 @@ abstract class PhabricatorDaemon extends PhutilDaemon { return PhabricatorUser::getOmnipotentUser(); } + + /** + * Format a command so it executes as the daemon user, if a daemon user is + * defined. This wraps the provided command in `sudo -u ...`, roughly. + * + * @param PhutilCommandString Command to execute. + * @return PhutilCommandString `sudo` version of the command. + */ + public static function sudoCommandAsDaemonUser($command) { + $user = PhabricatorEnv::getEnvConfig('phd.user'); + if (!$user) { + // No daemon user is set, so just run this as ourselves. + return $command; + } + + // Get the absolute path so we're safe against the caller wiping out + // PATH. + $sudo = Filesystem::resolveBinary('sudo'); + if (!$sudo) { + throw new Exception(pht("Unable to find 'sudo'!")); + } + + // Flags here are: + // + // -E: Preserve the environment. + // -n: Non-interactive. Exit with an error instead of prompting. + // -u: Which user to sudo to. + + return csprintf('%s -E -n -u %s -- %C', $sudo, $user, $command); + } + }