WordPress is one of the most popular content-management system in the world. Powering 33% of the top sites according to Wikipedia1, it therefore shouldn’t be surprising that WordPress is commonly targeted by hackers and script-kiddies alike. Plugins and themes are what make WordPress great and well-loved, but they are also a common entry point for hackers –– a 2016 Wordfence survey2 found that compromised plugins contributes to more than 55% of the hacks.
While many sources online offer WordPress security hardening tips and techniques3, an unprivileged Linux container will complement these techniques to offer greater security and isolation.
This post will show you how to install WordPress on an existing website in an unprivileged Linux container, so it gets harder for malicious actor to gain root of your entire system, and set up Apache2 reverse proxy to expose the WordPress to the Internet.
The Blue Print
We are installing a containerised WordPress on a website that is already served with Apache2. In my specific case, I wanted WordPress to be running on
blog.warmwolf.com (hello, we are here now!), whereas other files will be served via Apache as normal. We can visualise the relations as follows.
The main Apache2 server listens on port 443 for HTTPS connections. If the hostname in the HTTP request header is
www.warmwolf.com, files will be served normally by Apache. However, if the hostname is
blog.warmwolf.com, the Apache2 acts as a reverse proxy and the request (on port 443) will be forwarded to another Apache2 instance in the container that is listening on port 80. WordPress can then be served from within the container.
Here is the organisation of this article:
- On this page, we start with a bit of literature so we know what we will be doing later on.
- On Page 2, we create an unprivileged LXC container.
- On Page 3, we set up Apache2, MySQL and PHP in the container. We also set up our main Apache2 as a reverse proxy to serve pages from within the container.
- On Page 4, we set up WordPress inside the container.
- Finally, we end with some closing thoughts.
LXC (LinuX Container)
What is LXC?
In short, LXC offers an isolated environment just like a virtual machine but without the overhead of running a separate kernel and simulating the hardware. Docker prior to v1.10 even used LXC as the container execution driver.
How does unprivileged LXC work?
LXC allows the creation of an unprivileged container. The goal is to contain WordPress such that if it is wrecked by an attacker, it will be hard to have privilege escalation to the host computer. LXC’s kernel level isolation is achieved using cgroups and namespaces.
In the Linux world, each user is assigned an identifier (user ID / UID) which determines the resources the use can access. Root user has an UID of
0 and most Linux distros reserve first 100 UIDs for system use. New users are generally assigned numbers starting from
1000. Permissions can be assigned for groups of users as well with group IDs / GIDs. The users are allowed to impersonate a certain range of IDs, and these are known as subordinate UIDs and GIDs (subuid and subgid)4.
LXC makes use of these features. All the UIDs and GIDs of a container can be remapped to a high IDs on the host computer. Say, UID
65536 of a container can be remapped to UID
296608 on the host. A root user of UID
0 within the container is equivalent to UID
231072 in the host. While processes within the container will not be aware of these limitations, if an attacker managed to escape the container, their damages are limited to only some unprivileged users in the host! The container is unprivileged!
Why not Docker?
Docker supports user namespace remapping5 as well, but LXC can be used with further hardening such as AppArmor and SELinux, it appears that Docker does not yet support AppArmor at the time of writing, and volumes in Docker can cause issues with SELinux. Anyway, for the purpose of walling off WordPress, LXC should be sufficient.
Apache2 As a Reverse Proxy
A forward proxy (or often just called proxy), as opposed to a reverse proxy, allows computers usually within a network to connect to some other computers outside the network by acting as a middle-man. This can be when clients in an internal corporate network is trying to connect to the Internet via the corporate firewall, or when devices within the Great Firewall of China™ is trying to escape surveillance. In both cases, the proxy in the perspective of the clients is a stepping stone to communicating with other computers.
A reverse proxy, however, returns resources from other computers requested from clients while appearing as if the resources are from the proxy itself. An example would be when you connect to
google.com, you do not actually connect to the actual web servers directly. You probably passed through some reverse proxies for firewall protection, load balancing, content caching, and so on. From the perspective of the client, this reverse proxy doesn’t actually exists.
From the “Blue Print” figure, you can probably start to appreciate why the Apache server is acting as a reverse proxy. Clients request for
blog.warmwolf.com to which the Apache server appears to respond with, but in reality it retrieves the blog from another server.
On the next page, we will go through the steps to make a secure WordPress installation.