1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-09 16:32:39 +01:00

Add an SSH access log

Summary: Ref T4107. Ref T4189. This implements an SSH access log, similar to the HTTP access log.

Test Plan:
  [Thu, 05 Dec 2013 13:45:41 -0800]	77841	orbital	::1	dweller	epriestley	epriestley	git-receive-pack	/diffusion/POEMS/	0	324765	402	232
  [Thu, 05 Dec 2013 13:45:48 -0800]	77860	orbital	::1	dweller	epriestley	epriestley	git-receive-pack	/diffusion/POEMS/	0	325634	402	232

Reviewers: btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T4107, T4189

Differential Revision: https://secure.phabricator.com/D7719
This commit is contained in:
epriestley 2013-12-05 17:00:48 -08:00
parent 39b384041f
commit 5ca84589bd
5 changed files with 177 additions and 31 deletions

View file

@ -1,9 +1,13 @@
#!/usr/bin/env php
<?php
$ssh_start_time = microtime(true);
$root = dirname(dirname(dirname(__FILE__)));
require_once $root.'/scripts/__init_script__.php';
$ssh_log = PhabricatorSSHLog::getLog();
// First, figure out the authenticated user.
$args = new PhutilArgumentParser($argv);
$args->setTagline('receive SSH requests');
@ -38,6 +42,12 @@ try {
throw new Exception("Invalid username.");
}
$ssh_log->setData(
array(
'u' => $user->getUsername(),
'P' => $user->getPHID(),
));
if (!$user->isUserActivated()) {
throw new Exception(pht("Your account is not activated."));
}
@ -54,6 +64,15 @@ try {
if (!$original_argv) {
throw new Exception("No interactive logins.");
}
$ssh_log->setData(
array(
'C' => $original_argv[0],
'U' => phutil_utf8_shorten(
implode(' ', array_slice($original_argv, 1)),
128),
));
$command = head($original_argv);
array_unshift($original_argv, 'phabricator-ssh-exec');
@ -98,12 +117,35 @@ try {
$workflow->setIOChannel($metrics_channel);
$workflow->setErrorChannel($error_channel);
$err = $workflow->execute($original_args);
$rethrow = null;
try {
$err = $workflow->execute($original_args);
$metrics_channel->flush();
$error_channel->flush();
} catch (Exception $ex) {
$rethrow = $ex;
}
$metrics_channel->flush();
$error_channel->flush();
// Always write this if we got as far as building a metrics channel.
$ssh_log->setData(
array(
'i' => $metrics_channel->getBytesRead(),
'o' => $metrics_channel->getBytesWritten(),
));
if ($rethrow) {
throw $ex;
}
} catch (Exception $ex) {
fwrite(STDERR, "phabricator-ssh-exec: ".$ex->getMessage()."\n");
exit(1);
$err = 1;
}
$ssh_log->setData(
array(
'c' => $err,
'T' => (int)(1000000 * (microtime(true) - $ssh_start_time)),
));
exit($err);

View file

@ -996,7 +996,7 @@ phutil_register_library_map(array(
'Phabricator404Controller' => 'applications/base/controller/Phabricator404Controller.php',
'PhabricatorAWSConfigOptions' => 'applications/config/option/PhabricatorAWSConfigOptions.php',
'PhabricatorAccessControlTestCase' => 'applications/base/controller/__tests__/PhabricatorAccessControlTestCase.php',
'PhabricatorAccessLog' => 'infrastructure/PhabricatorAccessLog.php',
'PhabricatorAccessLog' => 'infrastructure/log/PhabricatorAccessLog.php',
'PhabricatorAccessLogConfigOptions' => 'applications/config/option/PhabricatorAccessLogConfigOptions.php',
'PhabricatorActionHeaderExample' => 'applications/uiexample/examples/PhabricatorActionHeaderExample.php',
'PhabricatorActionHeaderView' => 'view/layout/PhabricatorActionHeaderView.php',
@ -1811,6 +1811,7 @@ phutil_register_library_map(array(
'PhabricatorRepositoryVCSPassword' => 'applications/repository/storage/PhabricatorRepositoryVCSPassword.php',
'PhabricatorS3FileStorageEngine' => 'applications/files/engine/PhabricatorS3FileStorageEngine.php',
'PhabricatorSQLPatchList' => 'infrastructure/storage/patch/PhabricatorSQLPatchList.php',
'PhabricatorSSHLog' => 'infrastructure/log/PhabricatorSSHLog.php',
'PhabricatorSSHPassthruCommand' => 'infrastructure/ssh/PhabricatorSSHPassthruCommand.php',
'PhabricatorSSHWorkflow' => 'infrastructure/ssh/PhabricatorSSHWorkflow.php',
'PhabricatorSavedQuery' => 'applications/search/storage/PhabricatorSavedQuery.php',
@ -4337,6 +4338,7 @@ phutil_register_library_map(array(
'PhabricatorRepositoryTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhabricatorRepositoryVCSPassword' => 'PhabricatorRepositoryDAO',
'PhabricatorS3FileStorageEngine' => 'PhabricatorFileStorageEngine',
'PhabricatorSSHLog' => 'Phobject',
'PhabricatorSSHPassthruCommand' => 'Phobject',
'PhabricatorSSHWorkflow' => 'PhutilArgumentWorkflow',
'PhabricatorSavedQuery' =>

View file

@ -4,48 +4,56 @@ final class PhabricatorAccessLogConfigOptions
extends PhabricatorApplicationConfigOptions {
public function getName() {
return pht("Access Log");
return pht("Access Logs");
}
public function getDescription() {
return pht("Configure the access log, which logs all requests.");
return pht("Configure the access logs, which log HTTP/SSH requests.");
}
public function getOptions() {
$map = array(
'c' => pht("The HTTP response code."),
'C' => pht("The controller which handled the request."),
$common_map = array(
'C' => pht("The controller or workflow which handled the request."),
'c' => pht("The HTTP response code or process exit code."),
'D' => pht("The request date."),
'e' => pht("Epoch timestamp."),
'h' => pht("The webserver's host name."),
'p' => pht("The PID of the server process."),
'R' => pht("The HTTP referrer."),
'r' => pht("The remote IP."),
'T' => pht("The request duration, in microseconds."),
'U' => pht("The request path."),
'U' => pht("The request path, or request target."),
'm' => pht("For conduit, the Conduit method which was invoked."),
'u' => pht("The logged-in username, if one is logged in."),
'P' => pht("The logged-in user PHID, if one is logged in."),
'M' => pht("The HTTP method."),
'm' => pht("For conduit, the Conduit method which was invoked."),
'i' => pht("Request input, in bytes."),
'o' => pht("Request output, in bytes."),
);
$fdesc = pht("Format for the access log. Available variables are:");
$fdesc .= "\n\n";
foreach ($map as $key => $desc) {
$fdesc .= " - %".$key." ".$desc."\n";
}
$fdesc .= "\n";
$fdesc .= pht(
"If a variable isn't available (for example, %%m appears in the file ".
"format but the request is not a Conduit request), it will be rendered ".
"as '-'");
$fdesc .= "\n\n";
$fdesc .= pht(
"Note that the default format is subject to change in the future, so ".
"if you rely on the log's format, specify it explicitly.");
$http_map = $common_map + array(
'R' => pht("The HTTP referrer."),
'M' => pht("The HTTP method."),
);
$ssh_map = $common_map + array(
's' => pht("The system user."),
'S' => pht("The system sudo user."),
);
$http_desc = pht(
"Format for the HTTP access log. Use {{log.access.path}} to set the ".
"path. Available variables are:");
$http_desc .= "\n\n";
$http_desc .= $this->renderMapHelp($http_map);
$ssh_desc = pht(
"Format for the SSH access log. Use {{log.ssh.path}} to set the ".
"path. Available variables are:");
$ssh_desc .= "\n\n";
$ssh_desc .= $this->renderMapHelp($ssh_map);
return array(
$this->newOption('log.access.path', 'string', null)
->setLocked(true)
->setSummary(pht("Access log location."))
->setDescription(
pht(
@ -57,19 +65,61 @@ final class PhabricatorAccessLogConfigOptions
"If not set, no log will be written."))
->addExample(
null,
pht('Disable access log'))
pht('Disable access log.'))
->addExample(
'/var/log/phabricator/access.log',
pht('Write access log here')),
pht('Write access log here.')),
$this->newOption(
'log.access.format',
// NOTE: This is 'wild' intead of 'string' so "\t" and such can be
// specified.
'wild',
"[%D]\t%p\t%h\t%r\t%u\t%C\t%m\t%U\t%R\t%c\t%T")
->setLocked(true)
->setSummary(pht("Access log format."))
->setDescription($fdesc),
->setDescription($http_desc),
$this->newOption('log.ssh.path', 'string', null)
->setLocked(true)
->setSummary(pht("SSH log location."))
->setDescription(
pht(
"To enable the Phabricator SSH log, specify a path. The ".
"access log can provide more detailed information about SSH ".
"access than a normal SSH log (for instance, it can show ".
"logged-in users, commands, and other application data).\n\n".
"If not set, no log will be written."))
->addExample(
null,
pht('Disable SSH log.'))
->addExample(
'/var/log/phabricator/ssh.log',
pht('Write SSH log here.')),
$this->newOption(
'log.ssh.format',
'wild',
"[%D]\t%p\t%h\t%r\t%s\t%S\t%u\t%C\t%U\t%c\t%T\t%i\t%o")
->setLocked(true)
->setSummary(pht("SSH log format."))
->setDescription($ssh_desc),
);
}
private function renderMapHelp(array $map) {
$desc = '';
foreach ($map as $key => $kdesc) {
$desc .= " - `%".$key."` ".$kdesc."\n";
}
$desc .= "\n";
$desc .= pht(
"If a variable isn't available (for example, %%m appears in the file ".
"format but the request is not a Conduit request), it will be rendered ".
"as '-'");
$desc .= "\n\n";
$desc .= pht(
"Note that the default format is subject to change in the future, so ".
"if you rely on the log's format, specify it explicitly.");
return $desc;
}
}

View file

@ -0,0 +1,52 @@
<?php
final class PhabricatorSSHLog extends Phobject {
static $log;
public static function getLog() {
if (!self::$log) {
$path = PhabricatorEnv::getEnvConfig('log.ssh.path');
$format = PhabricatorEnv::getEnvConfig('log.ssh.format');
$format = nonempty(
$format,
"[%D]\t%p\t%h\t%r\t%s\t%S\t%u\t%C\t%U\t%c\t%T\t%i\t%o");
// NOTE: Path may be null. We still create the log, it just won't write
// anywhere.
$data = array(
'D' => date('r'),
'h' => php_uname('n'),
'p' => getmypid(),
'e' => time(),
);
$sudo_user = PhabricatorEnv::getEnvConfig('phd.user');
if (strlen($sudo_user)) {
$data['S'] = $sudo_user;
}
if (function_exists('posix_geteuid')) {
$system_uid = posix_geteuid();
$system_info = posix_getpwuid($system_uid);
$data['s'] = idx($system_info, 'name');
}
$client = getenv('SSH_CLIENT');
if (strlen($client)) {
$remote_address = head(explode(' ', $client));
$data['r'] = $remote_address;
}
$log = id(new PhutilDeferredLog($path, $format))
->setFailQuietly(true)
->setData($data);
self::$log = $log;
}
return self::$log;
}
}