Search code examples
gitapachebasic-authenticationsubdirectory

Disabling access for git to specific Directory with Apache Basic Authentication


I have git setup with my Apache2 server and it serves git request just fine. Now I want to setup Basic Authentication for this, so not everybody can use every directory. My goal is that only the ADMIN group has access to the complete /var/www/html/git directory and my GITGROUP can access only /var/www/html/git/subdir directories. However, while Apache is asking for credentials, with the setup (below) GITGROUP is still allowed to access all git directories. What am I doing wrong?

SetEnv GIT_PROJECT_ROOT /var/www/html/git
SetEnv GIT_HTTP_EXPORT_ALL
ScriptAlias /git/ /usr/lib/git-core/git-http-backend/
<Directory /usr/lib/git-core>
    Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
    AuthType Basic
    AuthName "Authentication Required"
    AuthUserFile "/etc/apache2/.htpasswd"
    AuthGroupFile "/etc/apache2/groups"
    Require group ADMIN GITGROUP

    Order allow,deny
    Allow from all
</Directory>
<Directory /var/www/html/git>
    AuthType Basic
    AuthName "Authentication Required"
    AuthUserFile "/etc/apache2/.htpasswd"
    AuthGroupFile "/etc/apache2/groups"
    Require group ADMIN
    Options -Indexes

    Order allow,deny
    Allow from all
</Directory>
<Directory /var/www/html/git/subdir>
    AuthType Basic
    AuthName "Authentication Required"
    AuthUserFile "/etc/apache2/.htpasswd"
    AuthGroupFile "/etc/apache2/groups"
    Require group ADMIN GITGROUP
    Options -Indexes

    Order allow,deny
    Allow from all
</Directory>

EDIT:

Based on the answer by @jsvendsgaard I created two ScriptAlias lines for two symlinks to git-core. After that it is only a matter of assigning the correct GIT_PROJECT_ROOT:

ScriptAlias /gitdir1/ /var/www/html/gitdir1/git-core/git-http-backend/
ScriptAlias /gitdir2/ /var/www/html/gitdir2/git-core/git-http-backend/
<Directory "/var/www/html/gitdir1/git-core">
    AuthType Basic
    AuthName "Authentication Required"
    AuthUserFile "/etc/apache2/.htpasswd"
    AuthGroupFile "/etc/apache2/groups"
    Require group ADMIN
    Options +ExecCGI -MultiViews -Indexes

    Order allow,deny
    Allow from all
</Directory>
<Directory "/var/www/html/gitdir2/git-core">
    AuthType Basic
    AuthName "Authentication Required"
    AuthUserFile "/etc/apache2/.htpasswd"
    AuthGroupFile "/etc/apache2/groups"
    Require group ADMIN GITGROUP
    Options +ExecCGI -MultiViews -Indexes

    Order allow,deny
    Allow from all
</Directory>
<Location "/gitdir1/">
    SetEnv GIT_PROJECT_ROOT /var/www/html/gitdir1
    SetEnv GIT_HTTP_EXPORT_ALL

    AuthType Basic
    AuthName "Authentication Required"
    AuthUserFile "/etc/apache2/.htpasswd"
    AuthGroupFile "/etc/apache2/groups"
    Require group ADMIN
</Location>
<Location "/gitdir2/">
    SetEnv GIT_PROJECT_ROOT /var/www/html/gitdir2
    SetEnv GIT_HTTP_EXPORT_ALL

    AuthType Basic
    AuthName "Authentication Required"
    AuthUserFile "/etc/apache2/.htpasswd"
    AuthGroupFile "/etc/apache2/groups"
    Require group ADMIN GITGROUP
</Location>

Solution

  • I believe I had a similar need. I was able to get my solution working on a RedHat server.

    Solution Using Apache 2.4

    The problem I found is that the git-http-backend script really controls all access to the repositories. Additionally, ScriptAlias is configured as such that it will be routing all /git/ requests to git-http-backend, regardless of the rest of the path. And, because you have provided access to this script for GITGROUP, that group will have access to any repository starting with /git/ in the URL. As per the documentation,

    https://git-scm.com/docs/git-http-backend

    git-http-backend knows the location to each repository because Apache is providing it with an environment variable. I suspect this means that git-http-backend is the only process on the server that directly accesses your repositories, and then provides the response back to Apache.

    My solution adds a few extra steps. Essentially, you would force any access to git-http-backend to occur only after you have already authenticated to the location of your repository, not before. This is done by placing a link to the script in the repository directory itself, and then authenticating that location.

    NOTE: I've only done this with bare repositories (git init --bare). I suspect that if for some reason you need to use a non-bare repository, this solution would need some tweaking (Ideas on that at the end):

    In a nutshell:

    1. Create a link to git-core in each repository (This is where I suspect you would have an issue if you were to use a non-bare repository, see below for idea on that)
    2. Use ScriptAliasMatch rather than ScriptAlias, so that you can match the request to the requested repository only, and direct the request to the symlink.
    3. Create a <Location> directive for each repository, and set the GIT_PROJECT_ROOT variable individually for each site there. This is because apache is no longer going to be providing the correct path of the repository to git.
    4. Create a <Directory> directive for each symlink. The sole purpose of this for me was to set the options for the directory, like -Indexes. You might use this for other options as needed.

    Details:

    1. The link. If your repository is /var/www/html/git/subdir, and your git-httpd-backend is in /usr/libexec/git-core, the command would be:

    ln -s /usr/libexec/git-core /var/www/html/git/subdir/git-core

    1. The Alias. This will use a regex with two backreferences, one ($1) to grab the path of the repository, and another ($2) to get the command (git-upload-pack, etc). It then aliases this to the symlink you created in step one. In my regex, I also ensure that the request is a valid git request. It assumes a request to your site would start look like https://yoursite/git/some_repo.git:
    ScriptAliasMatch \
      "(?x)^/git/(.*/)((HEAD | \
        info/refs | \
        objects/(info/[^/]+ | \
        [0-9a-f]{2}/[0-9a-f]{38} | \
        pack/pack-[0-9a-f]{40}\.(pack|idx)) | \
        git-(upload|receive)-pack))$" \
        "/var/www/html/git/$1/git-core/git-http-backend/$2"
    
    1. Create the <Location> directives. This is where you can set the path to the repository for git, and you can also add authentication here:
    <Location /git/subdir1.git>
        SetEnv GIT_PROJECT_ROOT /var/www/html/git/subdir1.git
        SetEnv GIT_HTTP_EXPORT_ALL
    
        AuthType Basic
        AuthName "Authorization Required"
        ... <the rest of your authentication details here> ...
    </Location>
    
    <Location /git/subdir2.git>
        SetEnv GIT_PROJECT_ROOT /var/www/html/git/subdir2.git
        SetEnv GIT_HTTP_EXPORT_ALL
    
        AuthType Basic
        AuthName "Authorization Required"
        ... <the rest of your authentication details here> ...
    </Location>
    
    1. Create the <Directory> directive. For each repository, create a directory for the symlink you created in step A:
    <Directory /var/www/html/git/subdir1.git/git-core>
        Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch -Indexes
        Allowoverride None
    </Directory>
    
    <Directory /var/www/html/git/subdir2.git/git-core>
        Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch -Indexes
        Allowoverride None
    </Directory>
    

    If you're not using a bare repository:

    I only use bare repositories on the git server, but I think that you might see an issue creating this symlink if you didn't have bare repositories. What I'm worried is that someone could somehow add your link to the repository. I'm not sure if this is possible, but a couple of ideas to get around it:

    1. Create a git-ignore for each link, although the repo user might be able to undo this.
    2. Create the symlink in some other directory, outside of the repository directory. The only issue with this I can think of right now, is that you might need to add a second layer of authentication to your directive.