3

All of the tutorials I could find on setting up single-sign on into an Apache-hosted website using Active Directory authentication do so by configuring Kerberos with insecure settings. It's been best practice for awhile now to disable RC4-HMAC encryption for Kerberos in Active Directory, but a lot of tutorials call for overriding krb5.conf's defaults and making everything work with RC4-HMAC.

I wanted to try setting up single sign-on with AES256 encryption, and I managed to make it work, so I'm recording this question and answer for anyone else who wants better security for their site.

Starting with RC4-HMAC

We'll start by getting it working with RC4-HMAC first, since this is easier. The standard steps to set up SSO start with creating a domain account with an associated SPN, which browsers will use to get encrypted credentials to send to the server. For this example, my user is REALM\HostServiceAccount:

  • UPN: HostServiceAccount@realm.com (and therefore the Kerberos principal name is HostServiceAccount@REALM.COM)
  • servicePrincipalName attribute (also settable by setspn): HTTP/host.example.com; HTTP/host (Kerberos: HTTP/host.example.com@REALM.COM)
  • Leave encryption settings alone; by default, this makes RC4-HMAC the strongest encryption allowed, so tickets from the domain will have this encryption

We add the following entries to /etc/krb5.conf on our target server:

[libdefaults]
    default_realm = REALM.COM

[domain_realm]
    .realm.com = REALM.COM
    realm.com = REALM.COM

We create the keytab and let the server read it:

# ktutil
ktutil:  add_entry -password -p HTTP/host.example.com@REALM.COM -k 1 -e rc4-hmac
Password for HTTP/host.example.com@REALM.COM: <enter password here>
ktutil:  write_kt /etc/apache2/service.keytab
ktutil:  q
# chown -v www-data:root /etc/apache2/service.keytab
# chmod -v 440 /etc/apache2/service.keytab

(At this point, of course, you would want to use kinit -kt service.keytab -S HTTP/host.example.com@REALM.COM HostServiceAccount@REALM.COM to test the keytab.)

Finally, we set Apache to authenticate users using our keytab:

KrbDelegateBasic off
KrbAuthoritative on
KrbMethodK5Passwd off
Krb5Keytab /etc/apache2/service.keytab
KrbAuthRealms REALM.COM

LogLevel debug

After we restart Apache, if all goes well, we do a request to the server from our Windows domain machine and see the following in Apache's error log:

[debug] src/mod_auth_kerb.c(1641): [client ****] kerb_authenticate_user entered with user (NULL) and auth_type Kerberos, referer: ****
[debug] src/mod_auth_kerb.c(1395): [client ****] Verifying client data using KRB5 GSS-API , referer: ****
[debug] src/mod_auth_kerb.c(1411): [client ****] Client didn't delegate us their credential, referer: ****
[debug] src/mod_auth_kerb.c(1430): [client ****] GSS-API token of length 185 bytes will be sent back, ****

Running klist on our client machine (or Wireshark to see the ticket in the request), we see that indeed we have used an RC4-HMAC ticket to authenticate:

#4>     Client: fluggo @ REALM.COM
        Server: HTTP/host.example.com @ REALM.COM
        KerbTicket Encryption Type: RSADSI RC4-HMAC(NT)

Moving up to better encryption

All is well, but again, this is not our goal. RC4-HMAC is considered insecure, so let's disable that and try to make the same setup work with AES256.

First, we'll ask our friendly neighborhood domain admin to enable the advanced encryption on REALM\HostServiceAccount, which will be two checkboxes labeled:

  • This account supports Kerberos AES 128 bit encryption
  • This account supports Kerberos AES 256 bit encryption

These appear in various places depending on the tools you're using; the end result should be that the LDAP attribute msDS-SupportedEncryptionTypes should be 0x18 or decimal 24, which represents that only AES128 and AES256 are supported.

To make this effective, we'll kill our local client tickets:

C:>klist purge

Current LogonId is 0:0xdeadbeef
        Deleting all tickets:
        Ticket(s) purged!

If we perform our request again, we'll see that the request has failed, but we've got an updated ticket:

C:>klist
      ...
#3>     Client: fluggo @ REALM.COM
        Server: HTTP/host.example.com @ REALM.COM
        KerbTicket Encryption Type: AES-256-CTS-HMAC-SHA1-96

Now we should just need to update our keytab with the new algorithms, and we should be golden:

# mv /etc/apache2/service.keytab ~/old.keytab
# ktutil
ktutil:  add_entry -password -p HTTP/host.example.com@REALM.COM -k 1 -e aes256-cts-hmac-sha1-96
Password for HTTP/host.example.com@REALM.COM: <enter password here>
ktutil:  add_entry -password -p HTTP/host.example.com@REALM.COM -k 1 -e aes128-cts-hmac-sha1-96
Password for HTTP/host.example.com@REALM.COM: <enter password here>
ktutil:  write_kt /etc/apache2/service.keytab
ktutil:  q
# chown -v www-data:root /etc/apache2/service.keytab
# chmod -v 440 /etc/apache2/service.keytab

We don't even need to restart Apache. Just resubmit the request.

Oops... it doesn't work. If we look at Apache's error log, we see:

[debug] src/mod_auth_kerb.c(1641): [client ****] kerb_authenticate_user entered with user (NULL) and auth_type Kerberos
[debug] src/mod_auth_kerb.c(1249): [client ****] Acquiring creds for HTTP@host.example.com
[debug] src/mod_auth_kerb.c(1395): [client ****] Verifying client data using KRB5 GSS-API
[debug] src/mod_auth_kerb.c(1411): [client ****] Client didn't delegate us their credential
[debug] src/mod_auth_kerb.c(1430): [client ****] GSS-API token of length 9 bytes will be sent back
[debug] src/mod_auth_kerb.c(1110): [client ****] GSS-API major_status:000d0000, minor_status:000186a5
[error] [client ****] gss_accept_sec_context() failed: Unspecified GSS failure.  Minor code may provide more information (, )

Well that's a ridculously unhelpful error message, but what went wrong? The answer, and one solution, to follow!

1

It turns out that the major problem here is one of the problems AES256 was intended to solve.

TL;DR: The principal name in the keytab now needs to match the account name.

The Problem

If we had run KRB5_TRACE=/dev/stderr kinit HostServiceAccount@REALM.COM back when the account was enabled for only RC4-HMAC encryption, we would have seen this line in the output:

[8192] 1441829478.537451: Selected etype info: etype rc4-hmac, salt "", params ""

Now that we have AES256 enabled, though, that line looks like this:

[8200] 1441829508.947208: Selected etype info: etype aes256-cts, salt "REALM.COMHostServiceAccount", params ""

When the switch was made from NTLM authentication to Kerberos, the RC4-HMAC algorithm was specified to reuse the NTLM hash. Microsoft declined to put a salt into the hash to make it easier for existing systems to interoperate with Kerberos. Now that users have the opportunity to upgrade, a salt is specified for the AES256 and AES128 algorithms, and the salt is the user name.

We can see this if we generate keytabs for RC4-HMAC and AES256 using different usernames. With RC4-HMAC:

fluggo@host:~$ ktutil
ktutil:  add_entry -password -p HTTP/host.example.com@REALM.COM -k 1 -e rc4-hmac
Password for HTTP/host.example.com@REALM.COM: 12345
ktutil:  add_entry -password -p HostServiceAccount@REALM.COM -k 1 -e rc4-hmac
Password for HostServiceAccount@REALM.COM: 12345
ktutil:  write_kt rc4.keytab
ktutil:  q
fluggo@host:~$ klist -Kek rc4.keytab
Keytab name: FILE:rc4.keytab
KVNO Principal
---- --------------------------------------------------------------------------
   1 HTTP/host.example.com@REALM.COM (arcfour-hmac)  (0x7a21990fcd3d759941e45c490f143d5f)
   1 HostServiceAccount@REALM.COM (arcfour-hmac)  (0x7a21990fcd3d759941e45c490f143d5f)

...the hash is the same, so these two entries are equivalent. But with AES256:

fluggo@host:~$ ktutil
ktutil:  add_entry -password -p HTTP/host.example.com@REALM.COM -k 1 -e aes256-cts-hmac-sha1-96
Password for HTTP/host.example.com@REALM.COM: 12345
ktutil:  add_entry -password -p HostServiceAccount@REALM.COM -k 1 -e aes256-cts-hmac-sha1-96
Password for HostServiceAccount@REALM.COM: 12345
ktutil:  write_kt aes.keytab
ktutil:  q
fluggo@host:~$ klist -Kek aes.keytab
Keytab name: FILE:aes.keytab
KVNO Principal
---- --------------------------------------------------------------------------
   1 HTTP/host.example.com@REALM.COM (aes256-cts-hmac-sha1-96)  (0x5746fa6f9b0c990ba7fb20acd85065040d66e843a043508569841768ef2b7917)
   1 HostServiceAccount@REALM.COM (aes256-cts-hmac-sha1-96)  (0x6a98fdccbce4db77f40192f4e916e0900a1b9cba2f6ca8bc737d968e4b961c25)

...the hashes are different. The principal name matters, and it needs to match the UPN of the account.

A Solution

Since the username must be correct for the keytab to work, we generate a new keytab with the principal name Active Directory uses on the ticket:

# rm /etc/apache2/service.keytab
# ktutil
ktutil:  add_entry -password -p HostServiceAccount@REALM.COM -k 1 -e aes256-cts-hmac-sha1-96
Password for HostServiceAccount@REALM.COM: <enter password here>
ktutil:  add_entry -password -p HostServiceAccount@REALM.COM -k 1 -e aes128-cts-hmac-sha1-96
Password for HostServiceAccount@REALM.COM: <enter password here>
ktutil:  write_kt /etc/apache2/service.keytab
ktutil:  q
# chown -v www-data:root /etc/apache2/service.keytab
# chmod -v 440 /etc/apache2/service.keytab

Apache cares about the principal name in the keytab, and so it won't find these entries on its own. Instead, we just direct Apache to use whatever working principal it can find:

KrbServiceName Any

I wanted Apache to find the principal by the correct name, but it hardly matters since our principals are the only ones in the keytab.

Restart Apache, refresh the page, and the authentication should work now.

-1

Solution for key tabs with multiple entries (SPNs) as described here: https://developer.ibm.com/answers/questions/270616/how-do-you-add-multiple-spns-to-the-same-keytab-fi.html

Add the /RawSalt switch to ktpass: ktpass.... -setupn /RawSalt "REALM.COMSameAccountAsMapuser"

  • As this question already has an accepted answer and is over 2 years old, I'm not sure how this is helpful? – DarkMoon Jan 31 '18 at 6:27
  • It is a different solution, and allows people to use multiple SPNs in a keytab, or create a keytab when the SPN and UPN differ. There is not much documentation on how to do this available so I posted.... – Thorston Feb 1 '18 at 0:56
  • To be specific it allows for SSO setup for a cluster or web farm behind an LB... – Thorston Feb 1 '18 at 1:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.