berthubert / trifecta

educational image sharing website built on a combination of modern C++, web and database technologies
MIT License
149 stars 9 forks source link

trifecta

A simple open source image sharing site, built with a combination of modern C++, database and web technologies. Intended to both be useful and make some points.

Webpage: berthub.eu/articles/trifecta, a blog post detailing "why", blog post about the various technologies used.

Description

Trifecta is a computer program that delivers you a website/web service. Your personal imgur. You can paste or drag images to Trifecta. If you upload an image, a post will be created for it automatically.

A post can contain multiple images. Each image can have a caption, and each post a title.

Posts can be public or not, or have a time limit on their public visibility. As owner of a post you can extend or change this limit.

Users can sign in using an temporary email link, and also reset their password this way. Users need not have an actual password.

Posts in Trifecta get opengraph tags so you get nice previews on social media and in messengers.

Available as docker/podman, rpm, deb and source.

Goals

In ~1200 lines of C++ & Javascript, this gets you a safe and secure image sharing site that you could run yourself and hopefully forget about.

Note: the security goals have not yet been achieved, heavy development is ongoing. There are no known problems though.

What is the point?

For one, I'd love to have an 'imgur' just for myself, one that does not monetize me or the viewers of my images. But I also do not want to host a giant web based solution with multiple security issues per year. Or month. I yearn for software like djbdns or qmail that you could trust to not have gaping security holes all the time.

Fundamentally, there is no way to keep a solution with hundreds (or thousands) of dependencies secure. Yet, this is what modern web development has mostly become.

Trifecta is an attempt to create a useful and reliable piece of software that also showcases that it is still possible to write small programs with a much more limited attack surface.

I wrote much more about the why of it all in this post on modern software practices.

Status & Thanks

Development is still ongoing, but usable.

Many thanks are also due to early users, testers and contributors:

While having 700 (indirect) dependencies is not good, benefiting from very good existing software is great:

Known problems

Security issues that have been addressed:

More low hanging fruit can be found in the GitHub issues list.

Concepts

More about this can be found on the Trifecta web page.

The software consists of a server process, which provides an API for creating users, posts, images etc. It hosts all these in a single sqlite3 database. The server also hosts a few Javascript and HTML files that provide the frontend. To send out password reset/passwordless login emails, it connects to an SMTP server.

To run the software, put it behind a real webserver that does TLS and certificate management for you. Instructions are in the README.

The server configures the sqlite database automatically, there is no need to load a schema. Out of the box, the system is not operational as it has no admin user. If you run the server with --rnd-admin-password it will create an admin user with a randomly generated password for you. If you run it again like that it will only change the password.

Configuration

Configuration is read both from the command line and from the environment:

The command line overrides the environment variables.

There is also a flag:

To get started:

trifecta --rnd-admin-password

And you should be in business. This creates a random admin password, which it prints for you. It also prints out the URL on which you can contact the service. On first use you'll get some scary looking SQL errors, these go away once you've uploaded your first image.

To do admin things (like create new users), visit /#admin

To take this into production using nginx (for robustness, letsencrypt, TLS etc), try:

upstream backendtrifect {
    server 10.0.0.12:3456 fail_timeout=5s max_fails=3;
}

...

location /trifecta/ {
    rewrite    /trifecta/(.*) /$1 break;
    proxy_pass http://backendtrifect;
    add_header X-Cache-Status $upstream_cache_status;
        client_max_body_size 50M; 
        proxy_set_header X-Real-IP $proxy_protocol_addr;

        add_header X-Cache-Status $upstream_cache_status;
        add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
        add_header X-Frame-Options "SAMEORIGIN" always;
        add_header X-Content-Type-Options "nosniff" always;
        add_header X-XSS-Protection "1; mode=block" always;
        add_header Referrer-Policy "same-origin";
        add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
}

Do know that the default configuration of Trifecta will listen on 127.0.0.1 only, use -l 0.0.0.0 (or TRIFECTA_LOCAL=0.0.0.0) to change this.

It should be obvious, but do not run Trifecta without a front-proxy that provides TLS (except for local testing).

Podman/Docker

You can get the Docker image by pulling berthubert/trifecta from the Docker Hub. There is also a Docker-compose file in example-configs/compose.yaml, through which you can also configure your container.

If running without Docker-compose, this works both for Podman and Docker:

docker run --init -p 1234:1234             \
  -v /some/place/local-db/:/local-db       \
  berthubert/trifecta                      \
  --rnd-admin-password

This syntax means:

This will exit quickly after creating the admin user.

Next up remove --rnd-admin-password, and start the container again, and you are in business.

Note that if you run using the Docker-compose file, there is a 'command' statement there for --rnd-admin-password which you need to uncomment once.

Building (optional)

Requires libsqlite3-dev. On Debian derived systems the following works:

apt install libsqlite3-dev python3-pip pkg-config

In addition, the project requires a recent version of meson, which you can get with 'pip3 install meson ninja' or perhaps 'pip install meson ninja' and only if that doesn't work 'apt install meson'.

The meson in Debian bullseye is very old, and will give you a confusing error message about 'git' if you try it. If you enable bullseye-backports you can do apt install -t bullseye-backports meson and get a working one. Or use the pip version, which is also great.

Then run:

meson setup build
meson compile -C build

Distributing binaries, docker etc

To make a more portable binary, try:

LDFLAGS="-static-libstdc++ -static-libgcc" meson setup build --prefer-static
meson compile -C build/

Or even a fully static one:

LDFLAGS=-static meson setup build --prefer-static -Dbuildtype=release -Dcpp-httplib:cpp-httplib_openssl=disabled -Dcpp-httplib:cpp-httplib_brotli=disabled

meson compile -C build/

From this it is trivial to create a Docker or podman image:

strip build/trifecta
podman build -t berthubert/trifecta -f Dockerfile

The Dockerfile is very simple, and worth reading. To export this image, try:

podman save localhost/berthubert/trifecta -o trifecta.container
bzip2 trifecta.container

This gets you a 2.0 megabyte compressed container you can distribute.

To run the image, run this once:

Simple Docker build

If you do not want to build trifecta yourself to generate a Docker image, use Dockerfile.full-build:

docker build -t berthubert/trifecta -f Dockerfile.full-build .

Inspiration

The SUSE past-o-o pastebin: https://github.com/openSUSE/paste-o-o

cottow's 6paster: https://github.com/cottow/6paster

Project with similar aims, a webmail solution built on Go and a functional language called Elm: https://github.com/inbucket/inbucket