How Single-User Git-Over-SSH Works

Posted 2020-05-03 23:29:02.079142964 UTC

I had to read through so much source code to figure this out


Have you ever wondered how GitHub, GitLab, Gogs, Gitea, and so on allow multiple users to push and pull data from repos with only one unix user? Perhaps you want to know how this is done so you can write your own version of the aforementioned type of software that doesn't suck. That's how I got here. Anyway, this is how it's done:

OpenSSH gives you an option (ForceCommand in /etc/ssh/sshd_config) to force the use of a particular command on connection, overriding the one the client actually wants to run. When this option is set, the client's "intended command" gets stored in an environment variable called $SSH_ORIGINAL_COMMAND. This can be used to force the execution of your own script (or binary) that allows and disallows "intended commands" each time anyone tries to do anything with this account over SSH.

Additionally, the ~/.ssh/authorized_keys file allows you to specify a per-key forced command. This means that you can set an option (say, --with-key) for the per-key override that isn't present in the global override in /etc/ssh/sshd_config. Now, in your forced command, you can disallow write access to connections that are missing the --with-key argument, since it's only present if a user has uploaded an SSH key.

Even further, you can provide the --with-key argument a key_id value that allows your software to know exactly which key was used for this connection. With that information, you can associate the SSH key with a user account and, for example, allow them extra read/write access to their private repositories and extra write access to their public ones. It's up to your software to keep a database of key_id <-> SSH key <-> user associations, however.

While you're messing with the sshd_config, you'll need to add PermitEmptyPasswords yes and you'll probably also want to add DisableForwarding yes. Thankfully, you can restrict the effects of those two options to apply only to your software's user and not to everyone on the server by using a Match section. All together, your sshd_config will have a new section like this:

Match User your_software_user
    PermitEmptyPasswords yes
    DisableForwarding yes
    ForceCommand /path/to/your/software

References:

  1. man 5 sshd_config for /etc/ssh/sshd_config options
  2. man 8 sshd for ~/.ssh/authorized_keys file format and options
  3. Both of the above for the uses of the $SSH_ORIGINAL_COMMAND environment variable
  4. The Gogs codebase for reverse-engineering all of the above