8

I host a private git server using Nginx. I'd like anyone to clone into my repos (without authorization) but require authorization if they try to push a commit.

My Nginx configuration is as follows:

server {
  listen 443 ssl;
  server_name git.example.com;
  ssl_certificate /fullchain.pem;
  ssl_certificate_key /privkey.pem;

  location ~ ^.*\.git/(HEAD|info/refs|objects/info/.*|git-(upload|recieve)-pack) {
    root /usr/share/nginx/git;

# --- incorrect solution ---

#    if ($1 = git-upload-pack) {
#        auth_basic "Restricted";
#        auth_basic_user_file /usr/share/nginx/htpasswd;
#    }
    client_max_body_size 0;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME /usr/lib/git-core/git-http-backend;
    fastcgi_param GIT_HTTP_EXPORT_ALL "";
    fastcgi_param GIT_PROJECT_ROOT $realpath_root;
    fastcgi_param REMOTE_USER $remote_user;
    fastcgi_param PATH_INFO $uri;
    fastcgi_param unix:/var/fcgiwrap.socket;

  }

From my understanding, a git push request sends a git-receive-pack to my server. My simple solution was to capture this suffix with $1 and use an if statement, but I found quickly that this is not the correct use for ifs (ifisevil).

Is there a more appropriate solution to what I'm trying to accomplish?

pairwiseseq
  • 313
  • 2
  • 13

2 Answers2

5

Try separating the locations:

server {
  listen 443 ssl;
  server_name git.example.com;
  ssl_certificate /fullchain.pem;
  ssl_certificate_key /privkey.pem;

  location ~ ^.*\.git/(HEAD|info/refs|objects/info/.*|git-recieve-pack) {
    # Do your stuff 

  location ~ ^.*\.git/git-upload-pack {
    auth_basic "Restricted";
    auth_basic_user_file /usr/share/nginx/htpasswd;
    # Do your stuff 
ofirule
  • 4,233
  • 2
  • 26
  • 40
  • Maybe I got the regex wrong. But this is the general idea. Feel free to fix it – ofirule May 03 '20 at 18:56
  • I thought this would work, too, but, for some reason, it still asks me to type in username and password when I clone a remote repo. I am using jgit shell script instead of git, but this shouldn't change how I clone/push repos, right? – pairwiseseq May 03 '20 at 20:41
  • Look at the nginx access log, and look at the output for both git and jgit. There might be differences – ofirule May 03 '20 at 21:30
  • 1
    Also, according to: https://git-scm.com/docs/git-http-backend It looks like it's the other way around and you should authenticate on `^.*\.git/git-recieve-pack` – ofirule May 03 '20 at 21:45
  • I tried your method but with `git-recieve-pack` and `git-upload-pack` rreversed. I can clone repos fine now without authorization, but when I push a commit, I get an error - `git-receive-pack` not allowed...? – pairwiseseq May 03 '20 at 22:28
  • 1
    I think this is the desired behavior when using `auth_basic` without providing username and password. Maybe this is the way to provide them: https://stackoverflow.com/questions/6565357/git-push-requires-username-and-password and maybe you just want application level authentication and authorization instead of `auth_basic` – ofirule May 03 '20 at 22:43
  • Git's not giving me a chance to provide a username and password. Git automatically gives me a `git-receive-pack not allowed` error. If I use `git-(upload|receive)-pack` as I did originally, `git push` works fine after typing username/password. This is progress regardless. I think I'm just writing the `location` block incorrectly. – pairwiseseq May 03 '20 at 22:53
3

You can auth/deny based on request_method.

        if ($request_method ~* "^(POST|PUT|DELETE)$" ) {
                # check if is there a cookie or something like that
                # and redirect to login if not
        }
Dilson Rainov
  • 401
  • 4
  • 8