systemd Services for Phabricator's phd and SSH Requirements

Submitted by Silvershock on Wed, 02/08/2017 - 22:31

Note: This article assumes you have already followed the Phabricator installation guide and have all the relevant user accounts already configured on your system. If you have not fully completed Phabricator setup, go do that, then come back. This article focuses on Ubuntu 16.04, but most of it should work anywhere. An excellent Ubuntu-specific installation guide (minus these systemd services) can be found on this Gist by sparrc.

You might have noticed from the menu bar that I've moved from Trac to Phabricator, and I'm moving my various planning info over. Phabricator gives me many of the same wiki, ticket and repo tools that Trac did, but also has far superior authentication tools, some basic CI integration, code review, design mocking, a nicer interface and so on.

Like many modern code repository tools, Phabricator takes over management of your Git connections, authenticating users based on standard asynchronous keys and keeping its database up-to-date as stored code changes. The various daemons it uses for tasks like this are handled by a control program called phd, and it also has a hook for managing authentication on your Git/SSH connections. However, because Phabricator is a PHP application and platform agnostic, it provides no way to turn phd into a service within your operating system, which can make managing it a pain in the ass.

phd Service

Fortunately, Ubuntu 16.04 (which this website runs on) uses systemd, so writing a service wrapper for the daemon is super-easy. After researching a few similar attempts and tweaking where they went wrong for me, I came up with the following:

[Unit]
Description=phabricator-phd
After=syslog.target network.target mysql.service
Before=lighttpd.service

[Service]
Type=oneshot
User=phd-user
Group=phd-group
Enviroment="PATH=/sbin:/usr/sbin:/usr/local/sbin:/usr/local/bin:/usr/bin:/bin"
ExecStart=/path/to/phd start
ExecStop=/path/to/phd stop
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

The parts of this that you'll need to edit should be obvious enough:

[Unit] > Before - Change this to the service identifier of the web server software you use. I use Lighttpd on here, but you might be on nginx, Apache or something else entirely. You can find your service in /lib/systemd/system
[Service] > User - Set this to the user phd should run as.
[Service] > Group - Same again, but for phd's user group.
[Service] > ExecStart, [Service] > ExecStop - Update the paths here to point to your phd file. (It's in the /bin directory of your Phabricator installation.)

Not a big file, is it? Saying nice things about systemd is punishable by death in certain Linux circles, but this is so much better than the older Upstart/SysV services! Once you have your file ready to go, save it to the service directory and fire it up!

$ sudo cp service.file /lib/systemd/system/phabricator-phd.service
$ sudo systemctl enable phabricator-phd.service
$ sudo systemctl start phabricator-phd

If everything has gone well, a status check on the service should look like this:

$ sudo systemctl status phabricator-phd

phabricator-phd.service - phabricator-phd
Loaded: loaded (/lib/systemd/system/phabricator-phd.service; disabled; vendor preset: enabled)
Active: active (exited) since Thu 2017-02-09 00:00:57 PST; 1s ago
Process: 639 ExecStart=/path/to/phabricator/bin/phd start (code=exited, status=0/SUCCESS)
Main PID: 639 (code=exited, status=0/SUCCESS)
   CGroup: /system.slice/phabricator-phd.service
           ├─643 php ./phd-daemon
           ├─644 php ./exec_daemon.php PhabricatorRepositoryPullLocalDaemon
           ├─645 php ./exec_daemon.php PhabricatorTriggerDaemon
           └─646 php ./exec_daemon.php PhabricatorTaskmasterDaemon

Because systemd is dependency based, enabling the service also establishes when it should start and stop. The file format used by systemd is pretty readable. You can see that this service is configured to start once the system is in multi-user mode, system logging and network services are ready, and MySQL is up and running. If you reboot your server, the service should start automatically at startup. Kick ass!

Now that we've got phd running, we need an SSH daemon so we can connect to Phabricator.

SSH Service

Phabricator needs its own SSH instance to manage. This can't be the same as your regular SSH connection to your server, so the best method is to move your regular SSH access to a non-standard port and let Phabricator have port 22. This will cause you much less hassle with dev tools in the long run, trust me, so let's move your standard SSH daemon first. Just edit your configuration file at /etc/ssh/sshd_config and change the Port directive to some other number, then save the file and restart the service.

$ sudo service ssh restart

Don't forget to specify the port number when connecting to your server from now on, using the -p option. OK, now we have port 22 freed up, let's sort out an SSH daemon for Phabricator to use. Fortunately, Phabricator does help out in this regard and provides a few pre-built example files we can use. Let's create the authentication hook that Phabricator will use to validate SSH connections. Go to your Phabricator installation and open the /resources/sshd/phabricator-ssh-hook.sh file. As you can see, this is a really basic hook file. Edit the VCSUSER variable value to the system user you expect developers to connect as (probably "git"), and edit the ROOT variable to be the path to your Phabricator installation. Once you've made these two edits, make a directory in /usr/lib for Phabricator scripts and copy the hook into it:

$ sudo mkdir /usr/lib/phabricator
$ sudo cp phabricator-ssh-hook.sh /usr/lib/phabricator/phabricator-ssh-hook.sh
$ sudo chown root:root /usr/lib/phabricator/phabricator-ssh-hook.sh
$ sudo chmod 755 /usr/lib/phabricator/phabricator-ssh-hook.sh

You might have noticed that there is also a file in the resources folder called sshd_config.phabricator.example. This is a good easily-modified SSH configuration for Phabricator. However, I've made a couple of modifications to this file to improve it. What I've basically done is taken the Phabricator example and added constraints for which key types can be utilised, to turn off some considered insecure and turn on some not supported by default in some versions. (If you're running at least OpenSSH 7 or newer, this shouldn't be an issue, but just in case, eh?)

Here's my modified file:

# NOTE: You must have OpenSSHD 6.2 or newer; support for AuthorizedKeysCommand
# was added in this version.

# NOTE: Edit these to the correct values for your setup.
AuthorizedKeysCommand /usr/lib/phabricator/phabricator-ssh-hook.sh
AuthorizedKeysCommandUser git
AllowUsers git

# Silver's mod: specify which host keys can be used
# (More specifically, disallow DSA)
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_ecdsa_key
HostKey /etc/ssh/ssh_host_ed25519_key

# You may need to tweak these options, but mostly they just turn off everything
# dangerous.
Port 22
Protocol 2
PermitRootLogin no
AllowAgentForwarding no
AllowTcpForwarding no
PrintMotd no
PrintLastLog no
PasswordAuthentication no
AuthorizedKeysFile none

PidFile /var/run/sshd-phabricator.pid

Make sure that the AuthorizedKeysCommandUser and AllowUsers directives are updated to match the user your developers will be connecting as (again, probably "git"), then save this to /etc/ssh. You now have configuration ready for your Phabricator SSH daemon. Finally, we need to configure the service itself. The best way to do this is to cheat. Let's copy the standard configuration for your existing SSH service and tweak it a little.

$ sudo cp /lib/systemd/system/ssh.service /lib/systemd/system/phabricator-ssh.service

Open the new phabricator-ssh.service file (you'll need to be root or using sudo) and do the following:

  1. Change the [Service] > ExecStart directive to be /usr/sbin/sshd -D $SSHD_OPTS -f /etc/ssh/sshd_config.phabricator
  2. Remove the [Install] > Alias directive completely

That's all you need to do. With that done, let's enable the new SSH service.

$ sudo systemctl enable phabricator-ssh
$ sudo systemctl start phabricator-ssh

Once you put your SSH public key into your user account on Phabricator, you should now be able to connect to your Git repositories. Your Phabricator daemons and SSH endpoint can now be managed via systemctl, and will come up automatically on boot. Hopefully this will be of use to you!