Skip to content

Commit c21a71f

Browse files
author
epriestley
committed
Cache generation of the SSH authentication keyfile for sshd
Summary: Ref T11469. This isn't directly related, but has been on my radar for a while: building SSH keyfiles (particular for installs with a lot of keys, like ours) can be fairly slow. At least one cluster instance is making multiple clone requests per second. While that should probably be rate limited separately, caching this should mitigate the impact of these requests. This is pretty straightforward to cache since it's exactly the same every time, and only changes when users modify SSH keys (which is rare). Test Plan: - Ran `bin/auth-ssh`, saw authfile generate. - Ran it again, saw it read from cache. - Changed an SSH key. - Ran it again, saw it regenerate. Reviewers: chad Reviewed By: chad Maniphest Tasks: T11469 Differential Revision: https://secure.phabricator.com/D16744
1 parent eb80f3f commit c21a71f

File tree

5 files changed

+100
-57
lines changed

5 files changed

+100
-57
lines changed

scripts/ssh/ssh-auth.php

Lines changed: 66 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -4,74 +4,84 @@
44
$root = dirname(dirname(dirname(__FILE__)));
55
require_once $root.'/scripts/__init_script__.php';
66

7-
$keys = id(new PhabricatorAuthSSHKeyQuery())
8-
->setViewer(PhabricatorUser::getOmnipotentUser())
9-
->withIsActive(true)
10-
->execute();
11-
12-
if (!$keys) {
13-
echo pht('No keys found.')."\n";
14-
exit(1);
15-
}
7+
$cache = PhabricatorCaches::getMutableCache();
8+
$authfile_key = PhabricatorAuthSSHKeyQuery::AUTHFILE_CACHEKEY;
9+
$authfile = $cache->getKey($authfile_key);
10+
11+
if ($authfile === null) {
12+
$keys = id(new PhabricatorAuthSSHKeyQuery())
13+
->setViewer(PhabricatorUser::getOmnipotentUser())
14+
->withIsActive(true)
15+
->execute();
16+
17+
if (!$keys) {
18+
echo pht('No keys found.')."\n";
19+
exit(1);
20+
}
1621

17-
$bin = $root.'/bin/ssh-exec';
18-
foreach ($keys as $ssh_key) {
19-
$key_argv = array();
20-
$object = $ssh_key->getObject();
21-
if ($object instanceof PhabricatorUser) {
22-
$key_argv[] = '--phabricator-ssh-user';
23-
$key_argv[] = $object->getUsername();
24-
} else if ($object instanceof AlmanacDevice) {
25-
if (!$ssh_key->getIsTrusted()) {
26-
// If this key is not a trusted device key, don't allow SSH
27-
// authentication.
22+
$bin = $root.'/bin/ssh-exec';
23+
foreach ($keys as $ssh_key) {
24+
$key_argv = array();
25+
$object = $ssh_key->getObject();
26+
if ($object instanceof PhabricatorUser) {
27+
$key_argv[] = '--phabricator-ssh-user';
28+
$key_argv[] = $object->getUsername();
29+
} else if ($object instanceof AlmanacDevice) {
30+
if (!$ssh_key->getIsTrusted()) {
31+
// If this key is not a trusted device key, don't allow SSH
32+
// authentication.
33+
continue;
34+
}
35+
$key_argv[] = '--phabricator-ssh-device';
36+
$key_argv[] = $object->getName();
37+
} else {
38+
// We don't know what sort of key this is; don't permit SSH auth.
2839
continue;
2940
}
30-
$key_argv[] = '--phabricator-ssh-device';
31-
$key_argv[] = $object->getName();
32-
} else {
33-
// We don't know what sort of key this is; don't permit SSH auth.
34-
continue;
35-
}
3641

37-
$key_argv[] = '--phabricator-ssh-key';
38-
$key_argv[] = $ssh_key->getID();
42+
$key_argv[] = '--phabricator-ssh-key';
43+
$key_argv[] = $ssh_key->getID();
3944

40-
$cmd = csprintf('%s %Ls', $bin, $key_argv);
45+
$cmd = csprintf('%s %Ls', $bin, $key_argv);
4146

42-
$instance = PhabricatorEnv::getEnvConfig('cluster.instance');
43-
if (strlen($instance)) {
44-
$cmd = csprintf('PHABRICATOR_INSTANCE=%s %C', $instance, $cmd);
45-
}
47+
$instance = PhabricatorEnv::getEnvConfig('cluster.instance');
48+
if (strlen($instance)) {
49+
$cmd = csprintf('PHABRICATOR_INSTANCE=%s %C', $instance, $cmd);
50+
}
4651

47-
// This is additional escaping for the SSH 'command="..."' string.
48-
$cmd = addcslashes($cmd, '"\\');
52+
// This is additional escaping for the SSH 'command="..."' string.
53+
$cmd = addcslashes($cmd, '"\\');
4954

50-
// Strip out newlines and other nonsense from the key type and key body.
55+
// Strip out newlines and other nonsense from the key type and key body.
5156

52-
$type = $ssh_key->getKeyType();
53-
$type = preg_replace('@[\x00-\x20]+@', '', $type);
54-
if (!strlen($type)) {
55-
continue;
56-
}
57+
$type = $ssh_key->getKeyType();
58+
$type = preg_replace('@[\x00-\x20]+@', '', $type);
59+
if (!strlen($type)) {
60+
continue;
61+
}
5762

58-
$key = $ssh_key->getKeyBody();
59-
$key = preg_replace('@[\x00-\x20]+@', '', $key);
60-
if (!strlen($key)) {
61-
continue;
62-
}
63+
$key = $ssh_key->getKeyBody();
64+
$key = preg_replace('@[\x00-\x20]+@', '', $key);
65+
if (!strlen($key)) {
66+
continue;
67+
}
68+
69+
$options = array(
70+
'command="'.$cmd.'"',
71+
'no-port-forwarding',
72+
'no-X11-forwarding',
73+
'no-agent-forwarding',
74+
'no-pty',
75+
);
76+
$options = implode(',', $options);
6377

64-
$options = array(
65-
'command="'.$cmd.'"',
66-
'no-port-forwarding',
67-
'no-X11-forwarding',
68-
'no-agent-forwarding',
69-
'no-pty',
70-
);
71-
$options = implode(',', $options);
78+
$lines[] = $options.' '.$type.' '.$key."\n";
79+
}
7280

73-
$lines[] = $options.' '.$type.' '.$key."\n";
81+
$authfile = implode('', $lines);
82+
$ttl = phutil_units('24 hours in seconds');
83+
$cache->setKey($authfile_key, $authfile, $ttl);
7484
}
7585

76-
echo implode('', $lines);
86+
echo $authfile;
7787
exit(0);

src/applications/auth/editor/PhabricatorAuthSSHKeyEditor.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,20 @@ protected function getMailThreadID(PhabricatorLiskDAO $object) {
191191
return 'ssh-key-'.$object->getPHID();
192192
}
193193

194+
protected function applyFinalEffects(
195+
PhabricatorLiskDAO $object,
196+
array $xactions) {
197+
198+
// After making any change to an SSH key, drop the authfile cache so it
199+
// is regenerated the next time anyone authenticates.
200+
$cache = PhabricatorCaches::getMutableCache();
201+
$authfile_key = PhabricatorAuthSSHKeyQuery::AUTHFILE_CACHEKEY;
202+
$cache->deleteKey($authfile_key);
203+
204+
return $xactions;
205+
}
206+
207+
194208
protected function getMailTo(PhabricatorLiskDAO $object) {
195209
return $object->getObject()->getSSHKeyNotifyPHIDs();
196210
}

src/applications/auth/query/PhabricatorAuthSSHKeyQuery.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
final class PhabricatorAuthSSHKeyQuery
44
extends PhabricatorCursorPagedPolicyAwareQuery {
55

6+
const AUTHFILE_CACHEKEY = 'ssh.authfile';
7+
68
private $ids;
79
private $phids;
810
private $objectPHIDs;

src/applications/cache/PhabricatorCaches.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,23 @@ private static function buildImmutableCaches() {
9999
return $caches;
100100
}
101101

102+
public static function getMutableCache() {
103+
static $cache;
104+
if (!$cache) {
105+
$caches = self::buildMutableCaches();
106+
$cache = self::newStackFromCaches($caches);
107+
}
108+
return $cache;
109+
}
110+
111+
private static function buildMutableCaches() {
112+
$caches = array();
113+
114+
$caches[] = new PhabricatorKeyValueDatabaseCache();
115+
116+
return $caches;
117+
}
118+
102119

103120
/* -( Repository Graph Cache )--------------------------------------------- */
104121

src/applications/cache/PhabricatorKeyValueDatabaseCache.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ public function deleteKeys(array $keys) {
9898
$this->establishConnection('w'),
9999
'DELETE FROM %T WHERE cacheKeyHash IN (%Ls)',
100100
$this->getTableName(),
101-
$keys);
101+
$map);
102102
}
103103

104104
return $this;

0 commit comments

Comments
 (0)