Setting up a UniFi controller on CentOS with SELinux

A few weeks ago I was tasked with setting up UniFi WiFi controller software on a server running CentOS. The software only seems to be officially supported on Windows and Mac, but it’s all written in Java (write once run anywhere™) so that makes this quite feasible. And for the most part it proved to be an easy enough task: there are plenty of guides for exactly this scattered around the Net.

The only problem: almost all of them tell you to disable Security-Enhanced Linux to get the controller to work. That wasn’t a permanent option, so after setting the WiFi controller up I went about figuring out how to make it play nicely with SELinux.

Installing the UniFi controller

Note: this section is largely paraphrased from here.

First, we’ll need to install MongoDB for UniFi’s use. To do this, we’ll need a repo file for Mongo so yum knows where to find it.

vi /etc/yum.repos.d/mongodb.repo
[mongodb]
name=MongoDB Repository
baseurl=http://downloads-distro.mongodb.org/repo/redhat/os/x86_64/
gpgcheck=0
enabled=1

That in place, we install Mongo and some other necessary stuff:

yum -y install mongodb-org mongodb-org-server java-1.7.0-openjdk unzip wget

We add a user for the UniFi controller:

useradd -r unifi

Now we download the UniFi controller software (written in Java) to /opt/UniFi:

wget http://dl.ubnt.com/unifi/3.2.1/UniFi.unix.zip
unzip -q UniFi.unix.zip -d /opt
chown -R unifi:unifi /opt/UniFi

We want the controller software to start up with our server, and the easiest way to get that right in newer versions of CentOS is to write a systemd script for Unifi. So we make a new file in the appropriate folder and write some stuff to it:

vi /usr/lib/systemd/system/unifi.service
# Systemd unit file for UniFi Controller

[Unit]
Description=UniFi AP Web Controller
After=syslog.target network.target

[Service]
Type=simple
User=unifi
ExecStart=/usr/bin/java -Xmx1024M -jar /opt/UniFi/lib/ace.jar start
ExecStop=/usr/bin/java -jar /opt/UniFi/lib/ace.jar stop
SuccessExitStatus=143


[Install]
WantedBy=multi-user.target

Now that that’s done we just need to enable MongoDB and the module we just wrote:

systemctl enable unifi.service
chkconfig mongod on

And reboot!

systemctl reboot

The controller web interface is now available at https://serveripaddressgoeshere:8443. Or it would be if you had SELinux disabled. Feel free to disable SELinux temporarily and play around with it, but remember to come back for the next section of this tutorial.

Configuring SELinux

To get it to work without disabling SELinux, we need to assure SELinux that the controller is a legitimate program and that its operations are trustworthy. There are basically three ways to go about doing this:

  1. Set SELinux file permissions (a fine-grained system for controlling what processes can do what on each file) correctly for the program’s files.
  2. Set SELinux booleans correctly for our operations (stuff like “can host webserver accessible by outside network = true”).
  3. Write a new SELinux policy to allow the program to do what it needs to.

Ideally you want to avoid the third method, although it’s probably the easiest and most likely to get things to work – a policy of writing policies for every little thing just makes security even more complicated than it already is. So my solution ended up being a mix of the first and second methods.

First off, we’ll need to install semanage, the program which lets us do all the SELinux configuration stuff we’re about to do. It’s provided by policycoreutils-python, so we do the following:

yum -y install policycoreutils-python

Now we can start configuring SELinux. First, let’s tell it about MongoDB’s default ports:

semanage port -m -t mongod_port_t 27117
semanage port -m -t mongod_port_t 27017

Then we give MongoDB access to some of the UniFi controller software’s folders.

chcon -R --type=mongod_var_lib_t /opt/UniFi/data
chcon -R --type=mongod_var_lib_t /opt/UniFi/logs

Now we specify that we’ll be allowing UniFi to run a web server by giving httpd (HTTP daemon):

chcon -R --type=httpd_sys_content_t /opt/UniFi/lib
chcon -R --type=httpd_sys_content_t /opt/UniFi/webapps
chcon -R --type=httpd_sys_content_t /opt/UniFi/work

Note that if restorecon, a command that resets all SELinux file permissions, is run on the file system, these settings will be lost. Making them permanent is left as an exercise to the reader.

Now that that’s done, we can set our SELinux booleans to allow our server to host a web server.

setsebool -P httpd_can_network_connect=1
setsebool -P httpd_can_network_connect_db=1

And we’re done. If everything’s gone well, we should now be able to connect to the UniFi controller admin web interface over https://serveripaddressgoeshere:8443. If not, well, sorry. :(

It’s a good idea to set up a firewall at this point, which again I’ll leave as an exercise for the reader (use iptables, firewalld or whatever else floats your boat). Unifi uses these TCP ports by default:

  • 8081 (for management purpose)
  • 8080 (device inform)
  • 8443 (controller UI / API)
  • 8880 (portal redirect port for HTTP)
  • 8843 (portal redirect port for HTTPs)
  • 27117 (local-bound port for DB server)

And this UDP port:

  • 3478

Lastly, keep in mind that UniFi WiFi access points should probably not be used as frisbees.

References

Webmentions(?)