mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-19 12:00:55 +01:00
Document how to register cluster devices with Almanac
Summary: Ref T4292. This is a required step in configuring a cluster: document and explain it. Previously `bin/almanac register` could //also// add and trust keys. I've removed this capability since I think it's needless and complicated. If there's some real use for it eventually, we could add a `bin/almanac add-key` or whatever. The workflow is simpler and has better guard rails that point you in the correct direction now. Test Plan: - Read documentation. - Ran `bin/almanac` with various good/bad flags. Reviewers: chad Reviewed By: chad Maniphest Tasks: T4292 Differential Revision: https://secure.phabricator.com/D15795
This commit is contained in:
parent
550a82d438
commit
2c870bad86
2 changed files with 298 additions and 63 deletions
|
@ -19,13 +19,6 @@ final class AlmanacManagementRegisterWorkflow
|
|||
'param' => 'key',
|
||||
'help' => pht('Path to a private key for the host.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'allow-key-reuse',
|
||||
'help' => pht(
|
||||
'Register even if another host is already registered with this '.
|
||||
'keypair. This is an advanced featuer which allows a pool of '.
|
||||
'devices to share credentials.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'identify-as',
|
||||
'param' => 'name',
|
||||
|
@ -36,13 +29,13 @@ final class AlmanacManagementRegisterWorkflow
|
|||
array(
|
||||
'name' => 'force',
|
||||
'help' => pht(
|
||||
'Register this host even if keys already exist.'),
|
||||
'Register this host even if keys already exist on disk.'),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
public function execute(PhutilArgumentParser $args) {
|
||||
$console = PhutilConsole::getConsole();
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$device_name = $args->getArg('device');
|
||||
if (!strlen($device_name)) {
|
||||
|
@ -51,7 +44,7 @@ final class AlmanacManagementRegisterWorkflow
|
|||
}
|
||||
|
||||
$device = id(new AlmanacDeviceQuery())
|
||||
->setViewer($this->getViewer())
|
||||
->setViewer($viewer)
|
||||
->withNames(array($device_name))
|
||||
->executeOne();
|
||||
if (!$device) {
|
||||
|
@ -59,6 +52,23 @@ final class AlmanacManagementRegisterWorkflow
|
|||
pht('No such device "%s" exists!', $device_name));
|
||||
}
|
||||
|
||||
$identify_as = $args->getArg('identify-as');
|
||||
|
||||
$raw_device = $device_name;
|
||||
if (strlen($identify_as)) {
|
||||
$raw_device = $identify_as;
|
||||
}
|
||||
|
||||
$identity_device = id(new AlmanacDeviceQuery())
|
||||
->setViewer($viewer)
|
||||
->withNames(array($raw_device))
|
||||
->executeOne();
|
||||
if (!$identity_device) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'No such device "%s" exists!', $raw_device));
|
||||
}
|
||||
|
||||
$private_key_path = $args->getArg('private-key');
|
||||
if (!strlen($private_key_path)) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
|
@ -67,7 +77,7 @@ final class AlmanacManagementRegisterWorkflow
|
|||
|
||||
if (!Filesystem::pathExists($private_key_path)) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht('Private key "%s" does not exist!', $private_key_path));
|
||||
pht('No private key exists at path "%s"!', $private_key_path));
|
||||
}
|
||||
|
||||
$raw_private_key = Filesystem::readFile($private_key_path);
|
||||
|
@ -85,8 +95,8 @@ final class AlmanacManagementRegisterWorkflow
|
|||
if ($err) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Unable to change ownership of a file to daemon user "%s". Run '.
|
||||
'this command as %s or root.',
|
||||
'Unable to change ownership of an identity file to daemon user '.
|
||||
'"%s". Run this command as %s or root.',
|
||||
$phd_user,
|
||||
$phd_user));
|
||||
}
|
||||
|
@ -133,43 +143,39 @@ final class AlmanacManagementRegisterWorkflow
|
|||
->withKeys(array($key_object))
|
||||
->executeOne();
|
||||
|
||||
if ($public_key) {
|
||||
if ($public_key->getObjectPHID() !== $device->getPHID()) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'The public key corresponding to the given private key is '.
|
||||
'already associated with an object other than the specified '.
|
||||
'device. You can not use a single private key to identify '.
|
||||
'multiple devices or users.'));
|
||||
} else if (!$public_key->getIsTrusted()) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'The public key corresponding to the given private key is '.
|
||||
'already associated with the device, but is not trusted. '.
|
||||
'Registering this key would trust the other entities which '.
|
||||
'hold it. Use a unique key, or explicitly enable trust for the '.
|
||||
'current key.'));
|
||||
} else if (!$args->getArg('allow-key-reuse')) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'The public key corresponding to the given private key is '.
|
||||
'already associated with the device. If you do not want to '.
|
||||
'use a unique key, use --allow-key-reuse to permit '.
|
||||
'reassociation.'));
|
||||
}
|
||||
} else {
|
||||
$public_key = id(new PhabricatorAuthSSHKey())
|
||||
->setObjectPHID($device->getPHID())
|
||||
->attachObject($device)
|
||||
->setName($device->getSSHKeyDefaultName())
|
||||
->setKeyType($key_object->getType())
|
||||
->setKeyBody($key_object->getBody())
|
||||
->setKeyComment(pht('Registered'))
|
||||
->setIsTrusted(1);
|
||||
if (!$public_key) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'The public key corresponding to the given private key is not '.
|
||||
'yet known to Phabricator. Associate the public key with an '.
|
||||
'Almanac device in the web interface before registering hosts '.
|
||||
'with it.'));
|
||||
}
|
||||
|
||||
if ($public_key->getObjectPHID() !== $device->getPHID()) {
|
||||
$public_phid = $public_key->getObjectPHID();
|
||||
$public_handles = $viewer->loadHandles(array($public_phid));
|
||||
$public_handle = $public_handles[$public_phid];
|
||||
|
||||
$console->writeOut(
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'The public key corresponding to the given private key is already '.
|
||||
'associated with an object ("%s") other than the specified '.
|
||||
'device ("%s"). You can not use a single private key to identify '.
|
||||
'multiple devices or users.',
|
||||
$public_handle->getFullName(),
|
||||
$device->getName()));
|
||||
}
|
||||
|
||||
if (!$public_key->getIsTrusted()) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'The public key corresponding to the given private key is '.
|
||||
'properly associated with the device, but is not yet trusted. '.
|
||||
'Trust this key before registering devices with it.'));
|
||||
}
|
||||
|
||||
echo tsprintf(
|
||||
"%s\n",
|
||||
pht('Installing public key...'));
|
||||
|
||||
|
@ -179,18 +185,12 @@ final class AlmanacManagementRegisterWorkflow
|
|||
Filesystem::writeFile($tmp_public, $raw_public_key);
|
||||
execx('mv -f %s %s', $tmp_public, $stored_public_path);
|
||||
|
||||
$console->writeOut(
|
||||
echo tsprintf(
|
||||
"%s\n",
|
||||
pht('Installing private key...'));
|
||||
execx('mv -f %s %s', $tmp_private, $stored_private_path);
|
||||
|
||||
$raw_device = $device_name;
|
||||
$identify_as = $args->getArg('identify-as');
|
||||
if (strlen($identify_as)) {
|
||||
$raw_device = $identify_as;
|
||||
}
|
||||
|
||||
$console->writeOut(
|
||||
echo tsprintf(
|
||||
"%s\n",
|
||||
pht('Installing device %s...', $raw_device));
|
||||
|
||||
|
@ -202,14 +202,7 @@ final class AlmanacManagementRegisterWorkflow
|
|||
Filesystem::writeFile($tmp_device, $raw_device);
|
||||
execx('mv -f %s %s', $tmp_device, $stored_device_path);
|
||||
|
||||
if (!$public_key->getID()) {
|
||||
$console->writeOut(
|
||||
"%s\n",
|
||||
pht('Registering device key...'));
|
||||
$public_key->save();
|
||||
}
|
||||
|
||||
$console->writeOut(
|
||||
echo tsprintf(
|
||||
"**<bg:green> %s </bg>** %s\n",
|
||||
pht('HOST REGISTERED'),
|
||||
pht(
|
||||
|
|
242
src/docs/user/cluster/cluster_devices.diviner
Normal file
242
src/docs/user/cluster/cluster_devices.diviner
Normal file
|
@ -0,0 +1,242 @@
|
|||
@title Cluster: Devices
|
||||
@group cluster
|
||||
|
||||
Guide to configuring hosts to act as cluster devices.
|
||||
|
||||
Cluster Context
|
||||
===============
|
||||
|
||||
This document describes a step in configuring Phabricator to run on
|
||||
multiple hosts in a cluster configuration. This is an advanced feature. For
|
||||
more information on clustering, see @{article:Clustering Introduction}.
|
||||
|
||||
In this context, device configuration is mostly relevant to configuring
|
||||
repository services in a cluster. You can find more details about this in
|
||||
@{article:Cluster: Repositories}.
|
||||
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
Some cluster services need to be able to authenticate themselves and interact
|
||||
with other services. For example, two repository hosts holding copies of the
|
||||
same repository must be able to fetch changes from one another, even if the
|
||||
repository is private.
|
||||
|
||||
Within a cluster, devices authenticate using SSH keys. Some operations happen
|
||||
over SSH (using keys in a normal way, as you would when running `ssh` from the
|
||||
command line), while others happen over HTTP (using SSH keys to sign requests).
|
||||
|
||||
Before hosts can authenticate to one another, you need to configure the
|
||||
credentials so other devices know the keys can be trusted. Beyond establishing
|
||||
trust, this configuration will establish //device identity//, so each host
|
||||
knows which device it is explicitly.
|
||||
|
||||
Today, this is primarily necessary when configuring repository clusters.
|
||||
|
||||
|
||||
Using Almanac
|
||||
=============
|
||||
|
||||
The tool Phabricator uses to manage cluster devices is the **Almanac**
|
||||
application, and most configuration will occur through the application's web
|
||||
UI. If you are not familiar with it, see @{article:Almanac User Guide} first.
|
||||
This document assumes you are familiar with Almanac concepts.
|
||||
|
||||
|
||||
What Lies Ahead
|
||||
===============
|
||||
|
||||
Here's a brief overview of the steps required to register cluster devices. The
|
||||
remainder of this document walks through these points in more detail.
|
||||
|
||||
- Create an Almanac device record for each device.
|
||||
- Generate, add, and trust SSH keys if necessary.
|
||||
- Install Phabricator on the host.
|
||||
- Use `bin/almanac register` from the host to register it as a device.
|
||||
|
||||
See below for guidance on each of these steps.
|
||||
|
||||
|
||||
Individual vs Shared Keys
|
||||
=========================
|
||||
|
||||
Before getting started, you should choose how you plan to manage device SSH
|
||||
keys. Trust and device identity are handled separately, and there are two ways
|
||||
to set up SSH keys so that devices can authenticate with one another:
|
||||
|
||||
- you can generate a unique SSH key for each device; or
|
||||
- you can generate one SSH key and share it across multiple devices.
|
||||
|
||||
Using **unique keys** allows the tools to do some more sanity/safety checks and
|
||||
makes it a bit more difficult to misconfigure things, but you'll have to do
|
||||
more work managing the actual keys. This may be a better choice if you are
|
||||
setting up a small cluster (2-3 devices) for the first time.
|
||||
|
||||
Using **shared keys** makes key management easier but safety checks won't be
|
||||
able to catch a few kinds of mistakes. This may be a better choice if you are
|
||||
setting up a larger cluster, plan to expand the cluster later, or have
|
||||
experience with Phabricator clustering.
|
||||
|
||||
Because all cluster keys are all-powerful, there is no material difference
|
||||
between these methods from a security or trust viewpoint. Unique keys are just
|
||||
potentially easier to administrate at small scales, while shared keys are
|
||||
easier at larger scales.
|
||||
|
||||
|
||||
Create Almanac Device Records
|
||||
=============================
|
||||
|
||||
For each host you plan to make part of a Phabricator cluster, go to the
|
||||
{nav Almanac} application and create a **device** record. For guidance on this
|
||||
application, see @{article:Almanac User Guide}.
|
||||
|
||||
Add **interfaces** to each device record so Phabricator can tell how to
|
||||
connect to these hosts. Normally, you'll add one HTTP interface (usually on
|
||||
port 80) and one SSH interface (often on port 22) to each device:
|
||||
|
||||
For example, if you are building a two-host repository cluster, you may end
|
||||
up with records that look like these:
|
||||
|
||||
- Device: `repo001.mycompany.net`
|
||||
- Interface: `123.0.0.1:22`
|
||||
- Interface: `123.0.0.1:80`
|
||||
- Device: `repo002.mycopmany.net`
|
||||
- Interface: `123.0.0.2:22`
|
||||
- Interface: `123.0.0.2:80`
|
||||
|
||||
Note that these hosts will normally run two `sshd` ports: the standard `sshd`
|
||||
which you connect to to operate and administrate the host, and the special
|
||||
Phabricator `sshd` that you connect to to clone and push repositories.
|
||||
|
||||
You should specify the Phabricator `sshd` port, **not** the standard `sshd`
|
||||
port.
|
||||
|
||||
If you're using **unique** SSH keys for each device, continue to the next step.
|
||||
|
||||
If you're using **shared** SSH keys, create a third device with no interfaces,
|
||||
like `keywarden.mycompany.net`. This device will just be used as a container to
|
||||
hold the trusted SSH key and is not a real device.
|
||||
|
||||
NOTE: Do **not** create a **service** record yet. Today, service records become
|
||||
active immediately once they are created, and you haven't set things up yet.
|
||||
|
||||
|
||||
Generate and Trust SSH Keys
|
||||
===========================
|
||||
|
||||
Next, you need to generate or upload SSH keys and mark them as trusted. Marking
|
||||
a key as trusted gives it tremendous power.
|
||||
|
||||
If you're using **unique** SSH keys, upload or generate a key for each
|
||||
individual device from the device detail screen in the Almanac web UI. Save the
|
||||
private keys for the next step.
|
||||
|
||||
If you're using a **shared** SSH key, upload or generate a single key for
|
||||
the keywarden device from the device detail screen in the Almanac web UI.
|
||||
Save the private key for the next step.
|
||||
|
||||
Regardless of how many keys you generated, take the key IDs from the tables
|
||||
in the web UI and run this command from the command line for each key, to mark
|
||||
each key as trusted:
|
||||
|
||||
```
|
||||
phabricator/ $ ./bin/almanac trust-key --id <key-id-1>
|
||||
phabricator/ $ ./bin/almanac trust-key --id <key-id-2>
|
||||
...
|
||||
```
|
||||
|
||||
The warnings this command emits are serious. The private keys are now trusted,
|
||||
and allow any user or device possessing them to sign requests that bypass
|
||||
policy checks without requiring additional credentials. Guard them carefully!
|
||||
|
||||
If you need to revoke trust for a key later, use `untrust-key`:
|
||||
|
||||
```
|
||||
phabricator/ $ ./bin/almanac untrust-key --id <key-id>
|
||||
```
|
||||
|
||||
Once the keys are trusted, continue to the next step.
|
||||
|
||||
|
||||
Install Phabricator
|
||||
===================
|
||||
|
||||
If you haven't already, install Phabricator on each device you plan to enroll
|
||||
in the cluster. Cluster repository devices must provide services over both HTTP
|
||||
and SSH, so you need to install and configure both a webserver and a
|
||||
Phabricator `sshd` on these hosts.
|
||||
|
||||
Generally, you will follow whatever process you otherwise use when installing
|
||||
Phabricator.
|
||||
|
||||
NOTE: Do not start the daemons on the new devices yet. They won't work properly
|
||||
until you've finished configuring things.
|
||||
|
||||
Once Phabricator is installed, you can enroll the devices in the cluster by
|
||||
registering them.
|
||||
|
||||
|
||||
Register Devices
|
||||
================
|
||||
|
||||
To register a host as an Almanac device, use `bin/almanac register`.
|
||||
|
||||
If you are using **unique** keys, run it like this:
|
||||
|
||||
```
|
||||
$ ./bin/almanac register \
|
||||
--device <device> \
|
||||
--private-key <key>
|
||||
```
|
||||
|
||||
For example, you might run this command on `repo001` when using unique keys:
|
||||
|
||||
```
|
||||
$ ./bin/almanac register \
|
||||
--device repo001.mycompany.net \
|
||||
--private-key /path/to/private.key
|
||||
```
|
||||
|
||||
If you are using a **shared** key, this will be a little more complicated
|
||||
because you need to override some checks that are intended to prevent mistakes.
|
||||
Use the `--identify-as` flag to choose a device identity:
|
||||
|
||||
```
|
||||
$ ./bin/almanac register \
|
||||
--device <keywarden-device> \
|
||||
--private-key <key> \
|
||||
--identify-as <actual-device>
|
||||
```
|
||||
|
||||
For example, you might run this command on `repo001` when using a shared key:
|
||||
|
||||
```
|
||||
$ ./bin/almanac register
|
||||
--device keywarden.mycompany.net \
|
||||
--private-key /path/to/private-key \
|
||||
--identify-as repo001.mycompany.net
|
||||
```
|
||||
|
||||
In particular, note that `--device` is always the **trusted** device associated
|
||||
with the trusted key. The `--identify-as` flag allows several different hosts
|
||||
to share the same key but still identify as different devices.
|
||||
|
||||
The overall effect of the `bin/almanac` command is to copy identity and key
|
||||
files into `phabricator/conf/keys/`. You can inspect the results by examining
|
||||
that directory. The helper script just catches potential mistakes and makes
|
||||
sure the process is completed correctly.
|
||||
|
||||
Note that a copy of the active private key is stored in the `conf/keys/`
|
||||
directory permanently.
|
||||
|
||||
|
||||
Next Steps
|
||||
==========
|
||||
|
||||
Now that devices are registered, you can build cluster services from them.
|
||||
Return to the relevant cluster service documentation to continue:
|
||||
|
||||
- build repository clusters with @{article:Cluster: Repositories};
|
||||
- return to @{article:Clustering Introduction}; or
|
||||
- review the Almanac application with @{article:Almanac User Guide}.
|
Loading…
Reference in a new issue