@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
1#!/usr/bin/env php
2<?php
3
4// This is a wrapper script for Git, Mercurial, and Subversion. It primarily
5// serves to inject "-o StrictHostKeyChecking=no" into the SSH arguments.
6
7// In some cases, Subversion sends us SIGTERM. If we don't catch the signal and
8// react to it, we won't run object destructors by default and thus won't clean
9// up temporary files. Declare ticks so we can install a signal handler.
10if (function_exists('pcntl_async_signals')) {
11 pcntl_async_signals(true);
12} else {
13 declare(ticks = 1);
14}
15
16$root = dirname(dirname(dirname(__FILE__)));
17require_once $root.'/scripts/__init_script__.php';
18
19// Contrary to the documentation, Git may pass a "-p" flag. If it does, respect
20// it and move it before the "--" argument.
21$args = new PhutilArgumentParser($argv);
22$args->parsePartial(
23 array(
24 array(
25 'name' => 'port',
26 'short' => 'p',
27 'param' => pht('port'),
28 'help' => pht('Port number to connect to.'),
29 ),
30 array(
31 'name' => 'options',
32 'short' => 'o',
33 'param' => pht('options'),
34 'repeat' => true,
35 'help' => pht('SSH options.'),
36 ),
37 ));
38
39$unconsumed_argv = $args->getUnconsumedArgumentVector();
40
41if (function_exists('pcntl_signal')) {
42 pcntl_signal(SIGTERM, 'ssh_connect_signal');
43}
44
45function ssh_connect_signal($signo) {
46 // This is just letting destructors fire. In particular, we want to clean
47 // up any temporary files we wrote. See T10547.
48 exit(128 + $signo);
49}
50
51$pattern = array();
52$arguments = array();
53
54$pattern[] = 'ssh';
55
56$pattern[] = '-o';
57$pattern[] = 'StrictHostKeyChecking=no';
58
59// This prevents "known host" failures, and covers for issues where HOME is set
60// to something unusual.
61$pattern[] = '-o';
62$pattern[] = 'UserKnownHostsFile=/dev/null';
63
64$as_device = getenv('PHABRICATOR_AS_DEVICE');
65$credential_phid = getenv('PHABRICATOR_CREDENTIAL');
66
67if ($as_device) {
68 $device = AlmanacKeys::getLiveDevice();
69 if (!$device) {
70 throw new Exception(
71 pht(
72 'Attempting to create an SSH connection that authenticates with '.
73 'the current device, but this host is not configured as a cluster '.
74 'device.'));
75 }
76
77 if ($credential_phid) {
78 throw new Exception(
79 pht(
80 'Attempting to proxy an SSH connection that authenticates with '.
81 'both the current device and a specific credential. These options '.
82 'are mutually exclusive.'));
83 }
84}
85
86if ($credential_phid) {
87 $viewer = PhabricatorUser::getOmnipotentUser();
88 $key = PassphraseSSHKey::loadFromPHID($credential_phid, $viewer);
89
90 $pattern[] = '-l %P';
91 $arguments[] = $key->getUsernameEnvelope();
92 $pattern[] = '-i %P';
93 $arguments[] = $key->getKeyfileEnvelope();
94}
95
96if ($as_device) {
97 $pattern[] = '-l %R';
98 $arguments[] = AlmanacKeys::getClusterSSHUser();
99 $pattern[] = '-i %R';
100 $arguments[] = AlmanacKeys::getKeyPath('device.key');
101}
102
103// Subversion passes us a host in the form "domain.com:port", which is not
104// valid for normal SSH but which we can parse into a valid "-p" flag.
105
106$passthru_args = $unconsumed_argv;
107$host = array_shift($passthru_args);
108$parts = explode(':', $host, 2);
109$host = $parts[0];
110
111$port = $args->getArg('port');
112
113if (!$port) {
114 if (count($parts) == 2) {
115 $port = $parts[1];
116 }
117}
118
119if ($port) {
120 $pattern[] = '-p %d';
121 $arguments[] = $port;
122}
123
124$options = $args->getArg('options');
125$allowed_ssh_options = array('SendEnv=GIT_PROTOCOL');
126
127if (!empty($options)) {
128 foreach ($options as $option) {
129 if (array_search($option, $allowed_ssh_options) !== false) {
130 $pattern[] = '-o %s';
131 $arguments[] = $option;
132 } else {
133 throw new Exception(
134 pht(
135 'Disallowed ssh option "%s" given with "-o". '.
136 'Allowed options are: %s.',
137 $option,
138 implode(', ', $allowed_ssh_options)));
139 }
140 }
141}
142
143$pattern[] = '--';
144
145$pattern[] = '%s';
146$arguments[] = $host;
147
148foreach ($passthru_args as $passthru_arg) {
149 $pattern[] = '%s';
150 $arguments[] = $passthru_arg;
151}
152
153$pattern = implode(' ', $pattern);
154array_unshift($arguments, $pattern);
155
156$err = newv('PhutilExecPassthru', $arguments)
157 ->resolve();
158
159exit($err);