Monthly Archives: September 2014

Authenticating Mediawiki users with FreeIPA LDAP

Following my success in configuring Jenkins to authenticate against FreeIPA LDAP I thought I would also integrate LDAP into Mediawiki.

The first step is to install the LdapAuthentication extension for Mediawiki. Installation is pretty simple, extract the LdapAuthentication archive to the extensions folder in your mediawiki installation. e.g. /var/www/mediawiki/extensions

On the FreeIPA server we need to create an unprivileged user to bind to LDAP:

-bash-4.2$ cat mediawiki.ldif 
dn: uid=mediawiki,cn=sysaccounts,cn=etc,dc=watchmysys,dc=com
changetype: add
objectclass: account
objectclass: simplesecurityobject
uid: mediawiki
userPassword: EpQIJjhRj
passwordExpirationTime: 20380119031407Z
nsIdleTimeout: 0

To create the mediawiki user you need to apply the LDIF to LDAP:

ldapmodify -h ipa.watchmysys.com -p 389 -x -D "cn=Directory Manager" -W -f mediawiki.ldif

Create a new role inside FreeIPA for Mediawiki users:
IPA Mediawiki Role

Here is what the corresponding LDAP object is:

# mediawiki, roles, accounts, watchmysys.com
dn: cn=mediawiki,cn=roles,cn=accounts,dc=watchmysys,dc=com
objectClass: groupofnames
objectClass: nestedgroup
objectClass: top
cn: mediawiki
description: mediawiki administrators
member: uid=hmartin,cn=users,cn=accounts,dc=watchmysys,dc=com

Inside the LdapAuthentication extension we need to configure the parameters used to query LDAP for users:

$wgLDAPDomainNames = array('watchmysys.com');
$wgLDAPServerNames = array('watchmysys.com' => 'ipa.watchmysys.com');
$wgLDAPUseLocal = false;
$wgLDAPEncryptionType = array('watchmysys.com' => 'tls');
$wgLDAPOptions = array();
$wgLDAPPort = array();
$wgLDAPSearchStrings = array('watchmysys.com' => 'uid=USER-NAME,cn=users,cn=accounts,dc=watchmysys,dc=com');
$wgLDAPProxyAgent = array('watchmysys.com' => 'uid=mediawiki,cn=sysaccounts,cn=etc,dc=watchmysys,dc=com');
$wgLDAPProxyAgentPassword = array('watchmysys.com' => 'EpQIJjhRj');
$wgLDAPSearchAttributes = array();
$wgLDAPBaseDNs = array('watchmysys.com' => 'dc=watchmysys,dc=com');
$wgLDAPGroupBaseDNs = array('watchmysys.com' => 'cn=roles,cn=accounts,dc=watchmysys,dc=com');
$wgLDAPUserBaseDNs = array('watchmysys.com' => 'cn=users,cn=accounts,dc=watchmysys,dc=com');
$wgLDAPWriterDN = array();
$wgLDAPWriterPassword = array();
$wgLDAPWriteLocation = array();
$wgLDAPAddLDAPUsers = array();
$wgLDAPUpdateLDAP = array();
$wgLDAPPasswordHash = array();
$wgLDAPMailPassword = array();
$wgLDAPPreferences = array('email'=>'mail','realname'=>'displayname','nickname'=>'cn');
$wgLDAPDisableAutoCreate = array();
$wgLDAPDebug = 0;
$wgLDAPGroupUseFullDN = array('watchmysys.com' => true);
$wgLDAPLowerCaseUsername = array('watchmysys.com' => true);
$wgLDAPGroupUseRetrievedUsername = array();
$wgLDAPGroupObjectclass = array('watchmysys.com' => 'groupofnames');
$wgLDAPGroupAttribute = array('watchmysys.com' => 'member');
$wgLDAPGroupNameAttribute = array('watchmysys.com' => 'cn');
$wgLDAPGroupsUseMemberOf = array('watchmysys.com' => false);
$wgLDAPUseLDAPGroups = array();
$wgLDAPLocallyManagedGroups = array();
$wgLDAPGroupsPrevail = array();
$wgLDAPRequiredGroups = array('watchmysys.com' => array('cn=mediawiki,cn=roles,cn=accounts,dc=watchmysys,dc=com'));
$wgLDAPExcludedGroups = array();
$wgLDAPGroupSearchNestedGroups = array('watchmysys.com' => false);
$wgLDAPAuthAttribute = array();
$wgLDAPAutoAuthUsername = "";
$wgLDAPAutoAuthDomain = "";
$wgPasswordResetRoutes['domain'] = true;
$wgLDAPActiveDirectory = array();

The above configuration uses direct binds to authenticate users, and anonymous binds to verify that the user is part of the specified wgLDAPRequiredGroups. There are instructions for how to authenticate a user against Active Directory using only anonymous binds but I wasn’t able to get this working against the FreeIPA LDAP implementation.

Additionally, I found that you need to set $wgLDAPGroupUseFullDN = array('watchmysys.com' => true); for group memberships to be found. Otherwise the LDAP Authentication extension will fail to find the mediawiki group and verify that the username is a member, thus refusing to authenticate the user.

Now that you have the LDAP Authentication extension configured you need to enable it in LocalSettings.php:

require_once "$IP/extensions/LdapAuthentication/LdapAuthentication.php";
require_once 'includes/AuthPlugin.php';
$wgAuth = new LdapAuthenticationPlugin();

After many unsuccessful login attempts I found that the MediaWiki database needed to be updated before I could login using my LDAP credentials:

/var/www/mediawiki$ php maintenance/update.php
...
Creating ldap_domains table ...done.
...
Done 0 files in 0.0 seconds
Fixing protocol-relative entries in the externallinks table...
Done, 0 rows updated.
Populating fa_sha1 field from fa_storage_key

Done 0 files in 0.0 seconds
Purging caches...done.

Done.

If you still cannot login it is possible to enable additional logging from the LdapAuthentication extension. To enable verbose logging edit LdapAuthnetication.php, change $wgLDAPDebug = 3; and add:

$wgDebugLogGroups["ldap"] = "/tmp/ldapdebug.log";

The log file defined in wgDebugLogGroups will contain information from the LdapAuthentication extension and will help you diagnose why authentication is failing.

Note: If a user’s Mediawiki username is the same as their LDAP username Mediawiki will remove their password from the database.

Before LDAP:

MariaDB [mediawiki]> select user_id,user_name,user_real_name,user_password from user where user_name='Hmartin';
+---------+-----------+----------------+----------------------------------------------+
| user_id | user_name | user_real_name | user_password                                |
+---------+-----------+----------------+----------------------------------------------+
|       2 | Hmartin   | Hal Martin     | :B:a24f940c:89ebc6b51ad2529ef6fd503b9ab1c8db |

After logging in as my LDAP user:

MariaDB [mediawiki]> select user_id,user_name,user_real_name,user_password from user where user_name='Hmartin';
+---------+-----------+----------------+----------------------------------------------+
| user_id | user_name | user_real_name | user_password                                |
+---------+-----------+----------------+----------------------------------------------+
|       2 | Hmartin   | Hal Martin     |                                              |

I’m guessing this means that if the LDAP server is down authentication will not fall back to the user password stored in the database (since it’s now absent).

That’s it. You should have LDAP authentication working for Mediawiki. If you run into any problems I suggest enabling wgLDAPDebug and wgDebugLogGroups and examining the logs to find out what went wrong. As always, tcpdump is your friend!

Jenkins authenticate with FreeIPA LDAP

I run a Jenkins server to build projects like the Arietta G25 Kernel and Banana Pi Kernel.

I also run a FreeIPA server for central authentication and user rights management. I’m not an expert on LDAP and Kerberos, which is why I like FreeIPA because it allows me to manage these without requiring that I be an LDAP or Kerberos demigod.

So, here’s how to configure Jenkins to authenticate against FreeIPA. You will need to install the Jenkins LDAP plugin before proceeding.

Manage Jenkins

The plugin can be installed by clicking on: Manage Jenkins -> Manage Plugins -> Available -> Search “LDAP Plugin”

On the FreeIPA server create an LDIF file to define an unprivileged user to read the LDAP tree. The FreeIPA LDAP server does not appear to support anonymous binds. I recommend the makepasswd program to generate the user password.

-bash-4.2$ cat jenkins.ldif 
dn: uid=jenkins,cn=sysaccounts,cn=etc,dc=watchmysys,dc=com
changetype: add
objectclass: account
objectclass: simplesecurityobject
uid: jenkins
userPassword: 7b1yYzNINU
passwordExpirationTime: 20380119031407Z
nsIdleTimeout: 0

To create the user you need to apply this LDIF to LDAP:

ldapmodify -h ipa.watchmysys.com -p 389 -x -D "cn=Directory Manager" -W -f jenkins.ldif

When the LDAP plugin is installed go back to the “Manage Jenkins” menu, click on “Configure Global Security” and “Enable security”

Next select “Security Realm” -> “LDAP”, and configure the settings for your IPA server as described below:

Jenkins LDAP Security

Server: ldaps://ipa.watchmysys.com
root DN: dc=watchmysys,dc=com
User search base: cn=users,cn=accounts
User search filter: (objectClass=inetOrgPerson)(objectClass=posixAccount)(uid=%u)
Group search filter: jenkins
Manager DN: uid=jenkins,cn=sysaccounts,cn=etc,dc=watchmysys,dc=com
Manager password: 7b1yYzNINU
Display Name LDAP attribute: displayname
Email Address LDAP attribute: mail

You may want to choose ldap:// instead of ldaps:// during your testing. I found it useful to run tcpdump between my Jenkins server and IPA server to diagnose authentication failures.

In FreeIPA create a role for Jenkins users:
FreeIPA Jenkins Role

Here is what the corresponding LDAP object is:

# jenkins, roles, accounts, watchmysys.com
dn: cn=jenkins,cn=roles,cn=accounts,dc=watchmysys,dc=com
objectClass: groupofnames
objectClass: nestedgroup
objectClass: top
cn: jenkins
description: Jenkins administrators
member: uid=hmartin,cn=users,cn=accounts,dc=watchmysys,dc=com

I decided that all Jenkins users should be allowed to administer once logged in. You may decide to implement a more complex security system with different privilege levels.

Jenkins Authentication Strategy

Save the changes. You will be unable to administer Jenkins without logging in now. Jenkins will update config.xml in its home with the new security settings:

  <useSecurity>true</useSecurity>
  <authorizationStrategy class="hudson.security.FullControlOnceLoggedInAuthorizationStrategy"/>
  <securityRealm class="hudson.security.LDAPSecurityRealm" plugin="[email protected]">
    <server>ldaps://ipa.watchmysys.com</server>
    <rootDN>dc=watchmysys,dc=com</rootDN>
    <inhibitInferRootDN>false</inhibitInferRootDN>
    <userSearchBase>cn=users,cn=accounts</userSearchBase>
    <userSearch>(objectClass=inetOrgPerson)(objectClass=posixAccount)(uid=%u)</userSearch>
    <groupSearchFilter>jenkins</groupSearchFilter>
    <groupMembershipStrategy class="jenkins.security.plugins.ldap.FromGroupSearchLDAPGroupMembershipStrategy">
      <filter></filter>
    </groupMembershipStrategy>
    <managerDN>uid=jenkins,cn=sysaccounts,cn=etc,dc=watchmysys,dc=com</managerDN>
    <managerPasswordSecret>bdV4rdfP9sQ1JTsDfJYGQSRvqYtsSafFdVLYwiuv6nTyiaAnfIwxeC2GNfQmy0dfs</managerPasswordSecret>
    <disableMailAddressResolver>false</disableMailAddressResolver>
    <displayNameAttributeName>displayname</displayNameAttributeName>
    <mailAddressAttributeName>mail</mailAddressAttributeName>
  </securityRealm>

If you cannot login to Jenkins using your LDAP username and password then remove the above lines from your config.xml and restart Jenkins. Jenkins will revert back to the default policy of anonymous users are admins.

If your user is not in the Jenkins role on the FreeIPA server you will not be able to login.
Jenkins LDAP Failed Login

You should now have LDAP-based authentication working in Jenkins. You now have all the benefits of central user management in Jenkins, enjoy!