Help needed with configuring Diffusion git hosting via SSH (mismatched users in procedure?)

Greetings,

I’m trying to setup Git repository hosting with Diffusion. I’m trying to follow this tutorial:
https://secure.phabricator.com/book/phabricator/article/diffusion_hosting/

(actual issue described after text in bold at the bottom)

I have authentication issues, but let me explain my configuration first.

  1. System accounts:
  • my www-user is www-data
  • my daemon-user is www-data
  • my vcs-user is git
  1. Configuring phabricator:
    Current settings are:
    [www-data][/d/p/phabricator]$ ./bin/config get phd.user
    {
      "config": [
        {
          "key": "phd.user",
          "source": "local",
          "value": "www-data",
          "status": "set",
          "errorInfo": null
        },
        {
          "key": "phd.user",
          "source": "database",
          "value": null,
          "status": "unset",
          "errorInfo": null
        }
      ]
    }

    [www-data][/d/p/phabricator]$ ./bin/config get diffusion.ssh-user
    {
      "config": [
        {
          "key": "diffusion.ssh-user",
          "source": "local",
          "value": "git",
          "status": "set",
          "errorInfo": null
        },
        {
          "key": "diffusion.ssh-user",
          "source": "database",
          "value": null,
          "status": "unset",
          "errorInfo": null
        }
      ]
    }
  1. Configuring Sudo
    My sudoers file contains following line
    git ALL=(www-data) SETENV: NOPASSWD: /usr/bin/git, /usr/bin/git-upload-pack, /usr/bin/git-receive-pack

I did not set www-user sudoers line, since my daemon-user is the same as www-user (both are www-data).

requiretty option is absent in the file.

  1. Additional SSH User Configuration

I’ve ensured password was not set to !! for git user.
Shell is set to /bin/sh

  1. Configuring HTTP
    I’ve skipped this section, since I’m going to provide ssh-only access.

  2. Configuring SSH, SSHD Port Assignment

I’m running extra sshd daemon process on port 60. Here’s snippet responsible for this configuration:

Port 60
Match LocalPort 60
  AllowUsers git
  AllowGroups git
  PasswordAuthentication no
  AllowAgentForwarding no
  AllowTcpForwarding no
  AuthorizedKeysFile none
  AuthorizedKeysCommand /data/phabricator/scripts/phabricator-ssh-hook.sh
  AuthorizedKeysCommandUser git
  1. SSHD Setup

Phabricator is informed about the port:

[www-data][/d/p/phabricator]$ ./bin/config get diffusion.ssh-port
{
  "config": [
    {
      "key": "diffusion.ssh-port",
      "source": "local",
      "value": 60,
      "status": "set",
      "errorInfo": null
    },
    {
      "key": "diffusion.ssh-port",
      "source": "database",
      "value": null,
      "status": "unset",
      "errorInfo": null
    }
  ]
}

/data/phabricator/scripts/phabricator-ssh-hook.sh is owned by root, set to 755 permissions, its parent directory is also root and 755. Its contents are like the example file:
#!/bin/sh

# NOTE: Replace this with the username that you expect users to connect with.
VCSUSER="git"

# NOTE: Replace this with the path to your Phabricator directory.
ROOT="/data/phabricator/phabricator"

if [ "$1" != "$VCSUSER" ];
then
  exit 1
fi

exec "$ROOT/bin/ssh-auth" $@

And now, according to configuration I should be able to connect like this (ssh keys are added for the user, that’s like triple-checked)

$ echo {} | ssh vcs-user@phabricator.yourcompany.com conduit conduit.ping

>> ACTUAL ISSUE BELOW

So in my case:

    [someuser][~]$ echo {} | ssh -p60 git@localhost conduit conduit.ping
    Permission denied (publickey).
    [someuser][~]$ echo $?
    255

That’s not good. What I’ve managed to establish, return code from phabricator–ssh-hook.sh is carried over.

Let’s craft the command by hand:

[someuser][/d/p/scripts]$ sudo -u git ./phabricator-ssh-hook.sh git
[2018-03-29 14:22:33] EXCEPTION: (PhutilProxyException) Configuration file "/data/phabricator/phabricator/conf/local/local.json" exists, but could not be read. {>} (FilesystemException) Path '/data/phabricator/phabricator/conf/local/local.json' is not readable. at [<phutil>/src/filesystem/Filesystem.php:1124]
arcanist(head=stable, ref.master=90546042144f, ref.stable=22b0893473ae), phabricator(head=stable, ref.master=d2cff6a2cf01, ref.stable=c56bdc92a2cf), phutil(head=stable, ref.master=500eecc03c58, ref.stable=fd9060c5a1ef)
  #0 <#2> Filesystem::assertReadable(string) called at [<phutil>/src/filesystem/Filesystem.php:39]
  #1 <#2> Filesystem::readFile(string) called at [<phabricator>/src/infrastructure/env/PhabricatorConfigLocalSource.php:30]
  #2 PhabricatorConfigLocalSource::loadConfig() called at [<phabricator>/src/infrastructure/env/PhabricatorConfigLocalSource.php:6]
  #3 PhabricatorConfigLocalSource::__construct() called at [<phabricator>/src/infrastructure/env/PhabricatorEnv.php:195]
  #4 PhabricatorEnv::buildConfigurationSourceStack(boolean) called at [<phabricator>/src/infrastructure/env/PhabricatorEnv.php:95]
  #5 PhabricatorEnv::initializeCommonEnvironment(boolean) called at [<phabricator>/src/infrastructure/env/PhabricatorEnv.php:75]
  #6 PhabricatorEnv::initializeScriptEnvironment(boolean) called at [<phabricator>/scripts/init/lib.php:22]
  #7 init_phabricator_script(array) called at [<phabricator>/scripts/init/init-script.php:9]
  #8 require_once(string) called at [<phabricator>/scripts/__init_script__.php:3]
  #9 require_once(string) called at [<phabricator>/scripts/ssh/ssh-auth.php:5]
[someuser][/d/p/scripts]$ echo $?
255

Bingo. Now let’s run the code using our daemon-user/www-user:

[someuser][/d/p/scripts]$ sudo -u www-data ./phabricator-ssh-hook.sh git
command="'/data/phabricator/phabricator/bin/ssh-exec' '--phabricator-ssh-user' 'groot' '--phabricator-ssh-key' '1'",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCYQcg/v+c0qOExDNt9yILfJZ2IGfxTnOgMI6bjveRbKGMiWM7VgVnlKo3QJP+TuDkujJqSHT04bGTvaG+J5EqamZxROqhTUkLhcOGI/8d3ZjO6eeCNBLT9rRUGMLe6AMqy+Kt4IzpOqAwXoa1R2WvC4URhifinDhbq07jGcNbM8AA+DEJRV7x7Ndke+oToXUsZfzt6baxwIjPNoZnxJA0oVvJEQrLtXff4Y+K7ytY8RS9rg1uFBKvZeE/J5EaLqRbQooWydlH8QU+Ndsn0zoKeqtdU4raLxBHfs9JxHNq5EpAYrWzIHZkA3dq9Mg6v1tuFKaPXzCKkBEfuVu1gQEpH
[someuser][/d/p/scripts]$ echo $?
0

OK, that’s something more useful. So stuff is somewhat working when run using daemon-user. Let’s change the sshd_config:

-  AuthorizedKeysCommandUser git
+  AuthorizedKeysCommandUser www-data

Restarted sshd daemon. Now:

[someuser][~]$ echo {} | ssh -p60 git@localhost conduit conduit.ping
[2018-03-29 14:33:28] EXCEPTION: (PhutilProxyException) Configuration file "/data/phabricator/phabricator/conf/local/local.json" exists, but could not be read. {>} (FilesystemException) Path '/data/phabricator/phabricator/conf/local/local.json' is not readable. at [<phutil>/src/filesystem/Filesystem.php:1124]
arcanist(head=stable, ref.master=90546042144f, ref.stable=22b0893473ae), phabricator(head=stable, ref.master=d2cff6a2cf01, ref.stable=c56bdc92a2cf), phutil(head=stable, ref.master=500eecc03c58, ref.stable=fd9060c5a1ef)
  #0 <#2> Filesystem::assertReadable(string) called at [<phutil>/src/filesystem/Filesystem.php:39]
  #1 <#2> Filesystem::readFile(string) called at [<phabricator>/src/infrastructure/env/PhabricatorConfigLocalSource.php:30]
  #2 PhabricatorConfigLocalSource::loadConfig() called at [<phabricator>/src/infrastructure/env/PhabricatorConfigLocalSource.php:6]
  #3 PhabricatorConfigLocalSource::__construct() called at [<phabricator>/src/infrastructure/env/PhabricatorEnv.php:195]
  #4 PhabricatorEnv::buildConfigurationSourceStack(boolean) called at [<phabricator>/src/infrastructure/env/PhabricatorEnv.php:95]
  #5 PhabricatorEnv::initializeCommonEnvironment(boolean) called at [<phabricator>/src/infrastructure/env/PhabricatorEnv.php:75]
  #6 PhabricatorEnv::initializeScriptEnvironment(boolean) called at [<phabricator>/scripts/init/lib.php:22]
  #7 init_phabricator_script(array) called at [<phabricator>/scripts/init/init-script.php:9]
  #8 require_once(string) called at [<phabricator>/scripts/__init_script__.php:3]
  #9 require_once(string) called at [<phabricator>/scripts/ssh/ssh-exec.php:7]
[someuser][~]$ echo $?
255

That suggests we’ve moved forward, but just a little, to the next stage where problem is exactly the same (permissions/user issue). At this point I’m completely clueless. It feels like everything requires running with daemon-user, but then entire configuration makes no sense.
Any insight would be appreciated.

Thanks,
Matt

The error message is pretty clear on this: Your git user cannot read the Phabricator configuration file.

Yeah, you configured it so that www-data is executing the AuthorizedKeysCommand, but afterwards, git is trying to run the actual command requested, which is phabricator/bin/ssh-exec, which also needs to read the configuration file.
So you actually need to make the file readable to user git.

@avivey @Info-Screen thank you for pointing this out. The default permissions to this file are 600 and I thought there must be something else on the way - since this file contains potentially sensitive data (database password). Shouldn’t this be achievable via some additional sudo line?

edit: tutorial tells me to use vcs-user to run AuthorizedKeysCommandUser as vcs-user. And originally I was running it this way and running into same issues, just message was not sent. Return code was 255 and that was the only hint what’s really going on. Nonetheless, I couldn’t find a single line in documentation that vcs-user is expected to be able to read/execute anything like that - hence the original confusion.

Entire tutorial seems like some bits are missing. Like for example - how do I specify target location of the repositories? Is it the vcs-user home directory? Because given how normal git via ssh works that’d be the location - but then I’m quite uncertain whether there’s any logic/magic done by phabricator or not.

OK, I’ve updated ownership to www-data:git and permissions to 640. And the entire thing looks still messed up:

[someuser][~]$ echo {} | ssh -p60 git@localhost conduit conduit.ping
{"result":"somehost.local","error_code":null,"error_info":null}
[2018-03-30 12:55:09] EXCEPTION: (Exception) Unable to write to logfile "/mnt/nfs/lsb-prd/phabricator/logs/ssh.log"! at [<phutil>/src/filesystem/PhutilDeferredLog.php:193]
arcanist(head=stable, ref.master=90546042144f, ref.stable=22b0893473ae), phabricator(head=stable, ref.master=d2cff6a2cf01, ref.stable=c56bdc92a2cf), phutil(head=stable, ref.master=500eecc03c58, ref.stable=fd9060c5a1ef)
  #0 <#2> PhutilDeferredLog::__destruct()
  #1 phlog(Exception) called at [<phutil>/src/filesystem/PhutilDeferredLog.php:201]
  #2 PhutilDeferredLog::write() called at [<phutil>/src/filesystem/PhutilDeferredLog.php:155]
  #3 PhutilDeferredLog::__destruct()

So now… Conclusion is: everything is being executed as vcs-user user, even though everything (aside of git with user git itself) should run with daemon-user. This makes entire system quite messed up and something is definitely not right. Question is… how do I untangle that? Procedure itself clearly suggests that user separation is a thing, but then either something has changed internally or procedure does not cover everything in order to set things up.

Of course. Hacky way would be to change ownership/permissions of every file, but that does not seem like right way. All the suggestions are welcome.

Hmm.

The procedures and the manual hadn’t changed in a long while, and we’ve never had these complaints before.
However, I agree that it’s not really clear right now what kind of permissions are needed, and it might make sense to limit some of the exposure.
However, the name of daemon-user is recorded in the config file, so vcs-user needs to be able to read it.

I’m guessing ssh.log is coming from this config option log.ssh.path (Which is disabled by default). Maybe we should explain in the docs that it’s written by vcs-user and needs appropriate permission.


When ssh-ing into Phabricator, you’re not logging-in to the machine directly, but into Phabricator. Your shell is replaced with phabricator-ssh-exec, which is executed in the scope of your Phabricator set-up; It loads the configuration from the config file and from the database.
We could sudo as soon as we have the name of the daemon-user, but that would arguably expose more parts of the host system to an attack: Any executable that can be invoked by Phabricator, rather than only the programs explicitly mentioned in /etc/sudoers. In the current setup, vcs-user still has read-write access to the Phabricator databse, but it’s exposing less of the whole system.

I’ve chowned git:git the ssh.log (as you mentioned, I’ve set it up in the config manually) and conduit.ping executed with no extra issues. I’m going to proceed with repository migration to see whether I bump into any more issues.

1 Like