Complete Guide to Setting Up a Secure Website on a VPS with Lighttpd

Last time I talked about setting up a simple web server and site from my phone. Since then, I’ve been spending a little bit of time getting things set up right. That’s been a bit more challenging than simply getting the thing up and running, so I figured I’d take a moment to jot down some notes in case I ever need to do this from scratch again.

Purchase yourself a Virtual Private Server

Instead of going with a hosted server, I opted for a Virtual Private Server (VPS) because I wanted the experience of getting down and dirty setting one up from scratch on Linux. I went with VPS.net based mostly on price. I don’t expect any traffic on the site except for myself, so the $5/month plan suites my needs and budget. If I was going to set up a production environment for a client, then I’d put a lot more thought into which service provider I’d use.

Install some software on your workstation

VPS.net provides a web based console, but I highly recommend installing a dedicated SSH client, like PuTTY, for administering your remote server. You’re also going to want to install an FTP program like Filezilla, unless you really want to manage and edit all of your files through the SSH terminal. Note that although we call it FTP, we really want to be using SFTP. SFTP operates on the SSH port and is encrypted, whereas plain ol’ FTP is insecure.

Basic server setup

Before we get to installing the Lighttpd (pronounced Lighty) on our new server, we’re going to want to take Server Mom’s advice and perform some basic set up. The first thing we need to do is create a new root user, change the root password that the VPS provider gave us, verify the new user can login & execute with root privileges, then disable root login.

Next, we’ll need to properly set up our firewall. We’ll use the iptables command to set things up. We’ll need to make sure the following ports are open, and disable all the ones we won’t be using.

  • Allow Related & Established connections. Many package managers need this to function properly.
  • Allow all outgoing traffic
  • Disallow all forwarding, we’re not a router
  • 22 – SSH (or whichever port you changed it to if you followed Server Mom’s tutorial)
  • 53 – DNS Lookup
  • 80 – Http
  • 443 – Https
  • 8080 – Or any other available port really. This one is for a test version of our new website. This allows us to safely test changes to our site.

When you’re finished, running iptables -L should return the following information.

Chain INPUT (policy DROP)
target prot opt source destination

ACCEPT tcp -- anywhere anywhere tcp dpt:8080
ACCEPT tcp -- anywhere anywhere tcp dpt:http
ACCEPT tcp -- anywhere anywhere tcp dpt:https
ACCEPT tcp -- anywhere anywhere tcp dpt:ssh
ACCEPT udp -- anywhere anywhere udp dpt:domain
ACCEPT tcp -- anywhere anywhere tcp dpt:domain
ACCEPT all -- anywhere anywhere state RELATED,ESTABLISHED

Chain FORWARD (policy DROP)
target prot opt source destination

Chain OUTPUT (policy ACCEPT)
target prot opt source destination

Install the Lighttpd Web Server

The official CentOS repositories don’t contain the Lighttpd distro, so I followed the instructions I found at Digital Ocean. I didn’t install MySQL or PHP, just the web server under “Step Three”.

sudo rpm --import https://fedoraproject.org/static/0608B895.txt
sudo wget http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
sudo rpm -ivh epel-release-6-8.noarch.rpm

Then run the following command to install Lighttpd:

sudo yum install lighttpd

Create a system start-up link for Lighttpd to enable the service to run at boot:

sudo chkconfig --levels 235 lighttpd on

Start the service and check that it is running:

sudo service lighttpd start
sudo service lighttpd status

Create a Website & Configure the Webserver

I started with the simplest thing that would work, create a web site and have Lighttpd serve it. By default, Lighttpd serves files from the /var/www/htdocs/ directory. I changed that slightly because I wanted to serve a different version of my site on different port for testing. So, under /var/www/htdocs/ I created a /prod/www.domain.net/ directory and a /test/www.domain.net/ directory.

I changed the /etc/lighttpd/lighttpd.conf configuration file to point to these two locations, first, by modifying the document root of the default site.

##
## Document root
##
server.document-root = server_root + "/htdocs/prod/www.domain.net"

Then I bound the other port to the test directory.

# Test Site
$SERVER["socket"] == ":8080" {
  server.document-root = server_root + "/htdocs/test/www.domain.net"
}

Restart the service with service lighttpd restart, ensure you have an index.html file in your site’s root directory,  then navigate to your ip address with your browser. To see the test version of your site, postfix it with :8080.

Congrats! You have a working web server!

Now go to your domain provider, and point your domain name to your server’s ip address.

Https/SSL

Now that we have a working website, let’s get it’s  traffic encrypted. Luckily, VPS.net provides a free cert from Comodo. Getting the cert was fairly straight forward and accomplished through a few emails to their support desk. There also some free options out there today. So, if your VPS provider doesn’t give you one, you can check out CACert.org and Let’s Encrypt amoung others. This is my notebook though, so I’m going to describe the process I went through with VPS.net and Comodo. I kind of followed the instructions I found at Setuptips.com, but some things must have changed since that was written.

First off, there’s no need to generate a self-signed certificate, so skip step one and instead, generate a key file directly, then generate your Certificate Signing Request. Be sure that you enter your domain name when prompted for the “Common Name”

openssl genrsa -out domain.net.key 4096
openssl req -new -key domain.net.key -out domain.net.csr

Second, VPS.net no longer has an option in their ticket system for requesting your free cert, so you’ll need to just directly send an email to support@vps.net requesting it. They’ll ask you for your contact information and the contents of the *.csr file. Be sure to use the same email address that is listed on WHOIS as the contact. The certificate will be sent to the domain contact as a method of verification. A few emails later, you’ll be sent a link where you can download your certificate.

Comodo uses a chain of trust, so along with your certificate, there will be several other certs that you’ll need to use to create a bundle. Extract the zip file into a temporary location. First, we’ll need to create your private *.pem file. Do this by creating a new text file and concatenating your private key from the *.key file along with the contents of domain.crt from the zip file. Next, create the bundle by doing the same thing with the other 3 files Comodo sent, and save the new file as domain.net.ca-bundle.
Move the *.pem and *.ca-bundle file someplace convenient, like /etc/ssl/localcerts/ or /etc/lighttpd/certs/ .

With our certificate ready to go, now we need to enable https for our website in Lighttpd. Open up the /etc/lighttpd/lighttpd.conf file and add the following entry.

# SSL/TLS
$SERVER["socket"] == ":443" {
  ssl.engine = "enable"
  ssl.pemfile = "/etc/ssl/localcerts/domain.net.pem"
  ssl.ca-file = "/etc/ssl/localcerts/domain.net.ca-bundle"
}

When you navigate to https://domain.net, you should now see the green padlock confirming that you’re securely connected to the website. Congrats!

Redirecting Http to Https

The last thing we’re going to do is redirect all http traffic on port 80 to https on port 443. The simplest is simply not doing a redirect and only supporting Https by changing the lighttpd.config to listen on port 443 instead of port 80.

server.port = 443

It works, but users won’t find your way to your site if they type in http://domain.net instead of https. They’re just left with an error in the browser.

The next simplest method is to redirect everything that comes in over http over to https.

$HTTP["scheme"] == "http" {
    # capture vhost name with regex conditiona -> %0 in redirect pattern
    # must be the most inner block to the redirect rule
    $HTTP["host"] =~ ".*" {
        url.redirect = (".*" => "https://%0$0")
    }
}

The problem with this, for me at least, is that it breaks the test version of my site that’s coming in on a non-default port. I really need that to work because I only have one server and can’t really afford a separate test environment for a hobby project. I was able to get around this by refining the regex a bit.

# Redirect http traffic from default port to https, but leave custom ports alone.

server.modules += ( "mod_redirect" )

# Make sure it's http. Don't want to end up in an endless https -> https loop.
$HTTP["scheme"] == "http" {

# Regex matches www and any subdomains, but not if the port is specified.
# This allows us to continue keeping a test version on a different port.

  $HTTP["host"] =~ "(^|.*\.)domain\.net$" {
    url.redirect = (".*" => "https://%0$0")
  }
}

, , , , , ,