I’m a dabbler, I like to dabble.

While most people are happily using Google Talk, Facebook chat, Skype and the like I’m busy playing around with my own chat server, writing plugins for it and seeing if I can get things like Single Sign On (SSO), DNS Service Records and Federation working. It’s time consuming, frustrating at times but ultimately rewarding. One particularly frustrating problem I recently tackled was single sign on with Openfire (a Jabber/XMPP messaging server).

My basic setup likely mirrors most enterprise-y networks:

  • Windows Active Directory Domain Controller with Windows Support Tools installed
  • Openfire 3.8 bound to the Windows DC
  • Windows XP/Windows Terminal Server Clients running Pandion/Pidgin
  • Mac OS X Clients Running Adium

The first step is to ensure that you have a working Windows AD network alongside a working Openfire installation.

  • AD Domain: EXAMPLE.COM
  • AD Host: DC.EXAMPLE.COM
  • Openfire (XMPP) Domain: EXAMPLE.COM
  • Openfire Host: OPENFIRE.EXAMPLE.COM
  • Keytab account: xmpp-openfire

Ensure you have an A and reverse DNS record for your Openfire server and then setup your DNS Service Records for Openfire like so:

_xmpp-client._tcp.example.com. 86400 IN SRV 0 0 5222 openfire.example.com.
_xmpp-server._tcp.example.com. 86400 IN SRV 0 0 5269 openfire.example.com.

With DNS done create two new Active Directory accounts. Account one is for binding the Openfire server to the domain (skip this account if you’ve already bound Openfire to your domain).

Account two is to associate your Service Principal Name (SPN) so Kerberos clients can find and authenticate using SSO with your Openfire server.

On account two check under Account properties that User cannot change passwordPassword never expires and Do not require Kerberos preauthentication are checked.

On the Windows Domain Controller you’ll now need to create the SPN and keytab. The SPN (Service Principal Name) is used by clients to lookup the name of the Openfire server for SSO. The keytab contains pairs of Service Principals and encrypted keys which allows a service to automatically authenticate against the Domain Controller without being prompted for a password.

Creating the SPN

I created two records since it seems some clients lookup xmpp/openfire.example.com@EXAMPLE.COM and some look up xmpp/openfire.example.com.

setspn -A xmpp/openfire.example.com@EXAMPLE.COM xmpp-openfire
setspn -A xmpp/openfire.example.com xmpp-openfire

Mapping the SPN to the keytab account xmpp-openfire and when prompted enter the xmpp-openfire password:

ktpass -princ xmpp/openfire.example.com@EXAMPLE.COM -mapuser xmpp-openfire@EXAMPLE.COM -pass * -ptype KRB5_NT_PRINCIPAL

Create the keytab

I found that the Java keytab didn’t work on my Openfire system in which case I used the Windows ktpass utility to create it. Some users report the converse, so see whichever works for you:

Java keytab generation:

ktab -k xmpp.keytab -a xmpp/openfire.example.com@EXAMPLE.COM

Windows keytab generation:

ktpass -princ xmpp/openfire.example.com@EXAMPLE.COM -mapuser xmpp-openfire@EXAMPLE.COM -pass * -ptype KRB5_NT_PRINCIPAL -out xmpp.keytab

Copy the keytab to your Openfire directory, typically /usr/share/openfire or /opt/openfire. The full path will look like this: /usr/share/openfire/xmpp.keytab

Configuring Linux for Active Directory

Configure Kerberos

First we need to install ntp, kerberos and samba:

apt-get install ntp krb5-config krb5-user krb5-doc winbind samba

Enter your workgroup name:

eg. EXAMPLE.COM

Configure /etc/krb5.conf

[logging]
default = FILE:/var/log/krb5libs.log
kdc = FILE:/var/log/krb5kdc.log
admin_server = FILE:/var/log/kadmind.log

[libdefaults]
dns_lookup_realm = true
dns_lookup_kdc = true
ticket_lifetime = 24h
forwardable = yes

[appdefaults]
pam = {
  debug = false
  ticket_lifetime = 36000
  renew_lifetime = 36000
  forwardable = true
  krb4_convert = false
}

Test connection to Active Directory by entering the following commands:

kinit xmpp-openfire@EXAMPLE.COM

Check if the request for the Active Directory ticket was successful using the kinit command

klist

The result of this command should be something like this:

Ticket cache: FILE:/tmp/krb5cc_0
Default principal: xmpp-openfire@EXAMPLE.COM

Valid starting Expires Service principal
07/11/13 21:41:31 07/12/13 07:41:31 krbtgt/EXAMPLE.COM@EXAMPLE.COM
renew until 07/12/14 21:41:31

Join the domain

Configure your smb.conf like so:

#GLOBAL PARAMETERS
[global]
   workgroup = EXAMPLE
   realm = EXAMPLE.COM
   preferred master = no
   server string = Linux Test Machine
   security = ADS
   encrypt passwords = yes
   log level = 3
   log file = /var/log/samba/%m
   max log size = 50
   printcap name = cups
   printing = cups
   winbind enum users = Yes
   winbind enum groups = Yes
   winbind use default domain = Yes
   winbind nested groups = Yes
   winbind separator = +
   idmap uid = 600-20000
   idmap gid = 600-20000
   ;template primary group = "Domain Users"
   template shell = /bin/bash

[homes]
   comment = Home Direcotries
   valid users = %S
   read only = No
   browseable = No

[printers]
   comment = All Printers
   path = /var/spool/cups
   browseable = no
   printable = yes
   guest ok = yes

Join the domain:

net ads join -U administrator

You will be asked to enter the AD Administrator password.

Verify you can list the user’s and groups on the domain:

wbinfo -u
wbinfo -g

Testing the keytab works:

From your Openfire system run the below command:

kinit -k -t /usr/share/openfire/resources/xmpp.keytab xmpp/openfire.example.com@EXAMPLE.COM -V

You should see:

Authenticated to Kerberos v5

Then create a GSSAPI configuration file called gss.conf in your Openfire configuration folder normally in /etc/openfire or /opt/openfire/conf. Ensure you set the path to your xmpp.keytab file:

com.sun.security.jgss.accept {
    com.sun.security.auth.module.Krb5LoginModule
    required
    storeKey=true
    keyTab="/usr/share/openfire/xmpp.keytab"
    doNotPrompt=true
    useKeyTab=true
    realm="EXAMPLE.COM"
    principal="xmpp/openfire.example.com@EXAMPLE.COM"
    debug=true
    isInitiator=false;
};

Ensure the file is owned by the openfire user.

Stop Openfire and enable GSSAPI by editing your openfire.xml configuration file which is found in the openfire conf directory:

<!-- sasl configuration -->
<sasl>
    <mechs>GSSAPI</mechs>
    <!-- Set this to your Keberos realm name which is usually your AD domain name in all caps. -->
    <realm>EXAMPLE.COM</realm>
    <gssapi>
        <!-- You can set this to false once you have everything working. -->
        <debug>true</debug>
        <!-- Set this to the location of your gss.conf file created earlier -->
        <!-- "/" is used in the path here not "\" even though this is on Windows. -->
        <config>/etc/openfire/gss.conf</config>
        <useSubjectCredsOnly>false</useSubjectCredsOnly>
    </gssapi>
</sasl>

Or add to System Properties:

sasl.gssapi.config /etc/openfire/gss.conf
sasl.gssapi.debug false
sasl.gssapi.useSubjectCredsOnly false
sasl.mechs GSSAPI
sasl.realm EXAMPLE.COM

Restart Openfire and you’re done.