~benthor/treehouse

A very minimal http server with automatic tls certificate handling and privilege dropping
9c104aec — Thorben Krüger 1 year, 5 months ago
improve doc; iterate twice over docroot to validate found symlinks
a578da9c — Thorben Krüger 1 year, 5 months ago
return 404 in case domain doesn't exist in docroot
267cc2a7 — Thorben Krüger 1 year, 5 months ago
symlink to redirect feature

clone

read-only
https://git.sr.ht/~benthor/treehouse
read/write
git@git.sr.ht:~benthor/treehouse

You can also use your local clone with git send-email.

#treehouse

treehouse is a very minimal webserver with automatic TLS certificate handling and privilege dropping. It does what it needs to do to serve a static website via HTTP and HTTPS and nothing else.

I personally use it to serve https://inimeg.space from an old Raspberry Pi.

#Philosophy

  • Prefer radically simple solutions
    • (mark potential pitfalls following from this with XXX in the comments)
  • Don't complicate code in order to make allowances for edge cases
  • Terminate with fatal errors for problems on startup, try to keep running afterwards

#Privilege Dropping

treehouse needs to be started as root in order to bind to the privileged http and https ports 80 and 443. However, as soon as this is accomplished, it uses the setgid and setuid syscalls to (by default) become nobody. As a result, treehouse can only serve files that are world-readable (or belong to nobody).

It is possible to set gid and uid to other values besides the default 65534 (nobody).

#Automatic TLS Certificate Handling

treehouse uses autocert to automatically obtain certificates from Let's Encrypt.

#secdir

A directory for treehouse to store its certificates needs to be provided with the mandatory secdir flag. If the specified path does not exist, it will be created (like mkdir -p). treehouse will also chown this directory to restrict access to the nobody user and group. It currently doesn't chmod an existing directory, a non-existing directory will however be created with 0700 permissions.

To make sure that treehouse can not accidentally serve its private TLS keys, it is an error to specify a secdir that is a subdirectory of docroot.

#Serving Domains

treehouse expects subdirectories in the docroot directory that are named according to the domain that is to be served. It will only attempt to obtain TLS certificates matching the directory names it saw on startup.

#Usage Example

I use treehouse like this:

Under ~/Public, I have a subdirectory called inimeg.space, containing some static html files. This directory gets detected when treehouse starts up.

sudo ~/bin/treehouse -secdir /etc/treehouse/secrets --docroot ~/Public
2023/03/02 Running as root, dropping privileges
2023/03/02 using /home/benthor/Public as docroot
2023/03/02 using /etc/treehouse/secret as secdir
2023/03/02 handling domain http(s)://inimeg.space
2023/03/02 serving HTTPS
2023/03/02 serving HTTP

Since treehouse also serves regular HTTP, you can also test your setup without necessarily invoking autocert. Just create a directory named 127.0.0.1 under your docroot, add an index.html to it and start treehouse.

#Building treehouse

You need some version of Go (version 1.x) installed. Even versions that are several years out of date should still work fine. Just do

go build .

Note that Cgo is not required, To build a static binary that still works even after your libc is updated, simply do

CGO_ENABLED=0 go build .

#Cross Compiling

Here is how I cross compile treehouse into a binary that I can deploy on my old Raspberry Pi 1:

CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=5 go build -trimpath -ldflags "-X main.Version=$(git describe --tags) -w -s" -v .

#Roadmap

The goal is to eventually publish a version 1.0 that can be considered complete and stable. The following items need to be done (in no particular order):

  • [ ] nail down certificate storage, including proper permission handling
  • [ ] be less snarky in error responses (you know what, I think they are fine as they are)
  • [ ] add switches to disable either http or https (dropped in the name of simplicity)
  • [x] improve sanity checking of provided arguments (it's as good as it's going to get)

#Changelog

#main

  • support domain redirections via symlinks in docroot
  • redirect http to https by default, for any domain we serve
    • (content no longer served via http)
  • different request log format

#v0.5.0

  • add improved README
  • restructure code for earlier privilege dropping
  • fix directory traversal, check against docroot instead of working dir
  • remove reverse proxy support in the name of lean-ness

#v0.4.0

  • add support for versioning
  • fix autocert: listen on port 80 for ACME "http-01" challenge responses
  • redirect anything on port 80 to https and the first -domain FQDN

#v0.3.0

  • add very basic reverse proxy support
  • typo fix in documentation

#v0.2.0

  • improve logging

#v0.1.0

  • first release