0b73d493d8
- Fix Caddy cfProxy helper for cloudflared http:// vhosts (X-Forwarded-Proto) - Fix Authelia LDAP bind (readonly user ACL + password sync) - Add gitea-admin-setup oneshot service to survive rebuilds - Update Authelia forward_auth with header_up X-Forwarded-Proto https - Update TODO.org with completed tasks and LDAP config details - Remove old Helm/k8s artifacts (Chart.yaml, templates/, values/, scripts) - Add result to .gitignore Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
301 lines
7.7 KiB
Org Mode
301 lines
7.7 KiB
Org Mode
#+title: Homey
|
|
|
|
A home environment for everyone!
|
|
|
|
* NixOS Deployment (active branch: nixos-port)
|
|
|
|
** Prerequisites
|
|
|
|
Before building, make sure the following are set in the repo:
|
|
|
|
- =hosts/pi-main/default.nix= — SSH public key, static IP, WiFi SSID
|
|
- =secrets/secrets.yaml= — all secrets populated and sops-encrypted
|
|
- WiFi password secret formatted as =wifi_psk=YourPassword= (see below)
|
|
|
|
** Adding / updating secrets
|
|
|
|
#+begin_src bash
|
|
sops secrets/secrets.yaml
|
|
#+end_src
|
|
|
|
Opens your editor with the decrypted file. Save and quit to re-encrypt.
|
|
|
|
The WiFi password entry must use the =wifi_psk== prefix so wpa_supplicant
|
|
can look up the value by name:
|
|
|
|
#+begin_src yaml
|
|
wifi/psk: "wifi_psk=YourActualWifiPassword"
|
|
#+end_src
|
|
|
|
** Phase 1 — Bootstrap image (flash this first)
|
|
|
|
The full =pi-main= config requires sops secrets, which require an age key
|
|
on the Pi — but the age key doesn't exist until after first boot. To
|
|
break the chicken-and-egg problem, flash a minimal bootstrap image first.
|
|
|
|
Before building, fill in the WiFi password in =flake.nix= in the
|
|
=pi-main-bootstrap= config (search for =WIFI_PASSWORD_HERE=):
|
|
|
|
#+begin_src nix
|
|
networks."Zakobar".psk = "your-actual-wifi-password";
|
|
#+end_src
|
|
|
|
Build the bootstrap SD image (requires =aarch64-linux= build capability —
|
|
either =boot.binfmt.emulatedSystems = ["aarch64-linux"]= on your
|
|
workstation, or an aarch64 remote builder):
|
|
|
|
#+begin_src bash
|
|
nix build .#nixosConfigurations.pi-main-bootstrap.config.system.build.sdImage \
|
|
--system aarch64-linux
|
|
#+end_src
|
|
|
|
Find your SD card device, then flash (double-check =/dev/sdX=!):
|
|
|
|
#+begin_src bash
|
|
lsblk
|
|
|
|
zstdcat result/sd-image/nixos-sd-image-*.img.zst | \
|
|
sudo dd of=/dev/sdX bs=4M status=progress conv=fsync
|
|
#+end_src
|
|
|
|
The Pi will boot at =192.168.1.100=, connect to =Zakobar= WiFi, and accept
|
|
SSH connections with your key. No services run yet.
|
|
|
|
** Phase 2 — Generate age key and re-encrypt secrets
|
|
|
|
#+begin_src bash
|
|
# SSH into the Pi
|
|
ssh admin@192.168.1.100
|
|
|
|
# Generate the age key
|
|
sudo age-keygen -o /var/lib/sops-nix/key.txt
|
|
|
|
# Print the public key — copy it
|
|
sudo age-keygen -y /var/lib/sops-nix/key.txt
|
|
#+end_src
|
|
|
|
Back on your workstation, add the public key to =secrets/.sops.yaml=
|
|
alongside the existing PGP key:
|
|
|
|
#+begin_src yaml
|
|
keys:
|
|
- &pi_main age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
creation_rules:
|
|
- path_regex: secrets/secrets.yaml$
|
|
key_groups:
|
|
- pgp:
|
|
- 076AA297579A0064
|
|
age:
|
|
- *pi_main
|
|
#+end_src
|
|
|
|
Then re-encrypt so the Pi can decrypt its own secrets:
|
|
|
|
#+begin_src bash
|
|
sops updatekeys secrets/secrets.yaml
|
|
#+end_src
|
|
|
|
** Phase 3 — Deploy the full config
|
|
|
|
#+begin_src bash
|
|
nixos-rebuild switch \
|
|
--flake .#pi-main \
|
|
--target-host admin@192.168.1.100 \
|
|
--build-host admin@192.168.1.100 \
|
|
--use-remote-sudo
|
|
#+end_src
|
|
|
|
The Pi builds its own config natively (no cross-compilation). sops-nix
|
|
will now decrypt all secrets and start all services.
|
|
|
|
** Caddy plugin hash
|
|
|
|
The first deploy will fail at the Caddy build step because =lib.fakeHash=
|
|
is a placeholder. Copy the correct hash from the error output and replace
|
|
it in =modules/caddy.nix=:
|
|
|
|
#+begin_src nix
|
|
caddyWithCloudflare = pkgs.caddy.withPlugins {
|
|
plugins = [ "github.com/caddy-dns/cloudflare@..." ];
|
|
hash = "sha256-REPLACE_WITH_REAL_HASH="; # ← paste here
|
|
};
|
|
#+end_src
|
|
|
|
Then re-run the deploy command from Phase 3.
|
|
|
|
** Ongoing deploys from workstation
|
|
|
|
All future config changes follow the same pattern:
|
|
|
|
1. Edit files on workstation
|
|
2. Run:
|
|
|
|
#+begin_src bash
|
|
nixos-rebuild switch \
|
|
--flake .#pi-main \
|
|
--target-host admin@192.168.1.100 \
|
|
--build-host admin@192.168.1.100 \
|
|
--use-remote-sudo
|
|
#+end_src
|
|
|
|
NixOS activates the new config on the Pi immediately, with an automatic
|
|
rollback if activation fails.
|
|
|
|
* Installation (legacy Helm)
|
|
|
|
Install using
|
|
|
|
#+begin_src bash
|
|
helm upgrade --install homey . -n homey
|
|
#+end_src
|
|
|
|
* Backing up
|
|
|
|
Backups use [[https://restic.net/][restic]] and run automatically via systemd on a daily schedule.
|
|
|
|
** Strategy — two tiers
|
|
|
|
1. *Primary (automatic)*: Daily backup to an S3-compatible bucket (Backblaze B2,
|
|
Wasabi, AWS S3, etc.). Restic deduplicates and encrypts before upload.
|
|
Retention: 7 daily, 4 weekly, 6 monthly snapshots.
|
|
|
|
2. *Offload (manual)*: Run =scripts/offload-backup.sh --target /path/to/disk=
|
|
to clone snapshots from the S3 repo onto a local disk (USB plugged into the
|
|
Pi, or a disk on your workstation). Uses =restic copy= so deduplication is
|
|
preserved on the target.
|
|
|
|
** What is backed up
|
|
|
|
All service data under =/mnt/data/=:
|
|
|
|
- =openldap/= — LDAP database and config
|
|
- =authelia/= — Authelia config and state
|
|
- =gitea/= — Gitea repositories and data
|
|
- =nextcloud/= — Nextcloud files + a =pg_dump= of the database
|
|
- =jellyfin/= — Jellyfin metadata (media files are excluded — re-downloadable)
|
|
- =transmission/= — Torrent client config
|
|
|
|
Nextcloud is placed into maintenance mode and postgres is =pg_dump='d before
|
|
each backup to ensure a consistent snapshot.
|
|
|
|
** Configuration
|
|
|
|
Repository URL and credentials are set per-host:
|
|
|
|
#+begin_src nix
|
|
# hosts/pi-main/default.nix
|
|
homey.backup.repository = "s3:https://s3.us-west-002.backblazeb2.com/your-bucket";
|
|
#+end_src
|
|
|
|
S3 credentials live in =secrets/secrets.yaml= as =restic/s3_access_key_id= and
|
|
=restic/s3_secret_access_key=.
|
|
|
|
** Restore
|
|
|
|
#+begin_src bash
|
|
# List snapshots
|
|
restic -r s3:https://... snapshots
|
|
|
|
# Restore latest snapshot to /mnt/data
|
|
restic -r s3:https://... restore latest --target /mnt/data
|
|
|
|
# Restore a single service
|
|
restic -r s3:https://... restore latest --target /mnt/data --include /mnt/data/gitea
|
|
#+end_src
|
|
|
|
* LDAP Configuration
|
|
|
|
Logins are done to PHPLDAPADMIN
|
|
|
|
DN is like:
|
|
|
|
cn=admin,dc=,dc=io
|
|
get-secret-val.sh homey openldap-admin password
|
|
|
|
First thing we do is create an organization unit called users
|
|
|
|
To add a new user, we create a child entry to ou=users
|
|
|
|
It has to be of type inetOrgPerson
|
|
|
|
cn = Common Name, sn = Sur Name.
|
|
Select RDN = User Name (uid) (FROM DROP DOWN MENU)
|
|
UID = USERNAME, that is what is important. (In PHPLdapAdmin it is under User Name)
|
|
|
|
Now we may continue!
|
|
|
|
* GITEA
|
|
|
|
Site Title: whatever
|
|
|
|
SSH Server Domain: git.<YOUR URL>
|
|
SSH Server Port: 2222
|
|
Gitea Base URL: http://git.<YOUR URL>
|
|
|
|
Then add Administrator Account Settings:
|
|
|
|
Administrator Username: gitea-admin
|
|
Password: from gitea-admin-pass
|
|
Email address must be populated
|
|
|
|
That will work after a few minutes.
|
|
|
|
Now we go into Authentication Sources
|
|
|
|
Add a new LDAP Authentication source
|
|
|
|
Authentication name: Home LDAP
|
|
Host: openldap
|
|
Port: 389
|
|
Bind DN = cn=readonly,dc=,dc=io
|
|
Bind Password: openldap-ro password
|
|
User Search Base: ou=users,dc=,dc=io
|
|
user search filter = (uid=%s)
|
|
Admin filter (title=admin)
|
|
Username Attribute: uid
|
|
First Name Attribute: cn
|
|
Surname Attribute: sn
|
|
Email Attribute: mail
|
|
|
|
* AUTHELIA
|
|
|
|
https://github.com/authelia/authelia/blob/57d5fbd3f5c82e83296023dc1de6e4f5ff063c00/examples/compose/lite/authelia/configuration.yml
|
|
This fucking sucks
|
|
https://gist.github.com/james-d-elliott/5152d27c0781aee856a3383f1284998e
|
|
|
|
* EVERYTHING
|
|
https://www.talkingquickly.co.uk/gitea-sso-with-keycloak-openldap-openid-connect
|
|
|
|
* DRONE AND GITEA
|
|
?
|
|
https://dev.to/ruanbekker/self-hosted-cicd-with-gitea-and-drone-ci-200l
|
|
|
|
* DAV
|
|
|
|
https://gitlab.com/davical-project/davical/-/blob/master/config/example-config.php
|
|
|
|
Line 800 ish for auth from reverse proxy
|
|
|
|
* NEXTCLOUD
|
|
|
|
I ran THIS command inside
|
|
su www-data -s /bin/bash -c php occ ldap:promote-group "admins"
|
|
|
|
** When maintenence mode
|
|
|
|
#+begin_example
|
|
kubectl exec --tty --stdin -n homey deploy/nextcloud -- su -l www-data -s /bin/bash
|
|
php /var/www/html/occ maintenance:mode --off
|
|
#+end_src
|
|
|
|
* I UNDERSTAND
|
|
|
|
I need to backup Chen's stuff
|
|
And... I need to Jellyfin
|
|
|
|
* PAPERLESS
|
|
|
|
https://github.com/paperless-ngx/paperless-ngx/blob/74c44fe418a91a526b5dab1a91fde4aaebd28bb1/docker/compose/docker-compose.postgres.yml
|
|
|
|
For docker
|