This blog has a new backend
Before beginning, here is the source code for this website and everything I'll be talking about.
If you've read my previous post and clicked on the little collapsible section up top, you will already know that something is afoot. I wrote the initial code for this blog nearly 5 years ago, since then it's mostly remained unchanged, with the occasional face-lift or Emacs version bump.
The way it used to work was that I utilized org-thtml, which is a Emacs Lisp HTML templating library, to generate HTML out of a set of Org Mode files. This has worked great and hasn't changed, what has changed is how and where that build is performed. It used to be done inside of the Nix sandbox (that's still supported), then the output would be served by Nginx, Caddy later. All of that was bundled together into my home servers NixOS configuration. Doing everething within NixOS at Nix build time was great for reproducibility and sandboxing, however it meant that if I wanted to publish a new post (like this one), I had to redeploy my server, which unfortunately carries a certain degree of risk with it. Risk I don't want to take on when I'm on the train or at my parents place in Slovakia, especially not for just a blog post.
A remedy to this problem, would have been to utilize some kind of CD system, to deploy to my server automatically, however CD scares me. I'm okay with CI, but I will never utilize true CD for any of personal devices. I want to be in the loop, physically activating a security key whenever a deploy is about to happen, both for security reasons and because with each deploy things may break and I want to be there when they do.
So what gives? If I don't want to do CD, updating the website will always be manual. Except now it isn't. I'm not okay with CD against my NixOS configurations, that's giving the CI/CD way too much priviledge, might as well hand it my GPG private key. What I however don't mind at all, is tightly scoped pull based CD, which is exactly what this website uses. Onto the details!
The Details
Since about 2 weeks ago, this website is in fact served by a Haskell daemon running on a VPS. This daemon also takes care of rebuilding this website, by talking to a Emacs daemon that is running as a systemd service. That means that if you change the source files on my VPS and make the Haskell daemon rebuild the website, those changes will be reflected on the web. Neat huh?
What makes this even better is that I already have CI, buildbot-nix specifically, and I can use it's effects feature to trigger this website to rebuild itself. However, this would mean that anyone with access to my website's git repository can perform ACE (arbitrary code execution) on my server. The HTML templates for this website can and do contain inline Emacs Lisp code which gets ran whenever Emacs rebuilds the website. That's very bad for security! How do we get around it? With GPG of course.
I have a habit of sigining most of my commits using my security key, sure it's annoying, but it means that software can relatively easily verify, with relative certainty, that I was in the loop when a commit was made. I baked this exact signature check and subsequent assumption into the Haskell daemon. Before performing an update it'll first check whether any new commits are signed by my security key. If they are, the rebuild is triggered, if not an error is signaled to the client.
The last defence I have put in place, is a very simple signature check between the client program asking the Haskell daemon to perform a rebuild. It's very similar to how Forgejo secures webhooks, just a signature on the payload which contains the commit to update to. Simple and effective.
The usual
As expected, I also utilized all the systemd sandboxing knobs to sandbox the both the Haskell daemon and Emacs daemon as much is possible, without affecting the job they need to do. This should protect the rest of the system, even if a highly unlikely compromise were to occur.
Lastly this runs on a VPS that doesn't contain much data, so it's not a very high risk environment in the first place.