Setting up Apache2 with SSI Enabled for Development

Last Updated: 2018-08-15

Why Bother?

For a lot of people who got into tech, at least at my age, the gateway drug was usually HTML - at the risk of dating myself, there was a popular webgame at the time called Neopets that actively encouraged the use of some primitive html techniques to design player-owned pages. The appeal was pretty simple - you could do it with a text editor, no extra software required. The earliest versions of the website was designed in almost exactly this way! Sure, the blog required Jekyll on a server somewhere, and the website itself needed hosting, but the website itself was built using nothing but gedit, chrome, and launching files directly from the disk!

However, HTML5 killed the <iframes> tag, which was the way I learned to do things like headers and footers. I got around this in the original version of the site by hand-coding a lot. When it came time to do the Summer 2018 overhaul, the thoughts of rehashing all that code manually were more than a little intimidating. Fortunately, there's a better way to do what iframes used to do, and quite a bit of other good things besides - the Server Side Include, or SSI.

SSI is a feature supported by Apache and several other server suites which serves as a sort of gateway drug to proper Common Gateway Interface scripts. SSI provides a framework for super minimal insertion of dynamic elements into otherwise static content. It wouldn't be appropriate to develop an entire webapp based on SSI, but as a stand-in for iframes, they're top notch. However, SSI isn't a native part of the HTML standard. To use it, you need to view the documents in question through a web server that supports some form of SSI syntax and can parse the commands you hide in the comments on the page.

This means spinning up an Apache instance, but any time you have a running server listening to a port on the computer, you're going to want to do some securing. Sure, you could stand up some standalone machine in the cloud or on a rack somewhere, but I fully believe in doing for ourselves when we can, and that meant coming up with a way to do it safely.

Installing and Hardening Apache

As it happens, I wrote a guide on this already. I'll save you the boring details - for one thing, the process of this is going to depend a lot on your existing setup, and for another, any advice I give you as far as hardening will be stale by the time this gets pushed to the server. The short version here is to pay attention to CVEs for Apache and to apply your patches. Apache is available for pretty much every operating system I'm aware of, so that's what I used.

When setting up Apache on your development machine, there's no harm in making it a manually-launched process instead of a service. On linux, you can do this by invoking sudo update-rc.d apache2 disable at the commandline, at least on systemd-based distros like Ubuntu. I recommend this - you're not intentionally hosting content to anybody but yourself, so there's no harm in making it a manual step

My recommended SSI Settings

Apache offers a few ways for you to control the use of includes - and I tend to stick to their recommendations pretty closely. You can enable SSI includes from the apache config files by adding Options Includes+ to the directives for a given directory. Further, for whatever directory will be the server-root (default: /www/html), I would set FollowSymLinks on the same line, for a reason we'll see later.

Finally, for the directory you're using, I strongly advise using XBitHack on for the SSI method. Essentially, there are two SSI methods - to define a filter which would process any files with a given extension for SSI directives, or the XBitHack, which checks to see if the execute bit is set for a given file, and if it is, then checks for SSI directives. This allows you to explicitly define the exact files you want to parse for SSI, and since apache SSI can run a limited set of console commands and otherwise trigger executables on the host machine, for security reasons, I like whitelists over wide nets. Obviously, this is only an option on unix hosts.

If you don't have the ability to set up the execute bit, you can create filters by following the instructions here.

Why The Security Concerns?

You're probably thinking to yourself at this point why you should be concerned with the security of the apache service if you're just hosting this locally. After all, you're just sitting on a network, hiding behind the NAT and the firewall on your router. Can't be that bad, right?

If your development rig is a desktop that lives in your house, sure. You're probably right. However, if, like a great and growing number of people, your primary machine is a laptop that travels with you, you have to operate under the assumption that the networks you are connecting to are hostile.

Defense in depth is a good idea. Just like how we don't pin all our security assumptions on the supposed attachment-filtering properties of Google Drive, we shouldn't pin them all on our assumption that our router, which nobody patched, is secure.

Ultimately, any computer running apache is a web server, with all the vulnerabilities thus implied. It may not be globally available thanks to the NAT your LAN hides behind, but any computer on your network can access it. Once you have apache running on your computer, point your browser at 127.0.0.1 and enjoy the splash page. Now point your browser at your computer's local IP, and get the same page - assuming the firewall on your computer allows incoming connections at 80.

Special Hardening for This Usecase

Firewall Configuration

Whether or not your router has a firewall on it, it's a good practice to have your device firewall active as well. For our purposes, the exact firewall doesn't matter. All we need to know is that we should be blocking incoming Any/Any for TCP on port 443, since we aren't going to bother using SSL. We should also block incoming any-any on TCP port 80, with an exception for 127.0.0.1 (the localhost interface) and, if desired, for a mobile device. I keep my phone statically mapped on local lan, so if I want to check the mobile appearance of a development version of the site I can ping the lan IP of my development machine and explore the site just like you would if it was live.

Of course, all this firewall config is unnecessary if you don't care about the mobile aspect, for some reason - perhaps your site or app isn't intended to be compatible with mobile platforms. Most desktop firewalls allow you to block any incoming connections by default - they open the ports only when you first initialize an outbound link. Sort of neat.

Port Oddness

If for some reason you really wanted to do so, you could modify apache's ports.conf file to listen at a truly oddball port. Combine this with a firewall and it's probably not even going to be evident to other machines on the same network that you have a web server available.

Symbolic Linking

I mentioned earlier we should set some symlinks, and this is for two good reasons - the first is that it's more comfortable to work out of somewhere in your home directory than some far-flung position like /var/www/html. The second is that you never have to touch the contents of the web server directly. Instead, the web server seeks the file in a read-only way. What's more, you can symlink whole directories, so in my case, /var/www/ doesn't contain a directory at all, but a symlink back to my working directory.

Ad Hoc, Et Solo Ad Hoc

We're spinning up this server for this purpose and only this purpose. Ideally, we should disable it from automatically booting as a true service would, and stop it when it's not in use. I mentioned the method for disabling autostart above - that done, you can start and stop apache manually - the exact command is going to vary a bit depending on version and OS, so your best bet is to refer to the documentation on this one.

In my case, I start and stop apache2 as a service using sudo service apache2 start and sudo service apache2 stop, with the above-mentioned configuration change to keep it from autostarting at boot. This will probably work for any of you working within a linux - as usual, windows users will have to refer to their documentation, I'm afraid.