@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator

Start phd daemons as the correctly configured user and refuse otherwise

Summary:
Fixes T5196
If no phd.user is configured the behaviour is unchanged besides printing a warning when run as root (Usually i would add an exit(1) here but that would break existing installs who do that).
If phd.user is set and the current user is root it will run the daemon as: su USER -c "command" (I'm not sure if this works for every platform needed)
Otherwise it will refuse to start if configured and current user mismatch.

Test Plan: Stopped & Started phd daemon with various users and different phd.user settings including root

Reviewers: #blessed_reviewers, epriestley

Reviewed By: #blessed_reviewers, epriestley

Subscribers: vinzent, epriestley

Maniphest Tasks: T5196

Differential Revision: https://secure.phabricator.com/D11036

authored by

Fabian Stelzer and committed by
epriestley
2fedb6f9 6132d801

+115 -6
+2
resources/sql/autopatches/20141223.daemonloguser.sql
··· 1 + ALTER TABLE {$NAMESPACE}_daemon.daemon_log 2 + ADD runningAsUser VARCHAR(255) COLLATE {$COLLATE_TEXT};
+35
src/applications/config/check/PhabricatorSetupCheckDaemons.php
··· 43 43 ->addCommand('phabricator/ $ ./bin/phd start'); 44 44 } 45 45 46 + $phd_user = PhabricatorEnv::getEnvConfig('phd.user'); 46 47 $environment_hash = PhabricatorEnv::calculateEnvironmentHash(); 47 48 $all_daemons = id(new PhabricatorDaemonLogQuery()) 48 49 ->setViewer(PhabricatorUser::getOmnipotentUser()) 49 50 ->withStatus(PhabricatorDaemonLogQuery::STATUS_ALIVE) 50 51 ->execute(); 51 52 foreach ($all_daemons as $daemon) { 53 + 54 + if ($phd_user) { 55 + if ($daemon->getRunningAsUser() != $phd_user) { 56 + $doc_href = PhabricatorEnv::getDocLink( 57 + 'Managing Daemons with phd'); 58 + 59 + $summary = pht( 60 + 'At least one daemon is currently running as a different '. 61 + 'user than configured in the Phabricator phd.user setting'); 62 + 63 + $message = pht( 64 + 'A daemon is running as user %s while the Phabricator config '. 65 + 'specifies phd.user to be %s.'. 66 + "\n\n". 67 + 'Either adjust phd.user to match %s or start '. 68 + 'the daemons as the correct user. '. 69 + "\n\n". 70 + 'phd Daemons will try to '. 71 + 'use sudo to start as the configured user. '. 72 + 'Make sure that the user who starts phd has the correct '. 73 + 'sudo permissions to start phd daemons as %s', 74 + phutil_tag('tt', array(), $daemon->getRunningAsUser()), 75 + phutil_tag('tt', array(), $phd_user), 76 + phutil_tag('tt', array(), $daemon->getRunningAsUser()), 77 + phutil_tag('tt', array(), $phd_user)); 78 + 79 + $this->newIssue('daemons.run-as-different-user') 80 + ->setName(pht('Daemons are running as the wrong user')) 81 + ->setSummary($summary) 82 + ->setMessage($message) 83 + ->addCommand('phabricator/ $ ./bin/phd restart'); 84 + } 85 + } 86 + 52 87 if ($daemon->getEnvHash() != $environment_hash) { 53 88 $doc_href = PhabricatorEnv::getDocLink( 54 89 'Managing Daemons with phd');
+1
src/applications/daemon/controller/PhabricatorDaemonLogViewController.php
··· 163 163 $view->addProperty(pht('Daemon Class'), $daemon->getDaemon()); 164 164 $view->addProperty(pht('Host'), $daemon->getHost()); 165 165 $view->addProperty(pht('PID'), $daemon->getPID()); 166 + $view->addProperty(pht('Running as'), $daemon->getRunningAsUser()); 166 167 $view->addProperty(pht('Started'), phabricator_datetime($c_epoch, $viewer)); 167 168 $view->addProperty( 168 169 pht('Seen'),
+2
src/applications/daemon/event/PhabricatorDaemonEventListener.php
··· 34 34 35 35 private function handleLaunchEvent(PhutilEvent $event) { 36 36 $id = $event->getValue('id'); 37 + $current_user = posix_getpwuid(posix_geteuid()); 37 38 38 39 $daemon = id(new PhabricatorDaemonLog()) 39 40 ->setDaemon($event->getValue('daemonClass')) 40 41 ->setHost(php_uname('n')) 41 42 ->setPID(getmypid()) 43 + ->setRunningAsUser($current_user['name']) 42 44 ->setEnvHash(PhabricatorEnv::calculateEnvironmentHash()) 43 45 ->setStatus(PhabricatorDaemonLog::STATUS_RUNNING) 44 46 ->setArgv($event->getValue('argv'))
+11 -1
src/applications/daemon/management/PhabricatorDaemonManagementDebugWorkflow.php
··· 21 21 'name' => 'argv', 22 22 'wildcard' => true, 23 23 ), 24 + array( 25 + 'name' => 'as-current-user', 26 + 'help' => 'Run the daemon as the current user '. 27 + 'instead of the configured phd.user', 28 + ), 24 29 )); 25 30 } 26 31 27 32 public function execute(PhutilArgumentParser $args) { 28 33 $argv = $args->getArg('argv'); 34 + $run_as_current_user = $args->getArg('as-current-user'); 29 35 30 36 if (!$argv) { 31 37 throw new PhutilArgumentUsageException( ··· 33 39 } 34 40 35 41 $daemon_class = array_shift($argv); 36 - return $this->launchDaemon($daemon_class, $argv, $is_debug = true); 42 + return $this->launchDaemon( 43 + $daemon_class, 44 + $argv, 45 + $is_debug = true, 46 + $run_as_current_user); 37 47 } 38 48 39 49 }
+62 -5
src/applications/daemon/management/PhabricatorDaemonManagementWorkflow.php
··· 3 3 abstract class PhabricatorDaemonManagementWorkflow 4 4 extends PhabricatorManagementWorkflow { 5 5 6 + private $runDaemonsAsUser = null; 7 + 6 8 protected final function loadAvailableDaemonClasses() { 7 9 $loader = new PhutilSymbolLoader(); 8 10 return $loader ··· 103 105 return head($match); 104 106 } 105 107 106 - protected final function launchDaemon($class, array $argv, $debug) { 108 + protected final function launchDaemon( 109 + $class, 110 + array $argv, 111 + $debug, 112 + $run_as_current_user = false) { 113 + 107 114 $daemon = $this->findDaemonClass($class); 108 115 $console = PhutilConsole::getConsole(); 109 116 117 + if (!$run_as_current_user) { 118 + // Check if the script is started as the correct user 119 + $phd_user = PhabricatorEnv::getEnvConfig('phd.user'); 120 + $current_user = posix_getpwuid(posix_geteuid()); 121 + $current_user = $current_user['name']; 122 + if ($phd_user && $phd_user != $current_user) { 123 + if ($debug) { 124 + throw new PhutilArgumentUsageException(pht( 125 + 'You are trying to run a daemon as a nonstandard user, '. 126 + 'and `phd` was not able to `sudo` to the correct user. '."\n". 127 + 'Phabricator is configured to run daemons as "%s", '. 128 + 'but the current user is "%s". '."\n". 129 + 'Use `sudo` to run as a different user, pass `--as-current-user` '. 130 + 'to ignore this warning, or edit `phd.user` '. 131 + 'to change the configuration.', $phd_user, $current_user)); 132 + } else { 133 + $this->runDaemonsAsUser = $phd_user; 134 + $console->writeOut(pht('Starting daemons as %s', $phd_user)."\n"); 135 + } 136 + } 137 + } 138 + 110 139 if ($debug) { 111 140 if ($argv) { 112 141 $console->writeOut( ··· 187 216 188 217 phutil_passthru('(cd %s && exec %C)', $daemon_script_dir, $command); 189 218 } else { 190 - $future = new ExecFuture('exec %C', $command); 191 - // Play games to keep 'ps' looking reasonable. 192 - $future->setCWD($daemon_script_dir); 193 - $future->resolvex(); 219 + try { 220 + $this->executeDaemonLaunchCommand( 221 + $command, 222 + $daemon_script_dir, 223 + $this->runDaemonsAsUser); 224 + } catch (CommandException $e) { 225 + // Retry without sudo 226 + $console->writeOut(pht( 227 + "sudo command failed. Starting daemon as current user\n")); 228 + $this->executeDaemonLaunchCommand( 229 + $command, 230 + $daemon_script_dir); 231 + } 232 + } 233 + } 234 + 235 + private function executeDaemonLaunchCommand( 236 + $command, 237 + $daemon_script_dir, 238 + $run_as_user = null) { 239 + 240 + if ($run_as_user) { 241 + // If anything else besides sudo should be 242 + // supported then insert it here (runuser, su, ...) 243 + $command = csprintf( 244 + 'sudo -En -u %s -- %C', 245 + $run_as_user, 246 + $command); 194 247 } 248 + $future = new ExecFuture('exec %C', $command); 249 + // Play games to keep 'ps' looking reasonable. 250 + $future->setCWD($daemon_script_dir); 251 + $future->resolvex(); 195 252 } 196 253 197 254 public static function ignoreSignal($signo) {
+2
src/applications/daemon/storage/PhabricatorDaemonLog.php
··· 13 13 protected $daemon; 14 14 protected $host; 15 15 protected $pid; 16 + protected $runningAsUser; 16 17 protected $argv; 17 18 protected $explicitArgv = array(); 18 19 protected $envHash; ··· 28 29 'daemon' => 'text255', 29 30 'host' => 'text255', 30 31 'pid' => 'uint32', 32 + 'runningAsUser' => 'text255?', 31 33 'envHash' => 'bytes40', 32 34 'status' => 'text8', 33 35 ),