This repo is now on Codeberg: https://codeberg.org/cliffle/httpd2
See the code(Note: the canonical repo for this project is on Codeberg.)
httpd2 (working title, as it's the second one I've written recently) is a
program that serves web pages / resources to the public. And that's it. For more
details see the manual.
It is inspired by, and patterned after, publicfile, the security-conscious
static file server — except it is HTTPS-native and supports HTTP/2. (I
have a detailed analysis vs. publicfile if you're into that sort
of thing.)
This repo also includes a derivative server, http301d, which only serves
unencrypted HTTP/1 301 Moved Permanently redirects to your HTTPS server, and
doesn't read files from disk etc. It makes a nice port-80 counterpart to
httpd2.
I make no claims that this software is secure or impervious. I wrote this as an exercise in applying secure programming principles to a modern HTTP server.
Basics:
Serves the contents of a single directory as a public website.
Supports modern web standards: HTTP/1.1, HTTP/2, TLS v1.3, etc.
Supports GZIP content encoding to reduce bandwidth and improve latency, using precompressed files to reduce server load.
Scales fairly well. (Tested with 10,000+ concurrent connections using multiple pipelined requests each.)
Architecture and resource usage:
Fully asynchronous architecture means it can handle a large number of simultaneous connections (10,000+) for not a lot of resources.
SMP-aware: requests are distributed over threads, and throughput increases as cores are added.
Allocates memory in proportion to the number of simultaneous outstanding requests, not the size of any files on disk.
Security practices:
Before so much as reading from its socket, httpd2 chroots into the content
directory and drops privileges. This makes it less likely to provide
root-level exploits, disclose files outside the content directory like
/etc/passwd, or run system binaries.
Because the server is designed to serve files that are "public," meaning they are handed to any web user, there is no user/password database or admin interface to probe or expose.
httpd2 can't list directory contents. Navigating to a directory serves an
index.html file if available, and that's it.
httpd2 declines all the HTTP state modification commands — it only
honors GET and HEAD.
httpd2 never runs another program, including script files (there is no CGI
etc. support).
httpd2 ignores files that are not user+group+world readable on the local
filesystem, so even if you accidentally copy a sensitive file into the web
root, it's unlikely to be served.
httpd2 is written in Rust. Compared to C, this means that certain kind of
exploits are much less likely (particularly buffer overruns, use-after-free,
and integer overflow). Compared to other memory-safe
languages, certain kinds of availability problems are much less likely (such
as memory leaks, latency stutter, and the like).
httpd2 uses pinned versions of well-tested libraries, which are statically
linked into the binary, rather than loading whatever version happens to be
installed at startup. (You can build it against MUSL to get a really-really
static binary.)
Pick a directory containing some world-readable files. (No, really, they must be
mode 0444 or higher.) Let's call that directory your_dir_here.
After checking out the server source code, run:
$ cargo run your_dir_here
You should now have an HTTPS server running on https://localhost:8000/ using a
self-signed certificate. Your browser will freak out the first time you try to
visit it, because the certificate is self-signed. In an actual deployment you'd
use an actual certificate. A reasonable configuration for that might be (note
that you'd have to run this as root):
# httpd2 -k /etc/letsencrypt/privkey.pem \
-r /etc/letsencrypt/fullchain.pem \
--chroot \
--uid 65534 --gid 65534 \
-A [::]:443 \
--upgrade \
/public/file/0
This will...
setuid/setgid to the given numeric IDs.upgrade-insecure-requests directive, which tells clients that find
an http: link within our site to try https: instead because our CMS is
old./public/file/0.If you'd like to produce a fully static program without any system dependencies on Linux (not even glibc), the current incantation for building with MUSL on AMD64 is:
$ cargo build --release --target=x86_64-unknown-linux-musl
Or with journald support:
$ PKG_CONFIG_ALLOW_CROSS=1 cargo build --release --features journald --target=x86_64-unknown-linux-musl
142 commits
Rust
100.0%
This repo is now on Codeberg: https://codeberg.org/cliffle/httpd2
See the code(Note: the canonical repo for this project is on Codeberg.)
httpd2 (working title, as it's the second one I've written recently) is a
program that serves web pages / resources to the public. And that's it. For more
details see the manual.
It is inspired by, and patterned after, publicfile, the security-conscious
static file server — except it is HTTPS-native and supports HTTP/2. (I
have a detailed analysis vs. publicfile if you're into that sort
of thing.)
This repo also includes a derivative server, http301d, which only serves
unencrypted HTTP/1 301 Moved Permanently redirects to your HTTPS server, and
doesn't read files from disk etc. It makes a nice port-80 counterpart to
httpd2.
I make no claims that this software is secure or impervious. I wrote this as an exercise in applying secure programming principles to a modern HTTP server.
Basics:
Serves the contents of a single directory as a public website.
Supports modern web standards: HTTP/1.1, HTTP/2, TLS v1.3, etc.
Supports GZIP content encoding to reduce bandwidth and improve latency, using precompressed files to reduce server load.
Scales fairly well. (Tested with 10,000+ concurrent connections using multiple pipelined requests each.)
Architecture and resource usage:
Fully asynchronous architecture means it can handle a large number of simultaneous connections (10,000+) for not a lot of resources.
SMP-aware: requests are distributed over threads, and throughput increases as cores are added.
Allocates memory in proportion to the number of simultaneous outstanding requests, not the size of any files on disk.
Security practices:
Before so much as reading from its socket, httpd2 chroots into the content
directory and drops privileges. This makes it less likely to provide
root-level exploits, disclose files outside the content directory like
/etc/passwd, or run system binaries.
Because the server is designed to serve files that are "public," meaning they are handed to any web user, there is no user/password database or admin interface to probe or expose.
httpd2 can't list directory contents. Navigating to a directory serves an
index.html file if available, and that's it.
httpd2 declines all the HTTP state modification commands — it only
honors GET and HEAD.
httpd2 never runs another program, including script files (there is no CGI
etc. support).
httpd2 ignores files that are not user+group+world readable on the local
filesystem, so even if you accidentally copy a sensitive file into the web
root, it's unlikely to be served.
httpd2 is written in Rust. Compared to C, this means that certain kind of
exploits are much less likely (particularly buffer overruns, use-after-free,
and integer overflow). Compared to other memory-safe
languages, certain kinds of availability problems are much less likely (such
as memory leaks, latency stutter, and the like).
httpd2 uses pinned versions of well-tested libraries, which are statically
linked into the binary, rather than loading whatever version happens to be
installed at startup. (You can build it against MUSL to get a really-really
static binary.)
Pick a directory containing some world-readable files. (No, really, they must be
mode 0444 or higher.) Let's call that directory your_dir_here.
After checking out the server source code, run:
$ cargo run your_dir_here
You should now have an HTTPS server running on https://localhost:8000/ using a
self-signed certificate. Your browser will freak out the first time you try to
visit it, because the certificate is self-signed. In an actual deployment you'd
use an actual certificate. A reasonable configuration for that might be (note
that you'd have to run this as root):
# httpd2 -k /etc/letsencrypt/privkey.pem \
-r /etc/letsencrypt/fullchain.pem \
--chroot \
--uid 65534 --gid 65534 \
-A [::]:443 \
--upgrade \
/public/file/0
This will...
setuid/setgid to the given numeric IDs.upgrade-insecure-requests directive, which tells clients that find
an http: link within our site to try https: instead because our CMS is
old./public/file/0.If you'd like to produce a fully static program without any system dependencies on Linux (not even glibc), the current incantation for building with MUSL on AMD64 is:
$ cargo build --release --target=x86_64-unknown-linux-musl
Or with journald support:
$ PKG_CONFIG_ALLOW_CROSS=1 cargo build --release --features journald --target=x86_64-unknown-linux-musl
142 commits
Rust
100.0%