@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
at recaptime-dev/main 161 lines 4.3 kB view raw
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);