/home/

it ain't much, but it's honest work

Recently I thought it was a good idea to purchase the itshonest.work domain to do the funniest thing ever (maybe), nothing too interesting, but I learned a couple of things I want to write down. I suggest you give the repo a check before moving on tho, if you are too lazy here is a TLDR for the "architecture":

you can just cache things

By default R2 files are not cached, as you can see by consulting the cf-cache-status:

$: curl -s -o /dev/null -D - https://aintmuch.but.itshonest.work/abiw.png | grep "cache"
cf-cache-status: DYNAMIC

DYNAMIC means that abiw.png was requested from the origin web server, but, what's a Cloudflare without a caching? Let's fix that by creating a caching rule:

{
  "expression": "(http.host eq \"aintmuch.but.itshonest.work\")",
  "description": "cache everything forever",
  "action": "set_cache_settings",
  "action_parameters": {
    "cache": true,
    "edge_ttl": {"mode": "override_origin","default":31536000},
    "browser_ttl": {"mode":"override_origin","default":31536000}
  }
}

So what? HITting cache now? you bet:

$: curl -s -o /dev/null -D - https://aintmuch.but.itshonest.work | grep "cache"
cache-control: max-age=31536000
cf-cache-status: HIT

In order to add a rule, we have to create a ruleset first, check abiw.sh.

you can just store secrets in the repo

Apparently I am doing nix now, and with this comes a nice approach to secret management.

The flake pins two inputs beyond nixpkgs:

The devShell provides age, agenix, jq, and liquidprompt (defintely unnecessary, but I like it). The shellHook sources liquidprompt and then runs the agenix-shell installation script, which decrypts three secrets (CLOUDFLARE_API_TOKEN, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY). This is super handy for development as it means i can just copy a command from the script and run it without having to worry about manually setting them.

Decryption uses a private SSH key located at /tmp/rc-ssh-key I extract from my Bitwarden vault with:

bw get item $(bw list items --search "rc-ssh-key" | jq -r '.[].id') | jq -r '.sshKey.privateKey' > /tmp/rc-ssh-key

The matching public key is fetched at eval time from https://git.rcast.dev/rc.keys inside secrets.nix, which declares which public keys can decrypt each secret file.

Each .age file is the encrypted ciphertext of the corresponding secret, this is safe to commit, because they can only be decrypted by whoever holds the matching private key (aka me).

To set or rotate a secret I run, e.g.:

agenix -e CLOUDFLARE_API_TOKEN.age

This is quickly becoming my favourite approach to secret management, if you don't like nix you can use jdx/fnox, perhaps in combination with jdx/mise, to achieve something similar.

you can just upload to S3 with curl

Uploading a file to S3 is easy with curl:

curl -X PUT "https://$ACCOUNT_ID.r2.cloudflarestorage.com/$BUCKET_NAME/abiw.png" \
  --aws-sigv4 "aws:amz:auto:s3" \
  --user "$AWS_ACCESS_KEY_ID:$AWS_SECRET_ACCESS_KEY" \
  --upload-file abiw.png \
  -H "Content-Type: image/png"

Yup, I hear you, yet another curl W, hard to disagree.

conclusions

This setup is essentially free to run, as Cloudflare free tier includes:

P.S: I am not a paid shill, I do this for free :P

It ain't much, but it's honest work, someone may say :)