TL;DR: Getting FreeIPA 4.7 fully functional is a pain in the arse (and not just on Debian/Ubuntu), I’d STRONGLY recommend you get a distro that has 4.8 packaged as that gets rid of a lot of the python 2 nastiness.

Because managing users on 20+ individual VMs manually was getting a bit beyond the pale I decided it was about time to delve into the wonderful world of Linux directory servers, enter FreeIPA.

Now a sane person would probably have built their FreeIPA directory servers on Fedora, CentOS or RHEL because, let’s face it, if Red Hat is the primary code maintainer it bloody well should work on their distribution. But me, having been scarred for life by dealing with RPM based distros before RPM had proper package management infrastructure around it (i.e. in the days before yum and dnf) any time I have to deal with CentOS or RHEL I have to go off and rock in the corner for a while before I can get started (have to exorcise the flashbacks to repeated rpm -ivh commands uncovering more and more, frequently circular dependencies in packages). Has a linux distro preference is probably best described as “anything that’s NOT Red Hat or otherwise RPM based”… EDITOR NOTE: turns out that it DOESN’T work on CentOS 7, but does work on Fedora 30, no idea about RHEL.

So because FreeIPA is a bit of a pig vis dependencies and such I figured another large, fairly well maintained distro would be the go. As such I ended up with Ubuntu 18.04.3 (pretty sure their freeipa-server package is unmodified from the parent in Debian so all the same probably applies there).

I’m not going to recount the whole process blow-by-blow here, that’s been done elsewhere I’m just going to call out the parts where I had some issues and also address how to successfully add the truckload of required DNS entries to Unbound running on opnSense.

Packaging Issues

So if you just install the freeipa-server package and run ‘ipactl start’ things will look like they work but they won’t;

Glyphs in the Web UI not showing up correctly

This manifests differently in different browsers so I won’t bother with a screen grab, but basically if the buttons and checkboxes don’t work you have this problem.

FreeIPA uses a third-party glyph set called FontAwesome (YO Red Hat, how’s about some GRACEFUL fallback for this scenario?), the Ubuntu/Debian packages install this to /usr/share/fonts/truetype/fontawesome, but FreeIPA wants it at /usr/share/fonts/truetype/font-awesome.

# cd /usr/share/fonts/truetype
# ln -s font-awesome /usr/share/fonts/truetype/fontawesome

Restart FreeIPA with ‘ipactl restart’ and the web UI will now work properly.

Web UI Login fails with “Login has failed for an unknown reason”

This is a simple permissions issue, basically apache attempts to access the /var/lib/krb5kdc/kdc.crt file to do it’s initial bind (check /var/log/apache2/error.log), but the permissions on /var/lib/krb5kdc are 0700 so it can’t traverse the directory even though kdc.crt is world readable…

# chmod a+x /var/lib/krb5kdc

Can’t execute password change at first login

When you create a user in FreeIPA they are created in “expired” state to force a password change at first login (the reasons for this are documented and make sense), this functionality leverages kadmin, but there’s another packaging issue that prevents kadmin from starting.

Couple of ways to tell this is happening, first off if you do an “ipactl status” kadmin will show as “STOPPED”, and if you try to change your password at login everything will seem OK until you get booted.

For some reason the Ubuntu/Debian package doesn’t create a default kadm5.acl file so we need to create one it’ll default to allow all if you want to be a bit more selective check ‘man kadm5.acl’;

# touch /etc/krb5kdc/kadm5.acl

Now restart FreeIPA with an ‘ipactl restart’ and kpassword should say “Started” this time.

Patch Dogtag

There’s a bug in the currently packaged version of dogtag, that causes failures in certificate management from the UI, primarily manifests when trying to unjoin hosts. Some weird ussue around deflate compression, apparently it’s fixed upstream but hasn’t made it down to the Debian/Ubuntu packages.

We need to patch /usr/lib/python2.7/dist-packages/ipaserver/plugins/dogtag.py, here’s a unified diff, but basically you need to remove the “Accept-Encoding” header.

@@ -1916,8 +1916,7 @@
             client_keyfile=None,
             cafile=self.ca_cert,
             method='POST',
-            headers={'Accept-Encoding': 'gzip, deflate',
-                     'User-Agent': 'IPA',
+            headers={'User-Agent': 'IPA',
                      'Content-Type': 'application/xml'},
             body=payload
         )

Roll pyasn1 Back to 0.3.7

Certificate renewals won’t work until this is done, FreeIPA is not compatible with the current version of pyasn1/pyasn1-modules, so we need to roll them back to appropriate versions, and hold the packages (installed as dependencies from freeipa-server) to prevent updates clobbering the change.

There are apparently patches coming from upstream for this but given the current versions installed as depedencies for the ipa-server package under CentOS are pyasn1=0.1.9 and pyasn1-modules=0.0.1 I’m not holding my breath (especially since current in CentOS is 4.6 and the Ubunut/Debian package is 4.7) EDITOR NOTE: Fedora 30 actually uses FreeIPA 4.8 which, miracle of miracles has largely moved all the python bits to python 3.

# pip install -Iv pyasn1==0.3.7
# pip install -Iv pyasn1-modules=0.1.5
# echo "python-pyasn1 hold" | dpkg --set-selections
# echo "python-pyasn1-modules hold" | dpkg --set-selections

At this point you should mostly have an operational master (I’d recommend doing the above BEFORE running ipa-server-install), there’s still issues creating a replica in my environment which I’ve yet to run down, if I do I’ll probably write that up separately (but I may just wait for Ubuntu 19.10 which has FreeIPA 4.8 packaged).

Configuring Unbound

FreeIPA usually wants to be the authoritative DNS for the domain with its internal BIND and there are some benefits there, but my environments already have a DNS in place which also has automatic registrations from DHCP going into it so don’t really want to disturb that, as a consequence we need to create a bunch of records manually.

First we dump the required records (the kinit admin bit is required to authenticate against the ipa server to get the records out).

# kinit admin
Password for admin@YOUR.DOMAIN: <enter your admin password>
# ipa dns-update-system-records --dry-run
  IPA DNS records:
    _kerberos-master._tcp.your.domain. 86400 IN SRV 0 100 88 ipa.your.domain.
    _kerberos-master._udp.your.domain. 86400 IN SRV 0 100 88 ipa.your.domain.
    _kerberos._tcp.your.domain. 86400 IN SRV 0 100 88 ipa.your.domain.
    _kerberos._udp.your.domain. 86400 IN SRV 0 100 88 ipa.your.domain.
    _kerberos.your.domain. 86400 IN TXT "YOUR.DOMAIN"
    _kpasswd._tcp.your.domain. 86400 IN SRV 0 100 464 ipa.your.domain.
    _kpasswd._udp.your.domain. 86400 IN SRV 0 100 464 ipa.your.domain.
    _ldap._tcp.your.domain. 86400 IN SRV 0 100 389 ipa.your.domain.
    ipa-ca.your.domain. 86400 IN A <IP Address>

Because Unbound doesn’t accept BIND zones we need to make some adjustments here to make this work, we need to convert each entry to a “local-data” entry and slap a “server” directive in front of the lot;

server:
local-data: '_kerberos-master._tcp.your.domain. 86400 IN SRV 0 100 88 ipa.your.domain.'
local-data: '_kerberos-master._udp.your.domain. 86400 IN SRV 0 100 88 ipa.your.domain.'
local-data: '_kerberos._tcp.your.domain. 86400 IN SRV 0 100 88 ipa.your.domain.'
local-data: '_kerberos._udp.your.domain. 86400 IN SRV 0 100 88 ipa.your.domain.'
local-data: '_kerberos.your.domain. 86400 IN TXT "YOUR.DOMAIN"'
local-data: '_kpasswd._tcp.your.domain. 86400 IN SRV 0 100 464 ipa.your.domain.'
local-data: '_kpasswd._udp.your.domain. 86400 IN SRV 0 100 464 ipa.your.domain.'
local-data: '_ldap._tcp.your.domain. 86400 IN SRV 0 100 389 ipa.your.domain.'
local-data: 'ipa-ca.your.domain. 86400 IN A <IP Address>'

Pay particular attention to the spacing and the types of quotes in use, Unbound is unnecessarily picky about these things, if Unbound doesn’t start you’ve cocked it up.

If you’re using Unbound “directly” you’ll need to throw this into unbound.conf, for pfSense/opnSense you need to drop them into the “Custom Options” field in DNS Resolver/Unbound DNS->General section in the WebUI.

You might also want to throw in an SRV record for your NTP server(s) too (since Kerberos relies on time sync, you’ll NEED NTP servers, ideally at least two, they can co-reside with your FreeIPA server(s) though) so your clients can autodiscover them, something like the following;

local-data: '_ntp._udp.your.domain. IN SRV 0 100 123 ipa.your.domain.'

The only major downside I’ve run into at this point as far as not using the inbuilt BIND in FereIPA is that SSHFP records can’t get automatically pushed to DNS, if I can be bothered I might figure out a workaround for this one day.