How does Verify Committer pre-receive hook work with SSH (appears to bypass)?

Jorge Suarez April 3, 2020

VERSION- Bitbucket 6.7.1 Server

The problem I'm trying to solve, though irrelevant to the question, is that we have a mass email domain migration and I want to fuzzy match committer (ie. verify last names match in emails, ignoring domain).

I'm dumbfounded here as I understand how the pre-recieve hook mostly works.  I'm wrote a custom filesystem hook using <repo_dir>/hooks/pre-receive.d/25_myhook.  The script basically does this:

  1. Get the Bitbucket user from env variable GIT_COMMITTER_NAME (apparently this is only for HTTP pushes), or GIT_COMMITTER_IDENT
  2. Query Bitbucket on localhost REST API to get email associated with username from prior step.  (I don't think Bitbucket provides the email to the script.)
    • Email is actually set to IP address (and actually that of reverse proxy)
    • remote: GIT_COMMITTER_EMAIL=jsuarezx@http.172.X.X.X     
    • OR...
    • remote: GIT_COMMITTER_IDENT=jsuarezx <jsuarezx@http.172.X.X.X> 1585962883 -0400
  3. Iterate through 'git rev-list' and check fuzzy match of lastname in committer email with that of user.

This seems to be workable for HTTP connections.

The problem however is that when I switch to SSH authentication, it appears to completely bypass the built in Verify Commiter hook, and for my script, there appears to be no easy way to check which Bitbucket account is doing the commit.

When using SSH, the following environment variables are not set: GIT_COMMITTER_NAME, GIT_COMMITTER_EMAIL.

GIT_COMMITTER_IDENT=xadmin <xadmin@server.com> 1585963121 -0400

It appears that when SSH is used, this variable is replaced by the local user that Bitbucket is run as.  

Question is, for SSH connections, is the Verify Committer hook applicable?  

Is this fixed in later versions?  (We are installing Dataceter 7.1 soon, but I haven't ready any info on this situation being broken prior, so not optimistic that behavior would be different,)

 

1 answer

1 accepted

0 votes
Answer accepted
Bryan Turner
Atlassian Team
Atlassian Team members are employees working across the company in a wide variety of roles.
September 22, 2020

@Jorge Suarez

Our built-in Verify Committer hook runs fine on SSH--identically to HTTP, in fact. It doesn't rely on environment variables with either protocol; the identity of the pushing user is read directly from the application state since our Verify Committer hook is implemented in Java.

$ git push ssh://git@localhost:7999/qa/foo.git HEAD
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 230 bytes | 230.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
remote: You can only push your own commits in this repository
remote: Commit 76dcc3151090930fed73b37c004d2461bf1f6bcb was committed by Bryan Turner <bturner@atlassian.com>
To ssh://localhost:7999/qa/foo.git
! [remote rejected] HEAD -> master (pre-receive hook declined)
error: failed to push some refs to 'ssh://git@localhost:7999/qa/foo.git'

$ git push http://localhost:7990/bitbucket/scm/qa/foo.git HEAD
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 230 bytes | 230.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
remote: You can only push your own commits in this repository
remote: Commit 76dcc3151090930fed73b37c004d2461bf1f6bcb was committed by Bryan Turner <bturner@atlassian.com>
To http://localhost:7990/bitbucket/scm/qa/foo.git
! [remote rejected] HEAD -> master (pre-receive hook declined)
error: failed to push some refs to 'http://localhost:7990/bitbucket/scm/qa/foo.git

We don't export `GIT_COMMITTER_NAME` or `GIT_COMMITTER_IDENT` anywhere in our HTTP (or SSH) hosting handling, and attaching with a debugger and looking at the environment we pass to `git http-backend` shows neither is set:

"CONTENT_LENGTH" -> "383"
"CONTENT_TYPE" -> "application/x-git-receive-pack-request"
"GATEWAY_INTERFACE" -> "CGI/1.1"
"GIT_CEILING_DIRECTORIES" -> "/path/to/bitbucket/home/shared/data/repositories:/path/to/bitbucket/home"
"GIT_HTTP_EXPORT_ALL" -> "true"
"GIT_PROJECT_ROOT" -> "/path/to/bitbucket/home/shared/data/repositories/12"
"HTTPS" -> "OFF"
"HTTP_ACCEPT" -> "application/x-git-receive-pack-result"
"HTTP_ACCEPT_ENCODING" -> "deflate, gzip"
"HTTP_AUTHORIZATION" -> "Basic YWRtaW46YWRtaW4="
"HTTP_CONTENT_LENGTH" -> "383"
"HTTP_CONTENT_TYPE" -> "application/x-git-receive-pack-request"
"HTTP_HOST" -> "localhost:7990"
"HTTP_USER_AGENT" -> "git/2.20.1"
"LC_ALL" -> "C"
"PATH_INFO" -> "/git-receive-pack"
"REMOTE_ADDR" -> "0:0:0:0:0:0:0:1"
"REMOTE_HOST" -> "0:0:0:0:0:0:0:1"
"REMOTE_USER" -> "jdoe"
"REQUEST_METHOD" -> "POST"
"SCRIPT_NAME" -> "/git-receive-pack"
"SERVER_NAME" -> "localhost"
"SERVER_PORT" -> "7990"
"SERVER_PROTOCOL" -> "HTTP/1.1"
"SERVER_SOFTWARE" -> "Apache Tomcat/9.0.37"
"STASH_GIT_TRANSCODE_HOST" -> "127.0.0.1"
"STASH_GIT_TRANSCODE_PORT" -> "50300"
"STASH_HOOK_ADDRESS" -> "127.0.0.1"
"STASH_HOOK_CALLBACK" -> "/path/to/bitbucket/home/bin/git-hooks/hook-callback.pl"
"STASH_HOOK_COORDINATOR" -> "/path/to/bitbucket/home/bin/git-hooks/hook-coordinator.sh"
"STASH_HOOK_PORT" -> "50301"
"STASH_HOOK_REQUEST_ID" -> "b3f6effbcdd0138fe78699312ce6352f66bd44a6"

What you're seeing is this Git code, which defaults the `GIT_COMMITTER_*` variables from the "REMOTE_USER" and "REMOTE_ADDR". There's no equivalent code in `receive-pack.c`, which is what SSH hosting uses, and so you don't get the same behavior.

These sorts of issues are why we recommend administrators and app developers write Java-based hooks instead of script-based ones. What you're trying to do can be done much more efficiently from Java, where the current user is available from the `AuthenticationContext`--no need to try and reverse engineer it from environment variables and make REST requests--and where we provide a way to register a callback to have your hook receive the newly pushed commits which is implemented in a way that allows that data to be reused by/shared with other hooks. (In other words, instead of every hook running its own `git rev-list` commands, your hook can register its interest and the system runs `git rev-list` once and streams the commits to every interested hook.)

If you choose to write it as a script anyway, you might check the "REMOTE_USER" environment variable. Bitbucket Server exports that before it forks out any `git` processes, if there's a currently-authenticated user. (Of course, whether that propagates to hook scripts is up to Git, but I'd expect it to retain the environment when forking subprocesses.)

Best regards,
Bryan Turner
Atlassian Bitbucket

Jorge Suarez September 23, 2020

Thanks Bryan, for future hooks I will look into implementing them using Java.  My use case required just a quick and dirty one-time solution while we transitioned users accounts and I do not use Java regularly, but I am much more familiar with bash.

Suggest an answer

Log in or Sign up to answer
DEPLOYMENT TYPE
SERVER
VERSION
6.7.1
TAGS
AUG Leaders

Atlassian Community Events