1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-27 07:50:57 +01:00
phorge-phorge/scripts/ssh/ssh-exec.php
epriestley 888b3839e7 Prepare to route VCS connections through SSH
Summary:
Fixes T2229. This sets the stage for a patch similar to D7417, but for SSH. In particular, SSH 6.2 introduced an `AuthorizedKeysCommand` directive, which lets us do this in a mostly-reasonable way without needing users to patch sshd (if they have a recent enough version, at least).

The way the `AuthorizedKeysCommand` works is that it gets run and produces an `authorized_keys`-style file fragment. This isn't ideal, because we have to dump every key into the result, but should be fine for most installs. The earlier patch against `sshd` passes the public key itself, which allows the script to just look up the key. We might use this eventually, since it can scale much better, so I haven't removed it.

Generally, auth is split into two scripts now which mostly do the same thing:

  - `ssh-auth` is the AuthorizedKeysCommand auth, which takes nothing and dumps the whole keyfile.
  - `ssh-auth-key` is the slightly cleaner and more scalable (but patch-dependent) version, which takes the public key and dumps only matching options.

I also reworked the argument parsing to be a bit more sane.

Test Plan:
This is somewhat-intentionally a bit obtuse since I don't really want anyone using it yet, but basically:

  - Copy `phabricator-ssh-hook.sh` to somewhere like `/usr/libexec/openssh/`, chown it `root` and chmod it `500`.
    - This script should probably also do a username check in the future.
  - Create a copy of `sshd_config` and fix the paths/etc. Point the KeyScript at your copy of the hook.
  - Start a copy of sshd (6.2 or newer) with `-f <your config file>` and maybe `-d -d -d` to foreground and debug.
  - Run `ssh -p 2222 localhost` or similar.

Specifically, I did this setup and then ran a bunch of commands like:

  - `ssh host` (denied, no command)
  - `ssh host ls` (denied, not supported)
  - `echo '{}' | ssh host conduit conduit.ping` (works)

Reviewers: btrahan

Reviewed By: btrahan

CC: hach-que, aran

Maniphest Tasks: T2229, T2230

Differential Revision: https://secure.phabricator.com/D7419
2013-10-29 15:32:40 -07:00

96 lines
2.3 KiB
PHP
Executable file

#!/usr/bin/env php
<?php
$root = dirname(dirname(dirname(__FILE__)));
require_once $root.'/scripts/__init_script__.php';
// First, figure out the authenticated user.
$args = new PhutilArgumentParser($argv);
$args->setTagline('receive SSH requests');
$args->setSynopsis(<<<EOSYNOPSIS
**ssh-exec** --phabricator-ssh-user __user__ [--ssh-command __commmand__]
Receive SSH requests.
EOSYNOPSIS
);
$args->parse(
array(
array(
'name' => 'phabricator-ssh-user',
'param' => 'username',
),
array(
'name' => 'ssh-command',
'param' => 'command',
),
));
try {
$user_name = $args->getArg('phabricator-ssh-user');
if (!strlen($user_name)) {
throw new Exception("No username.");
}
$user = id(new PhabricatorUser())->loadOneWhere(
'userName = %s',
$user_name);
if (!$user) {
throw new Exception("Invalid username.");
}
if ($user->getIsDisabled()) {
throw new Exception("You have been exiled.");
}
if ($args->getArg('ssh-command')) {
$original_command = $args->getArg('ssh-command');
} else {
$original_command = getenv('SSH_ORIGINAL_COMMAND');
}
// Now, rebuild the original command.
$original_argv = id(new PhutilShellLexer())
->splitArguments($original_command);
if (!$original_argv) {
throw new Exception("No interactive logins.");
}
$command = head($original_argv);
array_unshift($original_argv, 'phabricator-ssh-exec');
$original_args = new PhutilArgumentParser($original_argv);
$workflows = array(
new ConduitSSHWorkflow(),
);
$workflow_names = mpull($workflows, 'getName', 'getName');
if (empty($workflow_names[$command])) {
throw new Exception("Invalid command.");
}
$workflow = $original_args->parseWorkflows($workflows);
$workflow->setUser($user);
$sock_stdin = fopen('php://stdin', 'r');
if (!$sock_stdin) {
throw new Exception("Unable to open stdin.");
}
$sock_stdout = fopen('php://stdout', 'w');
if (!$sock_stdout) {
throw new Exception("Unable to open stdout.");
}
$socket_channel = new PhutilSocketChannel(
$sock_stdin,
$sock_stdout);
$metrics_channel = new PhutilMetricsChannel($socket_channel);
$workflow->setIOChannel($metrics_channel);
$err = $workflow->execute($original_args);
$metrics_channel->flush();
} catch (Exception $ex) {
echo "phabricator-ssh-exec: ".$ex->getMessage()."\n";
exit(1);
}