darkhttpd with stunnel
2021-11-30
A minimal web server with authentication for serving static content
Some weeks ago, I booked a virtual private server (VPS) for playing around with web servers. I don't want to run anything that needs a high performance, so I bought the smallest solution available: 1 virtual core, 512 MB RAM, 10 GB SSD (cost: 1 € per month). This comes with Plesk Obsidian, Apache and nginx preconfigured but I really wanted to start from scratch with a minimal (suckless) solution.
My aim was to serve a static version of my personal TiddlyWiki with some sort of authentication.
Minimal HTTP servers
I have considered these servers:
All of them consist of roughly 2000-3000 lines of C code (don't know about busybox) and offer similar features. Here is a quick comparison:
busybox | quark | darkhttpd | althttpd | |
---|---|---|---|---|
VIRT¹ | 2428 | 2764 | 2536 | 2544 |
RES¹ | 4 | 1860 | 720 | 672 |
SHR¹ | 0 | 1736 | 648 | 596 |
chroot | ✘ | ✔ | ✔ | ✔ |
drop privileges | ✔ | ✔ | ✔ | ✔ |
dir. listing | ✘ | ✔² | ✔² | ✘ |
(basic) auth. | ✔ | ?³ | ✔⁴ | ✔ |
virtual hosts | ✘ | ✔ | ✘ | ✔ |
CGI | ✔ | ?⁵ | ✘ | ✔ |
- 1: Output of
top -n 1 -p $(pgrep busybox)
etc. (in KiB) - 2: darkhttpd lists file sizes, quark does not.
- 3: There is a patch for digest authentication but it's broken/outdated.
- 4: Basic authentication for the whole website, not on directory level.
- 5: There is a patch for CGI but it's broken/outdated.
How to serve static content via HTTP (Port 80)
I have put my web content in /srv/static
. This folder belongs to root:root
. And I have created a user www
(group www
) for serving the content.
┌──────┐ │ port 80 (http)
│web ├───────────────────────────
│server│ │
└──────┘ localhost │ internet
sudo busybox httpd -h /srv/static -p 80 -f
sudo quark -d /srv/static -p 80 -l
sudo darkhttpd /srv/static/ --port 80 --chroot --uid www --gid www
sudo althttpd --root /srv/static/ --port 80 --user www
(You need superuser rights for running a server on port 80 and also for using the chroot feature.)
I picked darkhttpd because I want to have directory listings and use basic authentication.
Adding HTTPS
When you want to use basic authentication, you need HTTPS. Otherwise you'll send your password unencrypted (though base64-encoded) over the internet.
Most tiny servers don't offer HTTPS built-in. They are meant to be used with stunnel or something similar for getting TLS encryption (HTTPS):
quark does not natively support TLS. A more suckless approach than to implement TLS into it is to use a TLS reverse proxy (e.g. tlstunnel, hitch or stunnel). It accepts encrypted TLS connections and forwards them as unencrypted requests to a server. In this case, one can run such a reverse proxy to listen on a public IP address and forward the requests to a local port or UNIX-domain socket.
(Source: https://tools.suckless.org/quark/)
I picked stunnel because it seems to be the most mature solution and it's referenced in this busybox wiki page and in the althttpd README.
How to configure stunnel + darkhttpd
Preparation
- Install stunnel:
# on Ubuntu
sudo apt install stunnel4
# generic
cd ~/Downloads
wget https://www.stunnel.org/downloads/stunnel-5.60.tar.gz
tar xvzf stunnel-5.60.tar.gz
cd stunnel-5.60/
./configure
make
sudo make install
-
Get a SSL certificate:
- For example via acme.sh (follow instructions in the README).
-
The following examples assume you have put your certificate files here:
/etc/acme.sh/static/cert.pem
/etc/acme.sh/static/key.pem
Let's consider three scenarios:
1. http and https
In case you don't want to use basic authentication (or if you do, make sure not logging in while connected via port 80):
┌──────┐ port 80 (http)
│web ├┬──────────────────────────
│server││┌───────┐
└──────┘└┤stunnel╞═════════════════
└───────┘ port 443 (https)
- Start darkhttpd on port 80 (see above).
- Configure stunnel like that:
/usr/local/etc/stunnel/stunnel.conf
:
output = /usr/local/var/log/stunnel.log
cert = /etc/acme.sh/static/cert.pem
key = /etc/acme.sh/static/key.pem
[https]
client = no
accept = 443
connect = 80
- Start stunnel:
stunnel
2. https only, with basic authentication
┌──────┐ port 10080 (http), 127.0.0.1
│web ├┬──────── localhost │ internet
│server││┌───────┐ │
└──────┘└┤stunnel╞════════════════════
└───────┘ port 443 (https)
- Start darkhttpd with basic authentication, only accessible from localhost:
PASSWORD='s3cr3t-p4ssw0rd'
sudo darkhttpd /srv/static/ --port 10080 --chroot --uid www --gid www --addr 127.0.0.1 --auth john:"$PASSWORD"
(Could also be started without sudo
and --chroot
since we use a port number > 1024.)
- Configure stunnel:
/usr/local/etc/stunnel/stunnel.conf
:
output = /usr/local/var/log/stunnel.log
cert = /etc/acme.sh/static/cert.pem
key = /etc/acme.sh/static/key.pem
[https]
client = no
accept = 443
connect = 127.0.0.1:10080
3. https plus a redirection to https on the http port
Follow steps from 2. https only, with basic authentication
.
Additionally start another darkhttpd instance which redirects HTTP requests on port 80:
sudo darkhttpd /srv/static/ --port 80 --chroot --uid www --gid www --forward-all https://yourdomain.info