hi,
have setup ejabberd with ldap. user from ldap can login. problem is, that each user which is in my ldap has unter his tree a adressbock. so when i want to use the webadministration util for users, it shows me the thousands adressbook users. any one a hint, how i can filter the ldap users in webadmin? maybee schema filters or anything else?
greets
lin
LDAP filtering
Hey Lin,
[This turned out to be a long reply, so grab yourself a cup of your favorite beverage and get comfortable. :-) ]
I'm just an ejabberd user myself, but thanks for bringing this up. I've been testing with ejabberd 1.0.0 on Windows XP Pro SP2 using OpenLDAP for user authentication, and I never noticed this. Of course, I only had some test users in my LDAP tree. :-) But I noticed once I added more users, or added users anywhere else underneath the LDAP tree, you are absolutely right. They suddenly show up in the list of users via the ejabberd web admin tool.
Digging through the config file and the docs, I don't see any place where one can apply a filter for this. I see no way to filter the users externally; that is, outside of the LDAP server itself. And I see no way to even apply a LDAP search filter from within ejabberd. The truth is, documentation on LDAP use from ejabberd is spartan at best, and LDAP support in ejabberd is quite simplistic, lacking support even for such basic features as LDAPS.
I suspect that currently, if you want to restrict which users can login to ejabberd, you'll have to do this external to ejabberd itself. I don't know your setup as you fail to mention your particulars(see footnote 1)--what OS you're running ejabberd under, what LDAP server you're using, how your LDAP tree is configured, etc.--making specific instructions difficult. But let me use my test setup (with obvious adjustments) as an example and maybe this will help explain things.
____________________________________________________________
CONFIGURATION:
* Windows XP SP2
* ejabberd 1.0.0
* OpenLDAP for Windows v2.2.29 (made by Matthias Mohr)
* LDAP Admin GUI for manipulating LDAP tree
* all services running on same PC; ejabberd hooked into
OpenLDAP via loopback (i.e., localhost, 127.0.0.1)
* DNS entries appropriately mapped to PC's IP address so
full Jabber/XMPP communications work
* various Jabber/XMPP clients including but not limited to
* Pandion v2.5
* Exodus v0.9.1.0
* GAIM v2.0.0beta2 for Windows
____________________________________________________________
LDAP SETUP (SIMPLIFIED)
SCHEMA portion of slapd.conf:
________________________________________________________
...
########################################################
# Schemas
########################################################
## Include the minimum schema required.
include ./schema/core.schema
## Added to support the inetOrgPerson object.
include ./schema/cosine.schema
include ./schema/inetorgperson.schema
## Added to support LDAP Admin tool for creating user profiles
include ./schema/nis.schema
include ./schema/postfix.schema
...
________________________________________________________
rootsuffix: dc=myserver,dc=com
+--ou=people
| +---uid=admin ; user to admin ejabberd via web interface
| | +---ou=xmpp
| +---uid=user1 ; ejabberd user
| | +---ou=xmpp
| +---uid=user2
| +---uid=user3 ; ejabberd user
| | +---ou=xmpp
| +---uid=user4
|
+--ou=services
+---uid=ejabberd
Basically, I have an LDAP tree with two branches: 'people' and 'services'. The 'ejabberd' user is being setup for a reason which will become apparent soon enough. Though hard to do in ASCII graphics, note that three users--'admin', 'user1' and 'user3'--have an attribute 'ou' set to 'xmpp', while the others do not. Users have objectClass=inetOrgPerson, which allows for adding 'ou' attributes due to inheritance of objectClass=organizationalPerson schema.
Note all users have passwords set.
____________________________________________________________
EJABBERD SETUP
I had ejabberd.cfg configured for LDAP similar to below:
____________________________________________________________
...
{auth_method, ldap}.
{ldap_servers, ["localhost"]}. % List of LDAP servers
{ldap_uidattr, "uid"}. % LDAP attribute that holds user ID
{ldap_base, "dc=myserver,dc=com"}. % Base of LDAP directory
{ldap_rootdn, "cn=Manager,dc=myserver,dc=com"}. % LDAP manager
{ldap_password, "secret"}. % Password to LDAP manager
...
____________________________________________________________
Notice I was doing an authenticated bind as the rootdn, which works just fine. However, when you sign into the web interface (using the 'admin' user) and look, you'll see ALL the users sorted in alphabetical order, as in
____________________
admin@myserver.com
ejabberd@myserver.com
user1@myserver.com
user2@myserver.com
user3@myserver.com
user4@myserver.com
____________________
This is not what I wanted, as only some users were to have access to ejabberd. Left as is, ANY user within the LDAP tree (assuming a password had been set for them, of course), regardless of what subtree they were under, could login to ejabberd.
The first attempt to limit this was to change the one line in ejabberd.cfg to read
____________________________________________________________
...
{ldap_base, "ou=people,dc=myserver,dc=com"}. % Base of LDAP directory
...
____________________________________________________________
This restricted access to just the one subtree. But as many people operate LDAP directories where they wish to pool all user accounts but give each user only access to certain, specific services, this still left me seeing 'user2' and 'user4', neither of which I really wanted.
____________________
admin@myserver.com
user1@myserver.com
user2@myserver.com
user3@myserver.com
user4@myserver.com
____________________
Now notice I chose to add an attribute 'ou=xmpp' to tag those users which should have access to ejabberd, including the admin account for ejabberd. This is one of three common approaches to handling group permissions in LDAP. You can
1. structure things by placing all users for a given service under a single subtree; that is, for each service, create an organizationalUnit, and under that create the users who will use that service
2. add a multi-value capable attribute to each user for a given service (i.e., 'ou=xmpp' for ejabberd users; 'ou=ftp' for users permitted to use FTP, etc.)
3. use some combination of the above involving aliases; that is, create one organizationalUnit under which you place all users, then create an organizationalUnit for each service, under which you create aliases to those users which should be allowed to access the service
(I can only guess at this, as I've predominantly been working with OpenLDAP, but have been reading up on others such as Microsoft's Active Directory Application Mode (ADAM) server, which is basically a free LDAP server, and it is clear each LDAP server implementation has its own features/limitations.)
Approach #1 works well if each user uses ONE and ONLY one service. If you have users that need to access both ejabberd and FTP, for example, then Approach #1 requires having duplicates of users, which makes a mess when it comes to updating passwords, etc. In database parlance, this could lead to data anomalies, where one instance of a user has one passoword, and another has another.
Worse, you must keep in mind that when doing lookups in an LDAP tree for authentication purposes, it is ESSENTIAL that there is only ONE record that matches the search. These duplicates introduce a real headache if you don't set your basedn properly, as you may have multiple entries matching your search, which in the case of ejabberd means the user simply cannot login. (I ran into this several times while testing, and believe me, it's annoying. If your users can't authenticate, try doing the search by hand with the LDAP search tool of your choice, and see just how many entries are returned. If it's more than one, there's your problem.)
Approach #2 works well if you can search/filter for users of a given service. The downside is that, if your LDAP tree is sufficiently large, each lookup can take awhile as it requires searching the entire tree/subtree as defined by the basedn setting, and there's no easy visual way in your LDAP GUI to see all the users who belong to a given group.
Approach #3 gives you the visual tree structure in your LDAP GUI, but requires that the LDAP client can be set to dereference all aliases or the LDAP can be set to automatically do the same (the latter does not appear to be an option in OpenLDAP from what I can tell; not sure which, if any, servers can do this).
For my purposes, I opted for Approach #2, which allows me to 'tag' each user that should be allowed to use a given service. I then simply need to search/filter for those users.
And this is where your original question comes in. Based on what I've read on the ejabberd site, the docs, etc., I see no place for you to specify a filter in the ejabberd configuration to restrict just which users you find in your LDAP tree. So what can one do?
Well, if you're using OpenLDAP, for example, there's another approach to this: Access Control Lists (ACLs). ACLs allow you to specify WHO can access WHAT and HOW. I won't go into too much detail here, as you may be using another LDAP server entirely. But let's use my final setup to give you a taste.
In the end, I opted to create a subtree in my LDAP server which I named 'ou=services', and in there I am creating "users" which will represent each service (Apache, ejabberd, FTP server, etc.) that will use the LDAP server. So under 'ou=services' I created 'uid=ejabberd', setting a password for this user. The idea here is that I will have each service bind to the LDAP server as a specific user, which in turn lets me know just which service is looking for information.
I then modified my ejabberd.cfg LDAP config as follows:
____________________________________________________________
...
{auth_method, ldap}.
{ldap_servers, ["localhost"]}. % List of LDAP servers
{ldap_uidattr, "uid"}. % LDAP attribute that holds user ID
{ldap_base, "dc=myserver,dc=com"}. % Base of LDAP directory
{ldap_rootdn, "uid=ejabberd,ou=services,dc=myserver,dc=com"}. % LDAP manager
{ldap_password, "ejabberdsecret"}. % Password to LDAP manager
...
____________________________________________________________
Having done this, and restarting the LDAP and ejabberd services, I now get the same results as before, showing me ALL the users.
____________________
admin@myserver.com
ejabberd@myserver.com
user1@myserver.com
user2@myserver.com
user3@myserver.com
user4@myserver.com
____________________
So the next step is to modify the ACLs in OpenLDAP's slapd.conf file, the configuration file for the LDAP server. Until now my ACLs simply read as follows:
____________________________________________________________
...
############################################################
# Access Control Lists (ACLS)
############################################################
# Restrict userPassword to be used for authentication only,
# but allow users to modify their own passwords
access to attrs=userPassword
by self write
by * auth
# Simple ACL granting read access to the world
access to *
by * read
____________________________________________________________
The comments should make the ACLs self-explanatory. In essence, the first rule restricts access to the 'userPassword' atrribute so that only the owner of the password has write access to modify it. Everyone else has auth access, which means they can authenticate the user, but they can neither change the password nor even read it. OpenLDAP ACLs are "first match wins", so any LDAP client not attempting to access a user's password has access to the rest of the LDAP tree. This explains why in the ejabberd web interface we still see ALL the users.
But by changing the ACLs to the following, we get what we want:
____________________________________________________________
############################################################
# Access Control Lists (ACLS)
############################################################
# Restrict userPassword to be used for authentication only,
# but allow users to modify their own passwords
access to attrs=userPassword
by self write
by * auth
# Limit access of ejabberd to just XMPP users
access to *
filter=(ou=xmpp)
by dn="uid=ejabberd,ou=services,dc=myserver,dc=com" read
# Simple ACL granting read access to the world
access to *
by dn="uid=ejabberd,ou=services,dc=myserver,dc=com" none
by * read
____________________________________________________________
The first rule remains the same, allowing authentication to proceed as before.
The second rule, in effect, says "For ANY entries in the LDAP tree with an attribute of 'ou=xmpp' set, user ejabberd has read access."
The third rule is as before, with one addition. The addition basically says "Deny user ejabberd from accessing ANYTHING in the tree" while allowing all others read access.
Since OpenLDAP ACLs are "first match wins", when ejabberd tries to see if a user exists, it will ONLY see those users which have the 'ou=xmpp' attribute set.
____________________
admin@myserver.com
user1@myserver.com
user3@myserver.com
____________________
Thus the web interface now only shows the appropriate users. And since any attempt to authenticate a user is actually made up of two requests, beginning with searching to see if a user exists in the tree, any attempt by a user who does NOT have 'ou=xmpp' set to login to the ejabberd server will fail. ejabberd will first search to find the entry for the user (e.g., user2), and since that fails, no authentication will be attempted.
I'm not sure if you're still with me at this point, or if as usual I have provided completely too much information. But your original question made me realize my test setup was not secure, and since I delved into this and found a solution, I thought I'd pass along what I'd found.
Now if you're using a different LDAP server, I'm not sure what steps you need to go about doing. I've been meaning to install/toy with the MS ADAM server I mentioned above, but I simply haven't had the time yet. But hopefully the information above, if nothing else, will steer you in the right direction. And if you learn anything useful in the process, please share it here so that others might benefit.
____________________________________________________________
http://www.catb.org/~esr/faqs/smart-questions.html
(1) I found the following invaluable for advice/tips on how to ask questions. You may want to check it out:
first i want you to thank
first i want you to thank you for this realy big answer ;) thanks
second it work now, i had a field "description" of any user, but not on the adressbook. so i use this field for authentifikation.
thanks one more time
greetings
lin