Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/almanac/management/AlmanacManagementRegisterWorkflow.php
13442 views
1
<?php
2
3
final class AlmanacManagementRegisterWorkflow
4
extends AlmanacManagementWorkflow {
5
6
protected function didConstruct() {
7
$this
8
->setName('register')
9
->setSynopsis(pht('Register this host as an Almanac device.'))
10
->setArguments(
11
array(
12
array(
13
'name' => 'device',
14
'param' => 'name',
15
'help' => pht('Almanac device name to register.'),
16
),
17
array(
18
'name' => 'private-key',
19
'param' => 'key',
20
'help' => pht('Path to a private key for the host.'),
21
),
22
array(
23
'name' => 'identify-as',
24
'param' => 'name',
25
'help' => pht(
26
'Specify an alternate host identity. This is an advanced '.
27
'feature which allows a pool of devices to share credentials.'),
28
),
29
array(
30
'name' => 'force',
31
'help' => pht(
32
'Register this host even if keys already exist on disk.'),
33
),
34
));
35
}
36
37
public function execute(PhutilArgumentParser $args) {
38
$viewer = $this->getViewer();
39
40
$device_name = $args->getArg('device');
41
if (!strlen($device_name)) {
42
throw new PhutilArgumentUsageException(
43
pht('Specify a device with --device.'));
44
}
45
46
$device = id(new AlmanacDeviceQuery())
47
->setViewer($viewer)
48
->withNames(array($device_name))
49
->executeOne();
50
if (!$device) {
51
throw new PhutilArgumentUsageException(
52
pht('No such device "%s" exists!', $device_name));
53
}
54
55
$identify_as = $args->getArg('identify-as');
56
57
$raw_device = $device_name;
58
if (strlen($identify_as)) {
59
$raw_device = $identify_as;
60
}
61
62
$identity_device = id(new AlmanacDeviceQuery())
63
->setViewer($viewer)
64
->withNames(array($raw_device))
65
->executeOne();
66
if (!$identity_device) {
67
throw new PhutilArgumentUsageException(
68
pht(
69
'No such device "%s" exists!', $raw_device));
70
}
71
72
$private_key_path = $args->getArg('private-key');
73
if (!strlen($private_key_path)) {
74
throw new PhutilArgumentUsageException(
75
pht('Specify a private key with --private-key.'));
76
}
77
78
if (!Filesystem::pathExists($private_key_path)) {
79
throw new PhutilArgumentUsageException(
80
pht('No private key exists at path "%s"!', $private_key_path));
81
}
82
83
$raw_private_key = Filesystem::readFile($private_key_path);
84
85
$phd_user = PhabricatorEnv::getEnvConfig('phd.user');
86
if (!$phd_user) {
87
throw new PhutilArgumentUsageException(
88
pht(
89
'Config option "phd.user" is not set. You must set this option '.
90
'so the private key can be stored with the correct permissions.'));
91
}
92
93
$tmp = new TempFile();
94
list($err) = exec_manual('chown %s %s', $phd_user, $tmp);
95
if ($err) {
96
throw new PhutilArgumentUsageException(
97
pht(
98
'Unable to change ownership of an identity file to daemon user '.
99
'"%s". Run this command as %s or root.',
100
$phd_user,
101
$phd_user));
102
}
103
104
$stored_public_path = AlmanacKeys::getKeyPath('device.pub');
105
$stored_private_path = AlmanacKeys::getKeyPath('device.key');
106
$stored_device_path = AlmanacKeys::getKeyPath('device.id');
107
108
if (!$args->getArg('force')) {
109
if (Filesystem::pathExists($stored_public_path)) {
110
throw new PhutilArgumentUsageException(
111
pht(
112
'This host already has a registered public key ("%s"). '.
113
'Remove this key before registering the host, or use '.
114
'--force to overwrite it.',
115
Filesystem::readablePath($stored_public_path)));
116
}
117
118
if (Filesystem::pathExists($stored_private_path)) {
119
throw new PhutilArgumentUsageException(
120
pht(
121
'This host already has a registered private key ("%s"). '.
122
'Remove this key before registering the host, or use '.
123
'--force to overwrite it.',
124
Filesystem::readablePath($stored_private_path)));
125
}
126
}
127
128
// NOTE: We're writing the private key here so we can change permissions
129
// on it without causing weird side effects to the file specified with
130
// the `--private-key` flag. The file needs to have restrictive permissions
131
// before `ssh-keygen` will willingly operate on it.
132
$tmp_private = new TempFile();
133
Filesystem::changePermissions($tmp_private, 0600);
134
execx('chown %s %s', $phd_user, $tmp_private);
135
Filesystem::writeFile($tmp_private, $raw_private_key);
136
137
list($raw_public_key) = execx('ssh-keygen -y -f %s', $tmp_private);
138
139
$key_object = PhabricatorAuthSSHPublicKey::newFromRawKey($raw_public_key);
140
141
$public_key = id(new PhabricatorAuthSSHKeyQuery())
142
->setViewer($this->getViewer())
143
->withKeys(array($key_object))
144
->withIsActive(true)
145
->executeOne();
146
147
if (!$public_key) {
148
throw new PhutilArgumentUsageException(
149
pht(
150
'The public key corresponding to the given private key is unknown. '.
151
'Associate the public key with an Almanac device in the web '.
152
'interface before registering hosts with it.'));
153
}
154
155
if ($public_key->getObjectPHID() !== $device->getPHID()) {
156
$public_phid = $public_key->getObjectPHID();
157
$public_handles = $viewer->loadHandles(array($public_phid));
158
$public_handle = $public_handles[$public_phid];
159
160
throw new PhutilArgumentUsageException(
161
pht(
162
'The public key corresponding to the given private key is already '.
163
'associated with an object ("%s") other than the specified '.
164
'device ("%s"). You can not use a single private key to identify '.
165
'multiple devices or users.',
166
$public_handle->getFullName(),
167
$device->getName()));
168
}
169
170
if (!$public_key->getIsTrusted()) {
171
throw new PhutilArgumentUsageException(
172
pht(
173
'The public key corresponding to the given private key is '.
174
'properly associated with the device, but is not yet trusted. '.
175
'Trust this key before registering devices with it.'));
176
}
177
178
echo tsprintf(
179
"%s\n",
180
pht('Installing public key...'));
181
182
$tmp_public = new TempFile();
183
Filesystem::changePermissions($tmp_public, 0600);
184
execx('chown %s %s', $phd_user, $tmp_public);
185
Filesystem::writeFile($tmp_public, $raw_public_key);
186
execx('mv -f %s %s', $tmp_public, $stored_public_path);
187
188
echo tsprintf(
189
"%s\n",
190
pht('Installing private key...'));
191
execx('mv -f %s %s', $tmp_private, $stored_private_path);
192
193
echo tsprintf(
194
"%s\n",
195
pht('Installing device %s...', $raw_device));
196
197
// The permissions on this file are more open because the webserver also
198
// needs to read it.
199
$tmp_device = new TempFile();
200
Filesystem::changePermissions($tmp_device, 0644);
201
execx('chown %s %s', $phd_user, $tmp_device);
202
Filesystem::writeFile($tmp_device, $raw_device);
203
execx('mv -f %s %s', $tmp_device, $stored_device_path);
204
205
echo tsprintf(
206
"**<bg:green> %s </bg>** %s\n",
207
pht('HOST REGISTERED'),
208
pht(
209
'This host has been registered as "%s" and a trusted keypair '.
210
'has been installed.',
211
$raw_device));
212
}
213
214
}
215
216