@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$root = dirname(dirname(dirname(__FILE__)));
5require_once $root.'/scripts/init/init-script.php';
6
7$error_log = id(new PhutilErrorLog())
8 ->setLogName(pht('SSH Error Log'))
9 ->setLogPath(PhabricatorEnv::getEnvConfig('log.ssh-error.path'))
10 ->activateLog();
11
12// TODO: For now, this is using "parseParital()", not "parse()". This allows
13// the script to accept (and ignore) additional arguments. This preserves
14// backward compatibility until installs have time to migrate to the new
15// syntax.
16
17$args = id(new PhutilArgumentParser($argv))
18 ->parsePartial(
19 array(
20 array(
21 'name' => 'sshd-key',
22 'param' => 'k',
23 'help' => pht(
24 'Accepts the "%%k" parameter from "AuthorizedKeysCommand".'),
25 ),
26 ));
27
28$sshd_key = $args->getArg('sshd-key');
29
30// NOTE: We are caching a datastructure rather than the flat key file because
31// the path on disk to "ssh-exec" is arbitrarily mutable at runtime. See T12397.
32
33$cache = PhabricatorCaches::getMutableCache();
34$authstruct_key = PhabricatorAuthSSHKeyQuery::AUTHSTRUCT_CACHEKEY;
35$authstruct_raw = $cache->getKey($authstruct_key);
36
37$authstruct = null;
38
39if (phutil_nonempty_string($authstruct_raw)) {
40 try {
41 $authstruct = phutil_json_decode($authstruct_raw);
42 } catch (Exception $ex) {
43 // Ignore any issues with the cached data; we'll just rebuild the
44 // structure below.
45 }
46}
47
48if ($authstruct === null) {
49 $keys = id(new PhabricatorAuthSSHKeyQuery())
50 ->setViewer(PhabricatorUser::getOmnipotentUser())
51 ->withIsActive(true)
52 ->execute();
53
54 if (!$keys) {
55 echo pht('No keys found.')."\n";
56 exit(1);
57 }
58
59 $key_list = array();
60 foreach ($keys as $ssh_key) {
61 $key_argv = array();
62 $object = $ssh_key->getObject();
63 if ($object instanceof PhabricatorUser) {
64 $key_argv[] = '--phabricator-ssh-user';
65 $key_argv[] = $object->getUsername();
66 } else if ($object instanceof AlmanacDevice) {
67 if (!$ssh_key->getIsTrusted()) {
68 // If this key is not a trusted device key, don't allow SSH
69 // authentication.
70 continue;
71 }
72 $key_argv[] = '--phabricator-ssh-device';
73 $key_argv[] = $object->getName();
74 } else {
75 // We don't know what sort of key this is; don't permit SSH auth.
76 continue;
77 }
78
79 $key_argv[] = '--phabricator-ssh-key';
80 $key_argv[] = $ssh_key->getID();
81
82 // Strip out newlines and other nonsense from the key type and key body.
83 $type = $ssh_key->getKeyType();
84 $type = preg_replace('@[\x00-\x20]+@', '', $type);
85 if (!phutil_nonempty_string($type)) {
86 continue;
87 }
88
89 $key = $ssh_key->getKeyBody();
90 $key = preg_replace('@[\x00-\x20]+@', '', $key);
91 if (!phutil_nonempty_string($key)) {
92 continue;
93 }
94
95 $key_list[] = array(
96 'argv' => $key_argv,
97 'type' => $type,
98 'key' => $key,
99 );
100 }
101
102 $authstruct = array(
103 'keys' => $key_list,
104 );
105
106 $authstruct_raw = phutil_json_encode($authstruct);
107 $ttl = phutil_units('24 hours in seconds');
108 $cache->setKey($authstruct_key, $authstruct_raw, $ttl);
109}
110
111// If we've received an "--sshd-key" argument and it matches some known key,
112// only emit that key. (For now, if the key doesn't match, we'll fall back to
113// emitting all keys.)
114if ($sshd_key !== null) {
115 $matches = array();
116 foreach ($authstruct['keys'] as $key => $key_struct) {
117 if ($key_struct['key'] === $sshd_key) {
118 $matches[$key] = $key_struct;
119 }
120 }
121
122 if ($matches) {
123 $authstruct['keys'] = $matches;
124 }
125}
126
127$bin = $root.'/bin/ssh-exec';
128$instance = PhabricatorEnv::getEnvConfig('cluster.instance');
129
130$lines = array();
131foreach ($authstruct['keys'] as $key_struct) {
132 $key_argv = $key_struct['argv'];
133 $key = $key_struct['key'];
134 $type = $key_struct['type'];
135
136 $cmd = csprintf('%s %Ls', $bin, $key_argv);
137
138 if (phutil_nonempty_string($instance)) {
139 $cmd = csprintf('PHABRICATOR_INSTANCE=%s %C', $instance, $cmd);
140 }
141
142 // This is additional escaping for the SSH 'command="..."' string.
143 $cmd = addcslashes($cmd, '"\\');
144
145 $options = array(
146 'command="'.$cmd.'"',
147 'no-port-forwarding',
148 'no-X11-forwarding',
149 'no-agent-forwarding',
150 'no-pty',
151 );
152 $options = implode(',', $options);
153
154 $lines[] = $options.' '.$type.' '.$key."\n";
155}
156
157$authfile = implode('', $lines);
158
159echo $authfile;
160
161exit(0);