SSO: An easier way to join your CentOS 5 Openfire server to an AD Domain

The official documentation for SSO in Openfire is at SSO Configuration. Please read that document first to understand the concepts used.

Many of the SSO Documents already out there describe using ktpass.exe and related tools in order to generate the keytab for a service. This can be unwieldy, and does not scale – if you want to install other kerberized services (such as Apache/HTTP), remapping a user to a different princ with ktpass.exe will increase the kvno, and render your previous service useless.

The answer is to integrate your server more tightly into the Active Directory, and advancements in Samba make this possible (without making you 100% dependent on Samba!).

The process is relatively straightforward with a modern OS. For this example, I am using CentOS 5. My machine, openfire.example.com, is in my network DMZ, communicating with kdc.example.local, which is behind my firewall. My domain name is EXAMPLE.LOCAL.

Required software packages (that are not installed by default):

cyrus-sasl-gssapi

samba

samba-common

openfire

The most important part of any native Kerberos communication is correct DNS – you need to ensure that forward and reverse DNS for your Openfire server is correct, and that the hosts file and hostname on the machine agree with it. DNS configuration is an exercise left to the reader, but /etc/hosts on openfire.example.com reads:

127.0.0.1               localhost localhost.localdomain
172.17.5.5              openfire openfire.example.com

In my environment, we elect to use DNS to locate our domain controllers, so the krb5.conf is straightforward and short:

[logging] default = FILE:/var/log/krb5libs.log kdc = FILE:/var/log/krb5kdc.log admin_server = FILE:/var/log/kadmind.log [libdefaults] default_realm = EXAMPLE.LOCAL dns_lookup_realm = true dns_lookup_kdc = true [domain_realm] .example.com = EXAMPLE.LOCAL [appdefaults] pam = {
   debug = false
   ticket_lifetime = 36000
   renew_lifetime = 36000
   forwardable = true
   krb4_convert = false
   validate = true }

At this point, you should be able to use kinit to get credentials for a user from your AD domain. If you can’t, stop and think about what you did wrong.

Keytab population

In order to properly populate the server keytab with principals, we are going to utilize Samba. If you don’t plan on using Samba for anything else, this is easy. Delete everything from /etc/samba/smb.conf and replace with the following:

workgroup = EXAMPLE
security = ads
realm = example.local
use kerberos keytab = true
password server = kdc.example.local kdc2.example.local

Unfortunately, Samba doesn’t support DNS lookups of KDCs (that I have found), so two of my KDCs are hard-coded here.

Now, we can utilize the “net” command to join the server to the domain, and populate the keytab, using a domain administrator login. You can replace “Administrator” with any user that is a domain or enterprise admin.

net -UAdministrator ads join

It will prompt you for the password, churn butter for a bit, and report success! If it does not, the most common issues are DNS related – go back and re-check your forward and reverse DNS, and ensure that there are no conflicting entries in /etc/hosts.

Doing this, however, only got us our host principals. We need to request a new principal for the XMPP service from the domain controller:

net -UAdministrator ads keytab add xmpp

Now, you can use klist to confirm that you were able to join the domain and receive the principals:

[root@openfire ~]# klist -k /etc/krb5.keytab
Keytab name: FILE:/etc/krb5.keytab
KVNO Principal
---- --------------------------------------------------------------------------
   2 host/openfire.example.com@EXAMPLE.LOCAL
   2 host/openfire.example.com@EXAMPLE.LOCAL
   2 host/openfire.example.com@EXAMPLE.LOCAL
   2 host/openfire@EXAMPLE.LOCAL
   2 host/openfire@EXAMPLE.LOCAL
   2 host/openfire@EXAMPLE.LOCAL
   2 openfire$@EXAMPLE.LOCAL
   2 openfire$@EXAMPLE.LOCAL
   2 openfire$@EXAMPLE.LOCAL
   2 xmpp/openfire.example.com@EXAMPLE.LOCAL
   2 xmpp/openfire.example.com@EXAMPLE.LOCAL
   2 xmpp/openfire.example.com@EXAMPLE.LOCAL
   2 xmpp/openfire@EXAMPLE.LOCAL
   2 xmpp/openfire@EXAMPLE.LOCAL
   2 xmpp/openfire@EXAMPLE.LOCAL

While this is all fine and dandy, /etc/krb5.keytab is only readable by root (as it should be!) – we will need to extract just the xmpp princ to another keytab that is readable by the daemon user. To do this, use ktutil to rkt /etc/krb5.keytab, delent everything but the xmpp principals, and wkt /opt/openfire/krb5.xmpp.keytab. Then, change ownership of the keytab to match whatever user you run Openfire as. In the end, it should look like this:

[root@openfire-test ~]# klist -k /opt/openfire/krb5.xmpp.keytab
Keytab name: FILE:/opt/openfire/krb5.xmpp.keytab
KVNO Principal
---- --------------------------------------------------------------------------
   2 xmpp/openfire.example.com@EXAMPLE.LOCAL
   2 xmpp/openfire.example.com@EXAMPLE.LOCAL
   2 xmpp/openfire.example.com@EXAMPLE.LOCAL
   2 xmpp/openfire@EXAMPLE.LOCAL
   2 xmpp/openfire@EXAMPLE.LOCAL
   2 xmpp/openfire@EXAMPLE.LOCAL

Configure Openfire’s GSSAPI

We deviate slightly from documents presented by others here, as there is one small change we need to make. Since the xmpp service principal is only a service principal, and not mapped to an actual user account, we need to ensure that Java never attempts to treat it like a user account. In order to assure that, we have to add an additional line to gss.conf – isInitiator.

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

The rest of the configuration is the same as has already been laid out in other documents, such as SSO Configuration

It might be worth mentioning that while the hostname should match DNS, it doesn’t have to match the jabber domain name.

setting ‘password server = *’ should work for getting samba to find the DC.

The document title is misleading: it’s not exclusive to CentOS 5.

1 Like