UniFi Controller, Ubuntu 18.04, and oVirt
Next on my long list of things to get running in the house is a more professional WiFi setup. I had heard great things about Ubiquiti and saw that one of their UniFi AP-AC-PRO units was on a pretty good sale over at Amazon. I picked one up and in keeping with all of my other projects aimed to set it up on my VM cluster.
VM Setup
All of the Ubiquiti hardware requires a control plane application running somewhere to manage and configure the site. Thankfully, the application is available for Linux as well as Windows and Mac; plus it is packaged conveniently for Debian derived distributions. The install guide recommends Ubuntu 16.04 or 18.04 to host the application and since the built in oVirt image repository has an image for 18.04 I went for the newer version.
After pulling down the template I set up a new VM with 2CPUs, 3GB of RAM, and 20GB of disk space as per the user guide. I also only included a single NIC with a static IP, but I may add a second at some point in the future if I implement a dedicated management network fabric instead of just running everything through the same switches. All pretty simple so far.
I boot up the VM and notice after a while that it seems to be hung during the boot process. I pulled up the console window and saw that the boot process was stuck at initializing BTRFS for some reason. A quick search brought me to this bug report which suggested adding a VirtIO serial console to the VM configuration to resolve the issue. A quick configuration change and reboot later and we are off to the races.
ovirt-guest-agent and freeipa-client
The next hurdle for integrating this system with my larger architecture was to
add both ovirt-guest-agent
and freeipa-client
so that everything integrates
nicely. The install was simple:
$ sudo apt-get update
$ sudo apt-get upgrade
$ sudo apt-get install ovirt-guest-agent freeipa-client
But when I tried to start up the guest agent I ran into the same permission error I had when installing SecurityOnion. Thankfully the log directory was already owned by the correct user and I just needed to reload the udev rules to get the permissioning issue sorted out.
$ sudo udevadm trigger --subsystem-match="virtio-ports"
Next up was getting the VM added to FreeIPA and again we hit a snag. Apparently,
even when I set the full FQDN for the server through cloud-init
Ubuntu only
takes the host portion and sets that as the hostname. Unfortunately, the IPA
client installer assumes that the hostname is set to the FQDN and fails if it
isn’t. Again a pretty easy fix:
$ sudo hostnamectl status
$ sudo hostnamectl set-hostname controller.example.com
$ ipa-client-install --mkhomedir
Interestingly, this version of ipa-client-install
doesn’t provide options to
force NTPd usage or automatically create DNS records for the new server. That is
an easy fix though and I just added the A and PTR records by hand into FreeIPA.
With that, a quick reboot and we were ready to go installing the UniFi Controller.
UniFi Controller Install
The UniFi Controller is a Java application and ever so slightly involved to get
running. Thankfully, a member of the Ubiquiti community has provided a
handy little script which automates all of the installation.
Now usually I run screaming from anything wget X | sudo bash
but after pulling
this script down manually and reading through everything that it is doing it
looks free of anything shocking. I might try re-implementing it with Ansible if I
get the time but for now the initial install was as simple as running it and taking
all of the defaults.
Once the install finished I pulled up the web UI as described in the user guide and ran through the setup process there as well. When that finished I tried to adopt my AP but I noticed that while my AP was connected to the network and I could see it’s DHCP lease; the controller wasn’t discovering it. That sent me down yet another troubleshooting rabbit hole.
Device Discovery and Adoption
The first problem was that the controller VM and AP actually live on different
subnets within my home network and there is a fairly restrictive firewall between
the two to prevent anything nasty coming in over the WiFi. The controller and
any managed devices need several TCP and UDP ports opened so I
implemented those firewall changes and tried looking for the device again. No
dice. A quick nmap
from my laptop of the controller VM showed that the required
ports were accessible through both the router and host firewall so it was on to
the next theory.
Moving on, the APs are actually full Linux systems under the hood so the next
debugging step is to actually log in and view the logfiles.
Until the AP is adopted by a controller the default SSH credentials are
ubnt:ubnt
which will let you poke around the AP’s filesystem. I dug around in
the router’s DHCP lease list until I found the address the AP had pulled and
used that to SSH in.
After poking around in /var/log/messages
I saw that the AP seemed to be trying
to connect to http://unifi:8080/inform
and was unable to resolve the name. I
also found this forum post that said to reset the inform
URL to have the device connect correctly, so I tried that next using the DNS
name I had configured for the controller earlier.
$ set-inform http://controller.example.com:8080/inform
Immediately, the AP showed up in the controller interface allowing me to adopt it and provision. Once the initial provision was done I modified the AP settings to use a static IP and my FreeIPA DNS servers before allowing it to reconfigure again. Once that was done I added DNS entries for the AP and started setting up my WiFi networks.
Random Device Disconnects
While I was working through my network configuration I noticed that the AP was
displaying as Heartbeat Missed
and then Disconnected
even while I could see
it was still ping-able and passing traffic correctly. It seems that this is a
common issue and that generally the solution is
to remove and re-adopt the device. Unfortunately for me, that did not work as hoped
and shortly after re-configuring the device it was again in the Disconnected
state.
The next time the AP disconnected I SSHed into it using the post-adoption credentials
provided in the controller UI to look at the log files. This time I didn’t see
a failure to resolve unifi:8080
but rather the controller.example.com:8080
I had configured when setting up the device. After reconfirming that the DNS
servers were correctly configured for the AP and that it could curl
the
controller manually, I tried running set-inform
again. This allowed me to
re-adopt the AP and reconnect it, but only temporarily. Pretty soon it was
Disconnected
again and the logs still said that it was unable to resolve the
controller. The solution, frustratingly, was to simply use the controller’s IP
address instead of it’s DNS hostname when running set-inform
and that seems
to have the AP connected properly now. I suppose that there is either flakiness
in the DNS resolution on the AP or in how my FreeIPA servers are returning responses,
but that’s a debug for another day.
An Aside About mDNS
The last thing that I noticed while getting the WiFi networks up and running was
that Avahi/Bonjour/mDNS packets were not getting sent properly across the WiFi. Due to
this only other devices on wireless were able to see each other and they couldn’t
see any of the other devices connected to the LAN. This also seems to be a
problem other people have faced with Ubiquiti equipment. My
solution ended up being a combination of enabling Multicast Enhancement (IGMPv3)
for the WiFi networks where I wanted mDNS working and restarting the Avahi mDNS
reflector running on my router which was configured to pass mDNS packets between
the different subnets it manages. With those changes devices started popping up
again on the Macs and everything looked like it should.
Conclusion
After all the work of getting it set up the AP has actually worked fairly well and looks to provide what I need in an access point. Plus I must admit I am looking forward to a more centralized and streamlined configuration story as opposed to the consumer router turned AP route. The next couple of weeks will tell how well the controller connection holds up and how it handles multiple users streaming at the same time.
Update - Custom SSL Certificate
So I finally got tired of seeing the SSL error every time I wanted to connect to
the controller interface and I decided to issue a certificate for it from my
personal CA. I generated the CSR as described in the official documentation
but ran into issues when I tried to import the certificate and CA from FreeIPA.
The first error was just generally not being able to import the certificate with
ace.jar
and was resolved by stripping all newlines from both the certificate
and CA files.
$ tr -d '\n' < host.crt > host.strip.crt
$ tr -d '\n' < ca.crt > ca.strip.crt
With that I was able to get ace.jar
to at least parse my host certificate but
it failed to recognize the CA certificate and would throw an error saying that
it lacked an appropriate Certificate Authority in the keystore. I managed to fix
that by directly importing my CA certificate into the UniFi keystore using the
following command and the password aircontrolenterprise
which is the UniFi
default.
$ keytool -import -trustcacerts \
-keystore /varlib/unifi/keystore \
-alias custom-root \
-import -file ca.crt
Since my controller host is already registered with FreeIPA the certificate was available in the global keystore but for some reason the controller wasn’t picking it up. When keytool prompted to add the key anyway I said yes and the key was installed. From there the documented install method worked just fine.
$ java -jar lib/ace.jar import_cert host.strip.crt ca.strip.crt
$ systemctl restart unifi