Securing a public web server, end to end

I have just put a small Apache web server onto the open internet. It serves a couple of pages and a handful of files. Most of the time it is going to be ignored. Some of the time it is going to be probed by every script kiddie with a port scanner.

Before I plugged it in I went through a checklist. None of the items are exotic. All of them mattered, and I think the list is worth writing down.

Step one: minimal install

The Slackware install procedure lets you tick which package categories you want. For a server that is going to do nothing but serve a few HTTP pages, almost nothing is needed.

I installed: the base system, networking tools, the development tools (so I can build new versions of things), Apache, and a small selection of compression and editing utilities. That is it. No X. No printing. No mail server unless I am sending it from cron. No NFS. No NIS. No RPC.

Every service you do not install is one fewer thing that can be exploited and one fewer thing whose updates you need to track.

Step two: turn off everything you do not need

The default /etc/inetd.conf on Slackware is generous. It enables FTP, telnet, finger, identd, echo, chargen, daytime, time, and a small constellation of other things, all of which run on demand whenever someone connects to the corresponding port.

For a web server, none of these are needed. I commented out every line in inetd.conf that I was not actively using, and restarted inetd. The only things still listening are SSH (for me) and HTTP (for the world).

A common follow-up: confirm what is actually listening with netstat.

netstat -lnp | awk '$1 ~ /tcp/ && $6 == "LISTEN"'

If there is anything in that list you do not recognise, find out why before going further.

Step three: Apache as a non-root user

Apache, by default, on most distributions, will start as root, bind to port 80 (which requires root), and then drop privileges to a less-privileged user before handling requests. This is correct and should not be changed.

The non-privileged user is, by convention, nobody. Some recent guides recommend creating a dedicated www user instead, with no shell and no home directory, so that even if Apache is compromised the attacker has very little to work with.

I have done the latter. The user is in /etc/passwd as www:x:99:99:Apache:/var/www:/bin/false. The shell of /bin/false is the small but important detail — even if an attacker manages to invoke su www, they cannot get a shell.

Step four: sane filesystem permissions

Apache reads files. It must not write them, anywhere it does not strictly need to.

I set the document root, /var/www, to be owned by me, group www, mode 0755. The CGI directory, /var/www/cgi-bin, the same. Apache's logs, /var/log/apache, are owned by root, with the user www having write access only to the specific files Apache itself creates.

The key thing I am avoiding is a configuration where the web server can write files into the directories from which it serves files. That is the path by which most successful CGI exploits become persistent.

Step five: TCP wrappers

For SSH I have configured TCP wrappers so that only my home network's address range can even attempt a connection. Everything else gets refused at the wrapper layer, before the SSH daemon even sees the packet.

# /etc/hosts.allow
sshd : 192.0.2.0/24

# /etc/hosts.deny
sshd : ALL

This is in addition to my ipfwadm rules, not instead of them. Layered defence — firewall, wrapper, daemon configuration — is the only way I have found to be confident that I have not left a hole somewhere.

Step six: logging and watching

I have set Apache to its default verbose logging. I have also set up a small cron job to run my log analysis routines every morning and email me a digest. Nothing fancy: top IPs, error counts, requests for things that do not exist (which are usually probes).

This is the part that matters in practice. None of the previous steps will catch a problem that has slipped through. The logs will, eventually. The trick is to actually read them.

What I have not done

I have not added any kind of intrusion detection. The Snort project is starting to look ready for serious use, but I have not got round to installing it yet. That is the next post.


Back to all writing