Tuesday, June 25, 2013

CollabNet Subversion Edge 4.0 with SSPI WSGI and trac

CollabNet released Subversion Edge 4.0 recently, as they've written here. As you know I've been using this to host our Subversion and trac server. I've made a few tweaks and additions but I've never had any issues with updating Subversion Edge. Until now, that is.


This time Collabnet decided to update Apache from 2.2 to 2.4. This breaks compatibility with some of the modules I use. Let's look at mod_auth_sspi first.

The module mod_auth_sspi is used for authentication against an Active Directory. When configured properly, it allows clients to seamlessly connect using single-sign-on. In a corporate world this is a must-have. Unfortunately this module is no longer supported on Apache-2.4.
There is a replacement, however: mod_authnz_sspi. It can almost act as a drop-in replacement for mod_auth_sspi. No wonder, since it started out as a fork.
The changes are small. Instead of
require DOMAIN\Group
the correct syntax is now
require sspi-group  DOMAIN\Group
The module name change too, so the module has to be loaded like this:
LoadModule authnz_sspi_module lib/modules/mod_authnz_sspi.so
Don't worry, I'll post a complete apache config at the end.

The other module that acted up was mod_wsgi. Now if you don't use trac on Subversion Edge you might not even notice this. Even if you use trac, you can still make it work using CGI on windows. I'd really discourage you from doing that, since it's really abnormally slow.
A google search reveals a couple of mod_wsgi binaries built for Apache-2.4. None of them work with Subversion Edge 4.0 (at least none that I've found).

So I had to build it myself. The requirements are:
  • MS Visual C compiler (Visual Studio Express will do)
  • MS Windows SDK
  • Apache 2.4 development/header files
  • Python-2.7.1 development/header files
I downloaded the 3.4 source here. I got Apache 2.4.4 here.I got Python 2.7.1 here, because that's the version that's bundled with Collabnet Subversion Edge.
Note the folders you installed/extracted those files, you'll need them soon. There are a few makefiles for various Apache and Python versions, but none for Apache 2.4 and Python 2.7. So I had to create my own. Here are the contents:
CPPFLAGS = \
 /DWIN32 \
 /DNDEBUG \
 /I"C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include" \
 /I"C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1\Include" \
 /I"C:\Apache24\include" \
 /I"C:\Python27\include"

CFLAGS = \
 /MD \
 /GF \
 /Gy \
 /O2 \
 /Wall \
 /Zc:wchar_t \
 /Zc:forScope

LDFLAGS = \
 /link \
 "/LIBPATH:C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\lib" \
 "/LIBPATH:C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1\Lib" \
 "/LIBPATH:C:\Apache24\lib" \
 "/LIBPATH:C:\Python27\libs" \
 /OPT:REF \
 /OPT:ICF=2 \
 /RELEASE \
 /SUBSYSTEM:WINDOWS \
 /MANIFEST

LDLIBS = \
 python27.lib \
 libhttpd.lib \
 libapr-1.lib \
 libaprutil-1.lib

mod_wsgi.so : mod_wsgi.c
    cl $(CPPFLAGS) $(CFLAGS) $? /LD $(LDFLAGS) $(LDLIBS) /OUT:$@
    mt -manifest $@.manifest -outputresource:$@;2

clean :
    del *.obj *.so *.so.manifest *.lib *.exp
As you can see I've installed Python to C:\Python27 and Apache to C:\Apache24. Feel free to adjust your paths. To actually compile mod_wsgi you have to open a Visual Studio Command Prompt, otherwise some environment variables will be missing and the compiler fails.
I got a lot (100+) warnings from the compiler, but in the end it worked:
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:mod_wsgi.dll
/dll
/implib:mod_wsgi.lib
"/LIBPATH:C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\lib"
"/LIBPATH:C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1\Lib"
/LIBPATH:C:\temp\Apache24\lib
/LIBPATH:C:\Python27-x86\libs
/OPT:REF
/OPT:ICF=2
/RELEASE
/SUBSYSTEM:WINDOWS
/MANIFEST
python27.lib
libhttpd.lib
libapr-1.lib
libaprutil-1.lib
/OUT:mod_wsgi.so
mod_wsgi.obj
   Creating library mod_wsgi.lib and object mod_wsgi.exp
        mt -manifest mod_wsgi.so.manifest -outputresource:mod_wsgi.so;2
Microsoft (R) Manifest Tool version 5.2.3790.2076
Copyright (c) Microsoft Corporation 2005.
All rights reserved.
I copied to mod_wsgi.so file to the csvn/lib/modules/ folder on my Collabnet Subversion Edge server. With all modules in place, it's time to tweak the apache config files.

This is my httpd.conf as it currently resides in csvn/data/conf. The Subversion Edge web interface alters and changes the file, so editing it is always risky. This version seems stable enough though.

ServerRoot "D:\csvn"
Include "data/conf/csvn_modules_httpd.conf"
Include "data/conf/csvn_main_httpd.conf"
Include "data/conf/csvn_logging.conf"
Include "data/conf/csvn_default_dirs_httpd.conf"
#Include "data/conf/svn_viewvc_httpd.conf"
Include "data/conf/csvn_misc_httpd.conf"

LoadModule authnz_sspi_module lib/modules/mod_authnz_sspi.so

##########################################

#
# The following lines prevent .htaccess and .htpasswd files from being
# viewed by Web clients.
#
<FilesMatch "^\.ht">
    Require all denied
</FilesMatch>

LimitXMLRequestBody 0
ServerSignature  Off
ServerTokens  Prod

TypesConfig "D:\csvn\data/conf/mime.types"
AddType application/x-compress .Z
AddType application/x-gzip .gz .tgz

# commented out for safety - contets dumped below
# Include "D:\csvn\data/conf/svn_viewvc_httpd.conf"

Include "D:\csvn\data/conf/ctf_httpd.conf"

SSLEngine On

# Work around authz and SVNListParentPath issue
RedirectMatch ^(/svn)$ $1/
<Location /svn/>  
    DAV svn
    SVNParentPath "D:\csvn\data\repositories"
    SVNReposName "CollabNet Subversion Repository"
#   AuthzSVNAccessFile "D:\csvn\data/conf/svn_access_file"
    SVNListParentPath On

    AuthType SSPI
    SSPIAuth On
    SSPIAuthoritative On
    SSPIDomain DOMAIN
    SSPIOfferBasic On
    SSPIOmitDomain On
    <RequireAll>
        <RequireAny>
        require sspi-group DOMAIN\Group
        </RequireAny>
    </RequireAll>
</Location>

<Directory "D:\csvn\www\viewVC/docroot">
  AllowOverride None
  Options None
  Require all granted
</Directory>
Alias /viewvc-static "D:\csvn\www\viewVC/docroot"

<Location /viewvc-static>
  Require all granted
</Location>

ScriptAlias /viewvc "D:\csvn\bin/cgi-bin/viewvc.cgi"

<Location /viewvc>
    AddDefaultCharset UTF-8
    SetEnv CSVN_HOME "D:\csvn"

    AuthType SSPI
    SSPIAuth On
    SSPIAuthoritative On
    SSPIDomain DOMAIN
    SSPIOfferBasic On
    SSPIOmitDomain On
    require sspi-group DOMAIN\Group
</Location>

LoadModule wsgi_module lib/modules/mod_wsgi.so
WSGIScriptAlias /trac "data/conf/trac.wsgi"

<Location /trac>
    WSGIApplicationGroup %{GLOBAL}

    AuthType SSPI
    AuthName "Trac"
    SSPIAuth On
    SSPIAuthoritative On
    SSPIDomain DOMAIN
    SSPIOfferBasic On
    SSPIOmitDomain On
    <RequireAll>
        <RequireAny>
        require sspi-group DOMAIN\Group
        </RequireAny>
    </RequireAll>
</Location>
So basically the contents of the file svn_viewvc_httpd.conf were appended to the httpd.conf file, so I could adjust the Auth settings.
If you don't use trac, you can obviously skip WSGI and the trac location.

The last missing piece to have trac working is the trac.wsgi script. You can create have trac-admin create a template for you by running
trac-admin <env> deploy <dir>
I did that, and tweaked the resulting file a bit. I'm not sure whether that's the correct way of doing it, but this hack has been working fine on another server for years already.
Here is my version

import os

def application(environ, start_request):
    os.environ['TRAC_ENV_PARENT_DIR'] = 'd:\\trac'
#    if not 'trac.env_parent_dir' in environ:
#        environ.setdefault('trac.env_path', 'd:\\trac\\myproject')
    if 'PYTHON_EGG_CACHE' in environ:                                          
        os.environ['PYTHON_EGG_CACHE'] = environ['PYTHON_EGG_CACHE']
    elif 'trac.env_path' in environ:
        os.environ['PYTHON_EGG_CACHE'] = \
            os.path.join(environ['trac.env_path'], '.egg-cache')
    elif 'trac.env_parent_dir' in environ:
        os.environ['PYTHON_EGG_CACHE'] = \
            os.path.join(environ['trac.env_parent_dir'], '.egg-cache')
    from trac.web.main import dispatch_request
    return dispatch_request(environ, start_request)
Now when I browse to /trac on my Collabnet Subversion Edge server, I am presented the "Available Projects" overview.

Trac will work, but it requires python-subversion bindings. Luckily Collabnet supplies these with Subversion edge. More details here. They can be found in lib/svn-python. Just copy the contents of that folder to csvn/Python-2.5/lib/site-packages. Without doing that Trac will still work but lack svn support.

That's all folks!

If anyone is interested, I can make my build of mod_wsgi available.

3 comments:

Anonymous said...

Thanks for creating this post. We use collabnet as well and sspi-group setting was keeping me from getting ours to work. Added that and got me back on track.

Anonymous said...

Ignore earlier request today, found a functioning module at http://www.apachelounge.com/viewtopic.php?t=5143

abbey said...

very well explained..great job