Author Archives: Hal Martin

About Hal Martin

In my free time I like experiment with hardware and embedded systems. Here I write about personal projects and random adventures into firmware land.

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!

Building the Arietta G25 Kernel

I’m using the Arietta G25 for a project of mine. Earlier I described how to build a bootloader for the 256MB version of the board.

Today I’m going to describe how to build the kernel for the Arietta G25. I needed ADC support as well as modules for some USB to ethernet adaptors that weren’t included in the kernel image ACME Systems provides.

First off you will need to install the toolchain to build armel binaries. ACME Systems has a page describing how to install the ARM9 toolchain. Here’s the short summary:

$ sudo apt-get install emdebian-archive-keyring libc6-armel-cross libc6-dev-armel-cross binutils-arm-linux-gnueabi gcc-arm-linux-gnueabi g++-arm-linux-gnueabi u-boot-tools libncurses5-dev

Once you have the environment setup it’s time to checkout the kernel and build. This is the Jenkins script I use to build the Arietta G25 kernel:

if [ ! -d "linux-3.14.7" ]; then
wget https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.14.7.tar.xz -O linux-3.14.7.tar.xz
tar xvfJ linux-3.14.7.tar.xz
cd linux-3.14.7
wget http://www.acmesystems.it/www/compile_linux_3_14/acme.patch -O acme.patch
patch -p1 < acme.patch

wget https://watchmysys.com/blog/wp-content/uploads/2014/08/linux-at91.config -O .config
wget https://watchmysys.com/blog/wp-content/uploads/2014/08/arietta_256m_ikconfig.patch -O arietta_256m_ikconfig.patch
patch -p1 < arietta_256m_ikconfig.patch
else
cd linux-3.14.7
fi
make ARCH=arm clean
CPUS=$(cat /proc/cpuinfo | grep -c "processor")

wget https://watchmysys.com/blog/wp-content/uploads/2014/08/acme-arietta-adc.dtb -O arch/arm/boot/dts/acme-arietta.dtb
make -j$CPUS ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- zImage
make modules -j$CPUS ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
make modules_install INSTALL_MOD_PATH=./modules ARCH=arm
mkdir -p modules/boot
cp arch/arm/boot/zImage modules/boot/
rm linux-3.14.7-arietta.tar.bz2
tar -C modules -cjvf linux-3.14.7-arietta.tar.bz2 lib/ boot/

The above commands are available in a bash/Jenkins script at the bottom of this post.

Installation is fairly simple, you need to mount the sdcard /boot and / partitions on your computer and then run:

tar -C /path/to/sdcard -jxvf linux-3.14.7-arietta.tar.bz2

My kernel configuration includes the Atmel ADC driver as a module (at91_adc) so you can unload/reload it (possibly to save power). It also includes the dm9601 and sr9700 modules for the Davicom DM96xx USB 2.0 10/100M Ethernet Adaptor, which is an inexpensive USB to ethernet adaptor available online (such as this one).

If you want to generate your own dtb (for instance enable PWM instead of the ADC) you can do that at this ACME Systems page.

Build script: linux-at91.sh
Kernel and modules: linux-3.14.7-arietta.tar.bz2
Kernel Config: linux-at91-3.14.7.config