Configuring Mercurial with Active Directory Authentication and Per-Project Group Authorization


3 views

When setting up Mercurial to work with Active Directory (AD), you'll need to configure both authentication (who can access) and authorization (what they can do). The most common approach is through HTTP-based repositories with proper server configuration.

Apache Configuration Example

For Apache, you can use mod_auth_kerb for Kerberos authentication:


LoadModule auth_kerb_module modules/mod_auth_kerb.so

<Location /hg>
    AuthType Kerberos
    AuthName "Mercurial AD Authentication"
    KrbMethodNegotiate On
    KrbMethodK5Passwd On
    KrbServiceName HTTP
    KrbAuthRealms YOURDOMAIN.COM
    Krb5Keytab /etc/apache2/http.keytab
    Require valid-user
</Location>

To implement project-specific access control, you'll need to combine AD group membership checks with Mercurial's access control features. Here's how to set it up:

Using hgweb.config with AD Groups


[web]
allow_push = *
push_ssl = false

[hooks]
pretxnchangegroup.ad_auth = python:ad_auth.hook

[extensions]
hgsubversion = 
acl =

Create a Python hook (ad_auth.py) to verify group membership:


import ldap
from mercurial import util

def hook(ui, repo, hooktype, node=None, source=None, **kwargs):
    ldap_server = 'ldap://your-ad-server'
    base_dn = 'DC=yourdomain,DC=com'
    group_dn = 'CN=ProjectGroup,OU=Groups,DC=yourdomain,DC=com'
    
    try:
        conn = ldap.initialize(ldap_server)
        conn.simple_bind_s()
        
        user = os.environ.get('REMOTE_USER').split('@')[0]
        filter = f'(memberOf:1.2.840.113556.1.4.1941:={group_dn})'
        result = conn.search_s(base_dn, ldap.SCOPE_SUBTREE, 
                             f'(&(objectClass=user)(sAMAccountName={user}){filter})')
        
        if not result:
            ui.warn(f'User {user} not in required AD group\n')
            return True  # Block the push
    except Exception as e:
        ui.warn(f'AD verification failed: {str(e)}\n')
        return True
    
    return False  # Allow the push

For Windows environments using IIS, you can leverage Windows Authentication:

web.config Setup


<configuration>
  <system.webServer>
    <security>
      <authentication>
        <windowsAuthentication enabled="true" />
        <anonymousAuthentication enabled="false" />
      </authentication>
    </security>
    <handlers>
      <add name="Mercurial" path="*.hg" verb="*" 
           modules="IsapiModule" scriptProcessor="C:\path\to\hgwebdir.cgi"
           resourceType="Unspecified" requireAccess="Script" />
    </handlers>
  </system.webServer>
</configuration>

For more complex scenarios, consider these additional approaches:

  • Using RhgWeb (Mercurial plugin for IIS with AD support)
  • Setting up a reverse proxy with NGINX handling AD authentication
  • Implementing a custom WSGI middleware for authorization

The exact implementation will depend on your specific AD structure and security requirements, but these examples provide a solid foundation for integrating Mercurial with Active Directory authentication and group-based authorization.


When implementing Mercurial in an enterprise environment with Active Directory (AD), we need to address two critical security layers:

  1. Central Authentication: Verify user credentials against AD before granting any repository access
  2. Repository-Level Authorization: Control push/pull permissions based on AD group membership

The most robust solutions work with either Apache or IIS:

# Apache configuration example (httpd.conf)
LoadModule authnz_ldap_module modules/mod_authnz_ldap.so
LoadModule ldap_module modules/mod_ldap.so

<Location /hg>
  AuthType Basic
  AuthName "Mercurial Repositories"
  AuthBasicProvider ldap
  AuthLDAPURL "ldap://domaincontroller:389/DC=domain,DC=com?sAMAccountName?sub?(objectClass=*)"
  AuthLDAPBindDN "CN=binduser,CN=Users,DC=domain,DC=com"
  AuthLDAPBindPassword "password"
  Require valid-user
</Location>

For IIS implementation, we recommend using the IIS URL Authorization module with Windows Authentication:

# Web.config example for IIS
<configuration>
  <system.webServer>
    <security>
      <authentication>
        <windowsAuthentication enabled="true" />
        <anonymousAuthentication enabled="false" />
      </authentication>
      <authorization>
        <add accessType="Allow" users="*" />
      </authorization>
    </security>
  </system.webServer>
</configuration>

Using the hgweb.config file to control repository access:

[web]
allow_push = ADGROUP\Developers
allow_read = ADGROUP\Developers, ADGROUP\QA
push_ssl = false

[hooks]
pretxnchangegroup.ad_auth = python:mercurialhooks.validate_ad_group

Create a custom hook (mercurialhooks.py) to validate AD group membership:

import ldap
from mercurial import util

def validate_ad_group(ui, repo, **kwargs):
    server = ldap.initialize('ldap://domaincontroller')
    server.simple_bind_s('binduser@domain.com', 'password')
    
    user = os.environ.get('REMOTE_USER')
    if not user:
        raise util.Abort('Authentication required')
    
    groups = ['CN=Developers,OU=Groups,DC=domain,DC=com']
    user_dn = get_user_dn(server, user)
    
    if not check_membership(server, user_dn, groups):
        raise util.Abort('User not authorized for this operation')

def get_user_dn(server, username):
    result = server.search_s('DC=domain,DC=com', 
                           ldap.SCOPE_SUBTREE,
                           '(sAMAccountName=%s)' % username,
                           ['distinguishedName'])
    return result[0][1]['distinguishedName'][0]

def check_membership(server, user_dn, allowed_groups):
    for group in allowed_groups:
        result = server.search_s(group,
                               ldap.SCOPE_BASE,
                               '(member=%s)' % user_dn)
        if result:
            return True
    return False
  • Always use SSL/TLS for LDAP connections (ldaps:// on port 636)
  • Cache AD group memberships to reduce authentication overhead
  • Implement proper error handling for AD connectivity issues
  • Consider using Kerberos for single sign-on in Windows environments
Error Solution
403 Forbidden Verify AD group names and user membership
LDAP bind failures Check service account credentials and permissions
Performance issues Implement connection pooling for LDAP