= LDAP and Trac =
In desperation, since the problem-space is too vast to understand and
I am making no forward progress, I am taking to writing to hopefully
at least record my efforts.
My mission is to get Trac + AccountManager to authenticate using
LDAP. I know people do this, so….how hard can it be? Well, its
probably one of the vast majority of things that is easy if you know
what you’re doing and very hard if you don’t. I don’t know what I’m
doing, so I’ve devoted several hours to this so far.
So I go to the AccountManager wiki page and look at the section on
LDAP ( http://trac-hacks.org/wiki/AccountManagerPlugin#LDAP ).
Its…well, after reading I know I’m going to be in for the ride:
"""
Check LDAPAuthStore regarding how to link LdapPlugin to AccountManagerPlugin.
"""
Well, it’d be cool if LDAPAuthStore linked to a wiki page…but it
actually links to http://trac-hacks.org/ticket/1147 , which is, as one
might expect, a very long ticket.
Aside: Don’t put documentation in tickets! Its a horrible idea.
Documentation == something humans can read that lets them know how
things work. Tickets are conversations (like mailing lists and blog
posts). If I wanted to learn about cars, I probably wouldn’t start by
listening to Henry Ford’s conversations with Alexander Malcomson.
So I read the ticket, including the very helpful recollection of CC
changes and all of that. There is a python module — actually,
thirteen modules and patches — attached to the ticket that is an LDAP
authentication backend. It also requires the LdapPlugin (more on that
later).
So I read the ticket. And I read again, trying to figure out what to
do. Well, one helpful tidbit:
"""
Leave the apache setting same as after AccountManager is
installed. Don’t follow LdapPlugin’s apache setting.
Follow LdapPlugin’s trac.ini setting. didn’t use its
Permission/Groups part. It requires customize attributes
(tracperm) to be added to the LDAP server schema.
"""
Several hundred lines later, a stnank is documented
(http://trac-hacks.org/ticket/1147#comment:11 and
http://trac-hacks.org/ticket/1147#comment:12) :
"""
01/27/09 09:53:41 changed by hoffmann@…
- status changed from closed to reopened.
- resolution deleted.
Replying to iamer@open-craft.com:
It is working for me, can you please check your trac
configuration, and try to describe the problem more clearly ? Also
turn on debugging and see if there are any related messages
there. I am not the original author of the patches, I just merged
them and did a little modification.
Same dor me, it is not working. I am getting ERROR: Skipping
"acct_mgr.ldap_store = acct_mgr.ldap_store": (can’t import "No module
named tracusermanager.api") inside my logfile. I am using trac 0.11.2
Might that bew the problem?
01/27/09 10:15:49 changed by anonymous ¶
- status changed from reopened to closed.
- resolution set to fixed.
Installing the UserManagerPlugin resolved the issue
"""
Ah, so I need the UserManagerPlugin too. Um, even though all I really
want (right now, anyway) is authentication. Hmm, well, good to know.
Way way way later on in the ticket (
http://trac-hacks.org/ticket/1147#comment:26 ) mgood rises to the
level of sanity (too bad his suggestion was never taken):
"""
05/20/09 06:25:17 changed by mgood ¶
- status changed from reopened to closed.
- type changed from defect to enhancement.
- resolution set to wontfix.
- summary changed from IndexError: list index out of range to Add
- LDAP authentication backend.
Please create a separate plugin for this backend. I’d rather not add
the extra dependencies that this requires, but it could benefit from
being in version control and having its own issue list. It should make
it easier if users can install that plugin rather than trying to keep
track of the all these patches.
"""
Yeah, that would be nice. Too bad after that things proceeded as
normal instead of discussing this further. The last comment on the
ticket ( http://trac-hacks.org/ticket/1147#comment:32 )is pretty telling too (reminds me of the boat I am in):
"""
08/15/09 23:17:14 changed by rgrant@…
Is there some concise list of tasks to perform on a new install of
TRAC to get AccountManager working with LDAP? This forum seems to be
focused on fixing bugs in existing installs.
"""
In an attempt to get out of further spelunking, I followed a
lead in a recent email to trac-users and looked at the
TracLDAPPlugin. This started off challenging and ended with no
results. Firstly, its not hosted on trac-hacks.org, so I had to
resort to google to find it. Turns out it is here:
http://pypi.python.org/pypi/TracLDAPAuth/ . Interestingly, the pypi
page says the homepage is on trac-hacks:
http://trac-hacks.org/wiki/LDAPAuthPlugin . But going to that page,
the first line is "I’m not the developer or maintainer of the
LDAPAuthPlugin, and this is not a real reference…". Wonderful. But
looking at the plugin, it looks like exactly what I want. Instead of
being a UserManager tie in (which is more than I need, at least right
now), the TracLDAPAuth plugin is only an IPasswordStore for
AccountManager, which is exactly what I want.
So I install the plugin, which goes rather smoothly except installing
python-ldap, which of course fails to compile the C extensions due to
a missing .h file. python, as a language designed for human beings
rather than computers, should really solve this problem instead of
just invoking a C compiler and running off into the blue, or if not
"solve" at least "make better". Like a nice warning, "Oh, you don’t
have this needed .h file. Well, you should get it here." That would
be friggin nice. But I digress. So I get bored and apt-get install
python-ldap, which of course isn’t listed in a dependency of setup.py
(and neither is AccountManager). The installing the plugin works
smoothly. I fill out my [ldap] section (correctly, mind you, and yes,
I’m sure because I tested this later) and enable the plugin (correctly
again) and I go to login. Nothing. Denied. So I look at the
check_password code. Its rather short:
- def check_password(self, user, password):
-
self.log.debug(’LDAPAuth: Checking password for user %s’,
user)
try:
l = ldap.open(self.server_host)
bind_name = (self.bind_dn%user).encode(’utf8′)
self.log.debug(’LDAPAuth: Using bind DN %s’, bind_name)
l.simple_bind_s(bind_name, password.encode(’utf8′))
return True
- except ldap.LDAPError:
- return False
Hell, short enough to debug by hand. So I do. The following results:
>>> l.simple_bind_s(bind_name, password.encode('utf8'))
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "/usr/lib/python2.4/site-packages/ldap/ldapobject.py", line
199, in simple_bind_s
return self.result(msgid,all=1,timeout=self.timeout)
File "/usr/lib/python2.4/site-packages/ldap/ldapobject.py", line
428, in result
res_type,res_data,res_msgid = self.result2(msgid,all,timeout)
File "/usr/lib/python2.4/site-packages/ldap/ldapobject.py", line
432, in result2
res_type, res_data, res_msgid, srv_ctrls =
self.result3(msgid,all,timeout)
File "/usr/lib/python2.4/site-packages/ldap/ldapobject.py", line
438, in result3
rtype, rdata, rmsgid, serverctrls =
self._ldap_call(self._l.result3,msgid,all,timeout)
File "/usr/lib/python2.4/site-packages/ldap/ldapobject.py", line 97,
in _ldap_call
result = func(*args,**kwargs)
ldap.CONFIDENTIALITY_REQUIRED: {'info': 'TLS confidentiality
required', 'desc': 'Confidentiality required'}
Oh yeah, we have TLS, which the TracLDAPAuth plugin doesn’t
enable. But since its not on trac-hacks, my first step is to get it
there so I can ticket and/or make a vendor branch. So I’ll play with
TLS via the LDAP module. I look for the docs and find them at
http://www.python-ldap.org/ . The first question in the FAQ isn’t
exactly promising:
Q: Is python-ldap yet another abandon-ware project on SourceForge?
Yiy. Well, let’s ignore that for now and look at the API. There is
an ill-documented start_tls_s function that looks like what I want:
http://www.python-ldap.org/doc/html/ldap.html#ldap.LDAPObject.start_tls_s
. Let’s try it:
>>> l.start_tls_s()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "/usr/lib/python2.4/site-packages/ldap/ldapobject.py", line
528, in start_tls_s
return self._ldap_call(self._l.start_tls_s)
File "/usr/lib/python2.4/site-packages/ldap/ldapobject.py", line 97,
in _ldap_call
result = func(*args,**kwargs)
ldap.CONNECT_ERROR: {'desc': 'Connect error'}
Hmm, well, that’s awful. But I try it on my server and it magically
works!
Well, what’s the difference? Running strace, strace python -c
‘import ldap; l = ldap.open("ldap-master.openplans.org");
l.start_tls_s()’ yields the difference that on the server there is an
open call that reads from /etc/openldap.ldap.conf and on my box it
reads from /etc/ldap/ldap.conf. It does not try the other file in
either case, and no other relevent configuration (that I can tell,
anyway) is read after the python is loaded. I installed all of the
openldap packages and still on my machine it tries to read from
/etc/ldap/ldap.conf.
I mailed the author of TracLDAPAuth and requested his permission to
put it on trac-hacks so that I may more sanely tackle this issue. I
also asked the mailing list (link not sited as google groups fails to
put them in the email messages) what plugin I should use for LDAP. I
was nudged towards http://trac-hacks.org/ticket/1147 . So I decided
to bark down that road again.
I follow mgood’s suggestion and make a real host-to-live plugin out of
it and install and enabled for my Trac instance. I spent several
hours trying to get this to work with my LDAP server. No go. Not
sure if the plugin is fuxored or if the LdapPlugin is fuxored or if I
just don’t know enough about Ldap to configure it. But that this
point, I had wasted three days on the problem and was tired of it. I
did do something nice and put my plugin on
http://trac-hacks.org/wiki/LdapAuthStorePlugin so that tickets could
be filed against it, someone could adopt it, it could be versioned,
and all of those nice things associated with sane software
development. If you want it, just mail me and its yours.
So figuring that http://pypi.python.org/pypi/TracLDAPAuth/ seemed to
magically work on the servers…wait, an aside. When things magically
work — that is, they work and I don’t understand why, or in this case
they work on our PRODUCTION SERVERS and not on my development and
testing box, I get really worried. Quick + dirty says, "It works,
don’t worry about it." But I’ve been doing this long enough to know
that a tiger lurks in that closet. Meaning: lots more hours when
things stop magically working, possible unknown side-effects, possible
security problems (I mean, we are dealing with LDAP here…like, you
know, that thing we keep our passwords in. Let’s be careful, shall
we?), etc. Well, back to topic, I could rant all day.
So figuring that http://pypi.python.org/pypi/TracLDAPAuth/ seemed to
magically work on the servers, I decided to put this on trac-hacks and
hopefully the author wouldn’t complain too much and then add the two
lines necessary to support TLS and other fixes that might be
necessary. So I did: http://trac-hacks.org/wiki/TracLdapAuthPlugin
. And I emailed the author telling him what I did. I hope he’ll take
ownership or at least won’t mind. I try to add python-ldap
(remember that messy thing?) to install_requires in the setup.py
file. Well, that didn’t work so well, so I had to add the following
code to the top of the file:
- try:
- import ldap
- except ImportError:
- print """The python-ldap package isn’t installed on your system
- (import ldap failed). I would just put this in install_requires,
- but
I do have python-ldap on my system and I get this:
{{{
Processing dependencies for TracLDAPAuth==1.0
Searching for python-ldap==2.3.1
Reading http://pypi.python.org/simple/python-ldap/
Reading http://python-ldap.sourceforge.net/
Reading
http://sourceforge.net/project/showfiles.php?group_id=2072&package_id=2011
No local packages or download links found for python-ldap==2.3.1
error: Could not find suitable distribution for
Requirement.parse(’python-ldap==2.3.1′)
}}}
Well, that’s awful. If you know how to fix this, please file a ticket
at
http://trac-hacks.org/newticket?component=TracLdapAuthPlugin&owner=k0s
"""
System Message: ERROR/3 (trac-ldap.txt, line 285)
Unexpected indentation.
sys.exit(1)
Well, such is the state of python packaging (FIX IT! FIX IT! FIX
IT!). And I added the two lines that will add TLS (
http://trac-hacks.org/changeset/6606 ). So I’m going to mail TOPP
Labs Operations just to make sure we want to test and develop
code directly related to our security.
Looking at http://trac-hacks.org/tags/’ldap‘ , there are at least a
few other related resources in this twisted web:
-
http://trac-hacks.org/ticket/1600 : a patch for AccountManager that
also claims to provide LDAP authentication. Haven’t looked at it
-
http://trac-hacks.org/wiki/AccountLdapPlugin : "Allows you to
change your password defined in LDAP. Also moved the basic
properties of LDAP (user and mail) to the corresponding properties
in Trac". So it doesn’t claim to do auth, but it is heavily
overlapping with the above patches and plugins that do
- LdapPlugin : "LDAP support with group management has been added as
a Trac extension. This extension enables to use existing LDAP
groups to grant permissions rather than defining permissions for
every single user on the system." So again no support for auth, but
other plugins depend on it. Ho hum. In my opinion, auth should go
with this plugin. If people don’t want to use the auth
component, then don’t enable it.
-
http://trac-hacks.org/wiki/TracSysgroupsPlugin : "A permission
system-group provider for Trac. It asks linux / unix system to
which groups a validated user belongs. If one of this groups
matches a permission group name created with trac-admin permission
add command, these permissions are enabled for logged-on user."
So I’m not sure why its tagged with "ldap", save for the loose
association that unix can use LDAP for authentication and groups.
I also find it "funny" that AccountManager is tagged with "ldap" even
though it doesn’t support it OOTB.
So concludes my foray in LDAP + Trac. I’m still confused, but maybe,
just maybe you’ll be less confused than you were before upon reading this.