This repository contains tools for building static HTML pages, some of which are password-protected. It also contains other tools to help me secure my private affairs.
The website contains some private information I may need if I lost my phone while traveling the world. For now it contains:
- My 2FA recovery codes
- Some documents (Passport, Driver license, etc)
- Emergency contacts
- Administrative contacts (Bank, Insurance, etc)
- It uses castor as a build tool
- with some PHP tools, like twig to render the HTML
- It uses staticrypt to encrypt the HTML page with a password
The demo is partially deployed on github pages: https://lyrixx.github.io/private-stuff/
Even if the page is encrypted, I don't want to deploy real data there. So I just put some dummy data.
The real page is deployed somewhere else 👀 ... On cloudflare pages/worker, with another password protection.
Cloudflare allows to deploy static HTML, and also workers. Workers are a way to run some code at edge (on Cloudflare infrastructure). So I can deploy the HTML page and add another security layer at the HTTP level.
I followed this great post to setup the password protection.
Note
This part is optional. If you don't want to use cloudflare, you can just use
the artifacts generated in dist/public and deploy them on any static
hosting.
castor deploy needs wrangler to be authenticated. Either run
node_modules/.bin/wrangler login once (OAuth, stored in ~/.config/.wrangler/),
or put a CLOUDFLARE_API_TOKEN in .env.prod.local, plus a
CLOUDFLARE_ACCOUNT_ID if the token has access to several accounts.
castor deploy always deploys to the production branch of the Pages project
(CFP_PRODUCTION_BRANCH, main by default), whatever your local git branch,
and then checks that the site asks for a password. Pages preview deployments
(any other branch) do not receive the production secrets: CFP_PASSWORD would
be empty there and the middleware would let everyone in. Never create one with
real data, and consider enabling the Access policy for preview deployments in
the settings of the Pages project.
castor cloudflare:clean-deployments lists the deployments that are not in
production. Add --delete to delete them: only the previews of another branch
than CFP_PRODUCTION_BRANCH are deleted, never the active production deployment
nor the other deployments of this branch. A CLOUDFLARE_API_TOKEN needs the
Cloudflare Pages: Edit permission.
For development:
The project has two modes, selected by the APP_ENV variable:
dev(the default): dummy data, default passwords, deploy is forbidden. This is what a fresh clone and the GitHub Pages workflow use. Nothing to configure.prod: your own data fromdata/and your own passwords.
To work in prod mode locally:
-
create
.env.localwith a single line:APP_ENV=prod -
create
.env.prod.localwith your secrets (copy the keys from.env)- set really strong passwords
CFP_are not needed if you don't plan to deploy to Cloudflare Page
Both files are gitignored, and
.env.prod.localis only loaded inprodmode, so your secrets never leak into adevbuild. -
copy
data/recovery_codes.yaml.disttodata/recovery_codes.yamland fill it with your data -
do the same with
data/administrative_contacts.yaml.distandemergency_contacts.yaml.dist -
run
castor build --no-open -
deploy
dist/public/directory somewhere on the internet
To temporarily switch mode for a single command, set the variable on the command
line, e.g. APP_ENV=dev castor build to check the templates with dummy data.
>[!NOTE]
> If plan to use cloudflare, just use `castor deploy`
If you want to play with the stack locally, you'll need to resolve
private-stuff.test to local host:
echo "127.0.0.1 private-stuff.test" | sudo tee -a /etc/hosts
then run
castor build
It will, if needed:
- install JS vendor
- build all static content
- create new SSL certificats
- create docker image
- start a docker container
- open in your favorite browser the project
You can also run the castor command to see all others available tasks.
Some private stuff is too sensitive for the website (main password, bank accounts, etc.), but must be reachable if something really bad happens. It goes in a document sealed with Shamir's secret sharing: the document is encrypted, and its key is split between N people I trust. Any K of them can open it, fewer cannot.
The sealing is done by ReMemory, that
castor downloads in var/rememory/ and checks against a pinned checksum.
- write the document in Markdown in
data/shamir.md(seedata/shamir.md.dist) - list the holders, the threshold K and the language of the bundles in
data/shamir.yaml(seedata/shamir.yaml.dist) - check the document with
castor shamir:preview - seal it with
castor shamir:seal - send each bundle of
var/shamir/output/bundles/to its holder, then deletevar/shamir/: together, the bundles open the document
A holder opens the recover.html page of their bundle in any browser, offline,
nothing to install, and adds the bundles of the other holders until the
document opens.
Sealing again creates a new key: the previous bundles cannot open the new document, send the new bundles to every holder.
This repository is under the MIT license. See the complete license in the LICENSE file.