LDAP Authentication
The ldap_auth
plugin allows users to login to FreeGenes using account information stored in an
LDAP directory. This supports logins against Microsoft Active Directory, as well open-source OpenLDAP etc.
To enable LDAP authentication you must:
- Build the docker image with the build argument ENABLE_LDAP set to true
- Add
ldap_auth
to thePLUGINS_ENABLED
list infg/settings/config.py
- Configure the details of your LDAP directory in
fg/settings/secrets.py
. Seefg/settings/dummy_secrets.py
for an example OpenLDAP configuration. A good start is to do the following:
cp fg/settings/dummy_secrets.py fg/settings/secrets.py
Because no two LDAP directories are the same, configuration can be complex and there are no
standard settings. The plugin uses django-auth-ldap
, which provides more detailed documentation
at Read the Docs.
To test LDAP authentication you may wish to use a docker container that provides an OpenLDAP
directory. mwaeckerlin/openldap
(GitHub) (Docker
Hub) is a useful container configured
with unencrypted, StartTLS, and SSL access to an OpenLDAP directory.
Quick Start
This quick start is intended to demonstrate basic functionality of the LDAP server, and you should review the links referenced above for more detail.
What is LDAP?
LDAP (Lightweight Directory Access Protocol) is a common protocol used by organizations to hold user information and perform authentication. An LDAP directory hold records for each user, and groups which users may belong to.
LDAP directories are implemented by many different directory servers. The most commonly encountered are OpenLDAP on Linux, and Microsoft Active Directory on Windows platforms.
To test FreeGenes LDAP authentication we can use a Dockerized OpenLDAP server.
Create the server
As instructed in https://github.com/mwaeckerlin/openldap and (here!) let’s bring up a dummy LDAP server:
docker run -d --restart unless-stopped \
--name openldap \
-p 389:389 \
-e DOMAIN=my-company.com \
-e ORGANIZATION="Tacosaurus" \
-e PASSWORD=avocados \
mwaeckerlin/openldap
With this command we are:
- Allowing access on port 389 (unecrypted LDAP / StartTLS encrypted)
- Creating a directory that will have a
basedn: dc=my-company,dc=com
. The basedn is the root of the LDAP directory tree. It is usally created by breaking your domain name into domain components (dc). - Creating an admin account, which will have the dn (distinguished name)
cn=admin,dc=my-company,dc=com
and passwordavocados
.
The -d
means “detached” so you won’t see the output in the terminal. If you need to see output, remove the -d
. Here is the running container:
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
398b6297d6ff mwaeckerlin/openldap "/bin/sh -c /start.sh" 3 minutes ago Up 3 minutes 0.0.0.0:389->389/tcp, 636/tcp openldap
Interact with it
Here is a way to get familiar with the executables inside the image for ldap:
docker exec -it openldap bash
root@docker[72b21bd3c290]:/# which ldapadd
/usr/bin/ldapadd
root@docker[72b21bd3c290]:/# which ldapwhoami
/usr/bin/ldapwhoami
Note that the long string with cn= through dc= is your username! The password is the one you set for the image.
root@docker[4ec2c4f2737a]:/# ldapwhoami -x -D 'cn=admin,dc=my-company,dc=com' -W
Enter LDAP Password:
dn:cn=admin,dc=my-company,dc=com
For the password above you would enter the one we set in the environment for the image. In our case this was avocados
.
Add a user and group to the directory
If all has gone well we can check the content of the directory with:
$ ldapsearch -x -b 'dc=my-company,dc=com'
# extended LDIF
#
# LDAPv3
# base <dc=my-company,dc=com> with scope subtree
# filter: (objectclass=*)
# requesting: ALL
#
# my-company.com
dn: dc=my-company,dc=com
objectClass: top
objectClass: dcObject
objectClass: organization
o: Tacosaurus
dc: my-company
# admin, my-company.com
dn: cn=admin,dc=my-company,dc=com
objectClass: simpleSecurityObject
objectClass: organizationalRole
cn: admin
description: LDAP administrator
# search result
search: 2
result: 0 Success
# numResponses: 3
# numEntries: 2
We now need to add some test users and groups to our directory. Create a file called ‘example.ldif’ with the following content:
##
## Basic LDIF for LDAP simulation setup
## D. C. Trudgian Nov 2014
##
# ----------------------------------------------------------------------------
# STRUCTURE
# ----------------------------------------------------------------------------
dn: ou=users,dc=my-company,dc=com
ou: users
objectClass: top
objectClass: organizationalUnit
dn: ou=groups,dc=my-company,dc=com
ou: Group
objectClass: top
objectClass: organizationalUnit
# ----------------------------------------------------------------------------
# Test Groups
# ----------------------------------------------------------------------------
dn: cn=test,ou=groups,dc=my-company,dc=com
cn: test
description: Test User Group
gidNumber: 1000
objectClass: posixGroup
memberUid: testuser
memberUid: testadmin
dn: cn=admin,ou=groups,dc=my-company,dc=com
cn: admin
description: Test Admin Users
gidNumber: 1002
objectClass: posixGroup
memberUid: testadmin
# ----------------------------------------------------------------------------
# Test Users
# ----------------------------------------------------------------------------
dn: uid=testuser,ou=users,dc=my-company,dc=com
displayName: Test User
cn: Test User
title: Tester
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
loginShell: /bin/bash
uidNumber: 1000
gecos: Test User, Test Lab, Test Dept
sn: User
homeDirectory: /home2/testuser
mail: testuser@localhost
givenName: Test
employeeNumber: 1000
shadowExpire: 99999
shadowLastChange: 10000
gidNumber: 1000
uid: testuser
userPassword: {SSHA}IZQSiHPR9A/xKUPTKAM82EJoejtb70vD
dn: uid=testadmin,ou=users,dc=my-company,dc=com
displayName: Test Admin
cn: Test Admin
title: Tester
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
loginShell: /bin/bash
uidNumber: 1001
gecos: Test Admin, Test Lab, Test Dept
sn: Admin
homeDirectory: /home2/testadmin
mail: testadmin@localhost
postalAddress: NL5.136
givenName: Test
employeeNumber: 1000
shadowExpire: 99999
shadowLastChange: 10000
gidNumber: 1000
uid: testadmin
userPassword: {SSHA}84O5yFQxQwvQc1Dluc5fJrehrucmCFdH
This will create a directory with:
- Two organizational units (ou=users, ou=groups) to hold our users and groups
- Two groups, test and admin.
- A user testuser with password testuser who belongs to the test group.
- A user testadmin with password testadmin who belongs to the test and admin groups.
We use the ldapadd
command to import this ldif file into our directory (note this will prompt for the password):
cat example.ldif | ldapadd -x -H ldap://localhost -D 'cn=admin,dc=my-company,dc=com' -W -v
The variables mean the following:
-x
uses simple authentication-H
specifies the LDAP server to connect to-D
specifies we want to bind as our admin account-W
prompts for the password for that account
Important You need to get the ip-address of your ldap server. Since we aren’t using docker-compose, the containers won’t magically see one another. You can inspect the container’s networking as follows:
docker inspect openldap | grep IPAddress
"SecondaryIPAddresses": null,
"IPAddress": "172.17.0.2",
"IPAddress": "172.17.0.2",
The IPAddress thus is 172.17.0.2
. Note that you will need this address in the next step for AUTH_LDAP_SERVER_URI
.
Configure FreeGenes
To configure FreeGenes to authenticate against our LDAP directory we need to set
the following options in fg/settings/secrets.py
:
import ldap
from django_auth_ldap.config import LDAPSearch, PosixGroupType
# The URI to our LDAP server (may be ldap_auth:// or ldaps://)
AUTH_LDAP_SERVER_URI = "ldap://172.17.0.2"
# Any user account that has valid auth credentials can login
AUTH_LDAP_USER_SEARCH = LDAPSearch("ou=users,dc=my-company,dc=com",
ldap.SCOPE_SUBTREE, "(uid=%(user)s)")
AUTH_LDAP_GROUP_SEARCH = LDAPSearch("ou=groups,dc=my-company,dc=com",
ldap.SCOPE_SUBTREE, "(objectClass=posixGroup)"
)
AUTH_LDAP_GROUP_TYPE = PosixGroupType()
# Populate the Django user model from the LDAP directory.
AUTH_LDAP_USER_ATTR_MAP = {
"first_name": "givenName",
"last_name": "sn",
"email": "mail"
}
# Map LDAP group membership into Django admin flags
AUTH_LDAP_USER_FLAGS_BY_GROUP = {
# Anyone in this group is a superuser for the app
"is_superuser": "cn=admin,ou=groups,dc=my-company,dc=com"
}
Also ensure ‘ldap_auth’ is listed in PLUGINS_ENABLED
inside fg/settings/config.py
.
Finally, you must build the Docker image with the build argument ENABLE_LDAP set to true:
docker build --build-arg ENABLE_LDAP=true -t quay.io/vsoch/freegenes .
It’s recommended to have the uwsgi logs open so any issue with ldap is shown clearly. You can do that with:
docker-compose logs -f uwsgi
For example, if you put in an incorrect credential, you would see the following in the logs:
uwsgi_1 | [pid: 56|app: 0|req: 4/4] 172.17.0.1 () {42 vars in 1025 bytes} [Thu Oct 26 07:18:10 2017] GET /ldap_auth/login/?next=http://127.0.0.1/login/ => generated 13475 bytes in 26 msecs (HTTP/1.1 200) 7 headers in 382 bytes (1 switches on core 0)
uwsgi_1 | search_s('ou=users,dc=my-company,dc=com', 2, '(uid=%(user)s)') returned 0 objects:
uwsgi_1 | Authentication failed for adminuser: failed to map the username to a DN.
Once you have set these options, start up FreeGenes and you should be able to see the ldap option on the login page:
and login with the username/password pairs testuser/testuser and testadmin/testadmin. As a final note, if you choose this method to deploy an actual ldap server, you might consider adding the container to the docker-compose. If you’ve done this and need help, or want to contribute what you’ve learned, please submit a Pull Request to update these docs.
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.