Changes to rpi setup
This commit is contained in:
@@ -19,7 +19,7 @@ modules/
|
||||
storage.nix # External HD mount + per-service directory layout
|
||||
caddy.nix # Caddy reverse proxy (DNS-01 ACME, forward_auth)
|
||||
cloudflared.nix # Cloudflare Tunnel for remote access
|
||||
backup.nix # Restic daily backups
|
||||
backup.nix # Restic daily backups (S3 primary + manual offload)
|
||||
services/
|
||||
openldap.nix # OpenLDAP — central identity provider
|
||||
authelia.nix # Authelia — SSO gateway
|
||||
@@ -226,19 +226,54 @@ production-ready:
|
||||
- [ ] **`hosts/pi-main/default.nix` — fill in real values**:
|
||||
- SSH public key in `users.users.admin.openssh.authorizedKeys.keys`
|
||||
- External HD device path in `homey.storage.device`
|
||||
- Backup repository URL in `homey.backup.repository`
|
||||
- Backup repository URL in `homey.backup.repository` — must be an S3-compatible
|
||||
URL, e.g. `"s3:https://s3.us-west-002.backblazeb2.com/your-bucket-name"`
|
||||
|
||||
- [ ] **`secrets/secrets.yaml` — populate and encrypt**: Fill in all secret
|
||||
values (old passwords from k8s + freshly generated ones), then run
|
||||
values (old passwords from k8s + freshly generated ones, including
|
||||
`restic/s3_access_key_id` and `restic/s3_secret_access_key`), then run
|
||||
`sops --encrypt --in-place secrets/secrets.yaml` before committing.
|
||||
|
||||
- [ ] **`secrets/.sops.yaml` — add real age keys**: Replace both
|
||||
`AGE-PUBLIC-KEY-*` placeholders with actual public keys (workstation + Pi).
|
||||
- [x] **`secrets/.sops.yaml` — PGP key**: The encryption subkey
|
||||
`076AA297579A0064` is already in `.sops.yaml`.
|
||||
|
||||
- [ ] **Cloudflare Tunnel**: Create the tunnel in the Zero Trust dashboard,
|
||||
copy the tunnel token into secrets, and configure public hostnames. See
|
||||
`modules/cloudflared.nix` and Phase 3 of `PORTING.md` for details.
|
||||
|
||||
- [ ] **Second machine**: When ready, add `hosts/pi-secondary/` and uncomment
|
||||
the `pi-secondary` entry in `flake.nix`. Services communicating cross-machine
|
||||
should reference the primary Pi's LAN IP instead of `127.0.0.1`.
|
||||
|
||||
- [ ] **Jellyfin and Transmission**: Both modules are written and importable
|
||||
but disabled. Enable in `hosts/pi-main/default.nix` when ready:
|
||||
```nix
|
||||
homey.jellyfin.enable = true;
|
||||
homey.transmission.enable = true;
|
||||
```
|
||||
|
||||
- [ ] **Backup — S3 credentials**: Add `restic/s3_access_key_id` and
|
||||
`restic/s3_secret_access_key` to secrets, and set `homey.backup.repository`
|
||||
to your S3-compatible bucket URL in `hosts/pi-main/default.nix`.
|
||||
|
||||
- [ ] **Backup — offload script**: Write `scripts/offload-backup.sh` for
|
||||
manually copying snapshots to a local disk (USB attached to Pi, or a disk
|
||||
on your workstation). Uses `restic copy` to clone from the S3 repo into a
|
||||
local restic repo on the target path. See `TODO.org` for design notes.
|
||||
|
||||
### Post- Pi first boot
|
||||
|
||||
These items require the Pi to be built, flashed, and booted at least once.
|
||||
|
||||
- [ ] **`secrets/.sops.yaml` — add Pi age key**: After generating the age key
|
||||
on the Pi (`age-keygen -o /var/lib/sops-nix/key.txt`), add the public key
|
||||
to `.sops.yaml` alongside the existing PGP key, then run
|
||||
`sops updatekeys secrets/secrets.yaml`.
|
||||
|
||||
- [ ] **`hosts/pi-main/hardware.nix` — verify SD card labels**: The file
|
||||
assumes partition labels `NIXOS_SD` (root) and `FIRMWARE` (boot). Relabel
|
||||
after flashing if they differ, or update the `fileSystems` entries.
|
||||
|
||||
- [ ] **Gitea LDAP auth**: After first start, configure LDAP authentication
|
||||
in Gitea's admin panel (Admin → Authentication Sources → Add LDAP source).
|
||||
The old Helm chart had this commented out; it must be done manually once.
|
||||
@@ -250,18 +285,3 @@ production-ready:
|
||||
- [ ] **Nextcloud LDAP app**: After restoring the Nextcloud volume, verify
|
||||
the LDAP Users and Contacts app is still configured correctly
|
||||
(Admin → LDAP/AD Integration).
|
||||
|
||||
- [ ] **`hosts/pi-main/hardware.nix` — verify SD card labels**: The file
|
||||
assumes partition labels `NIXOS_SD` (root) and `FIRMWARE` (boot). Relabel
|
||||
after flashing if they differ, or update the `fileSystems` entries.
|
||||
|
||||
- [ ] **Second machine**: When ready, add `hosts/pi-secondary/` and uncomment
|
||||
the `pi-secondary` entry in `flake.nix`. Services communicating cross-machine
|
||||
should reference the primary Pi's LAN IP instead of `127.0.0.1`.
|
||||
|
||||
- [ ] **Jellyfin and Transmission**: Both modules are written and importable
|
||||
but disabled. Enable in `hosts/pi-main/default.nix` when ready:
|
||||
```nix
|
||||
homey.jellyfin.enable = true;
|
||||
homey.transmission.enable = true;
|
||||
```
|
||||
|
||||
@@ -1,8 +1,20 @@
|
||||
{
|
||||
description = "Homey - self-hosted home server NixOS configuration";
|
||||
|
||||
# Binary cache for pre-built Raspberry Pi kernel + firmware packages.
|
||||
# nixos-raspberrypi builds against its own pinned nixpkgs and publishes
|
||||
# to this cache — using it avoids compiling linuxPackages_rpi4 from source.
|
||||
nixConfig = {
|
||||
extra-substituters = [
|
||||
"https://nixos-raspberrypi.cachix.org"
|
||||
];
|
||||
extra-trusted-public-keys = [
|
||||
"nixos-raspberrypi.cachix.org-1:4iMO9LXa8BqhU+Rpg6LQKiGa2lsNh/j2oiYLNOQ5sPI="
|
||||
];
|
||||
};
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05";
|
||||
|
||||
# sops-nix for secret management
|
||||
sops-nix = {
|
||||
@@ -10,17 +22,18 @@
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
# Caddy with Cloudflare DNS plugin (not in nixpkgs mainline)
|
||||
caddy-cloudflare = {
|
||||
url = "github:NixOS/nixpkgs/nixos-24.11"; # see modules/caddy.nix for override
|
||||
};
|
||||
# Raspberry Pi hardware support — provides vendor kernel, firmware,
|
||||
# bootloader management, and a binary cache for pre-built aarch64 packages.
|
||||
# Intentionally NOT following our nixpkgs: the cache is built against the
|
||||
# flake's own pinned nixpkgs, so following would invalidate all cache hits.
|
||||
nixos-raspberrypi.url = "github:nvmd/nixos-raspberrypi/main";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, sops-nix, ... }@inputs:
|
||||
outputs = { self, nixpkgs, sops-nix, nixos-raspberrypi, ... }@inputs:
|
||||
let
|
||||
# Shared specialArgs passed to every host
|
||||
commonArgs = {
|
||||
inherit inputs;
|
||||
inherit inputs nixos-raspberrypi;
|
||||
# Top-level site config — override per-host if needed
|
||||
homeyConfig = {
|
||||
domain = "home.zakobar.com"; # base domain for all services
|
||||
@@ -31,12 +44,24 @@
|
||||
};
|
||||
};
|
||||
|
||||
mkHost = { system, hostPath, extraModules ? [] }:
|
||||
nixpkgs.lib.nixosSystem {
|
||||
inherit system;
|
||||
# nixos-raspberrypi.lib.nixosSystem is a drop-in replacement for
|
||||
# nixpkgs.lib.nixosSystem that:
|
||||
# - injects vendor kernel/firmware overlays
|
||||
# - wires up the trusted cache substituters
|
||||
# - passes nixos-raspberrypi into specialArgs automatically
|
||||
# It uses the flake's own pinned nixpkgs by default (currently 25.11).
|
||||
mkHost = { hostPath, extraModules ? [] }:
|
||||
nixos-raspberrypi.lib.nixosSystem {
|
||||
specialArgs = commonArgs;
|
||||
modules = [
|
||||
sops-nix.nixosModules.sops
|
||||
# RPi 4 base: vendor kernel (linuxPackages_rpi4), firmware,
|
||||
# bootloader (u-boot), initrd modules, config.txt management
|
||||
nixos-raspberrypi.nixosModules.raspberry-pi-4.base
|
||||
# SD image target — provides system.build.sdImage
|
||||
({ modulesPath, ... }: {
|
||||
imports = [ "${modulesPath}/installer/sd-card/sd-image-aarch64.nix" ];
|
||||
})
|
||||
hostPath
|
||||
./modules/common.nix
|
||||
./modules/storage.nix
|
||||
@@ -56,15 +81,72 @@
|
||||
in {
|
||||
nixosConfigurations = {
|
||||
|
||||
# Bootstrap image — flash this first.
|
||||
# Minimal: SSH key, WiFi, static IP. No sops, no services.
|
||||
# Purpose: boot the Pi, generate the age key, then deploy pi-main.
|
||||
pi-main-bootstrap = nixos-raspberrypi.lib.nixosSystem {
|
||||
specialArgs = commonArgs;
|
||||
modules = [
|
||||
nixos-raspberrypi.nixosModules.raspberry-pi-4.base
|
||||
({ modulesPath, ... }: {
|
||||
imports = [ "${modulesPath}/installer/sd-card/sd-image-aarch64.nix" ];
|
||||
})
|
||||
./hosts/pi-main/hardware.nix
|
||||
({ pkgs, lib, ... }: {
|
||||
networking.hostName = "pi-main";
|
||||
time.timeZone = commonArgs.homeyConfig.timezone;
|
||||
i18n.defaultLocale = "en_US.UTF-8";
|
||||
system.stateVersion = "25.05";
|
||||
|
||||
nix.settings.experimental-features = [ "nix-command" "flakes" ];
|
||||
nixpkgs.config.allowUnfree = true;
|
||||
|
||||
# WiFi — PSK inline (bootstrap only, not in Nix store long-term)
|
||||
networking.wireless = {
|
||||
enable = true;
|
||||
networks."Zakobar".psk = "0502711157";
|
||||
};
|
||||
networking.interfaces.wlan0.ipv4.addresses = [{
|
||||
address = "192.168.1.100";
|
||||
prefixLength = 24;
|
||||
}];
|
||||
networking.useDHCP = false;
|
||||
networking.interfaces.wlan0.useDHCP = false;
|
||||
networking.defaultGateway = "192.168.1.1";
|
||||
networking.nameservers = [ "1.1.1.1" "8.8.8.8" ];
|
||||
networking.firewall.allowedTCPPorts = [ 22 ];
|
||||
|
||||
# SSH — key only, no passwords, no root
|
||||
services.openssh = {
|
||||
enable = true;
|
||||
settings = {
|
||||
PasswordAuthentication = false;
|
||||
PermitRootLogin = "no";
|
||||
};
|
||||
};
|
||||
|
||||
users.mutableUsers = false;
|
||||
users.users.admin = {
|
||||
isNormalUser = true;
|
||||
extraGroups = [ "wheel" ];
|
||||
openssh.authorizedKeys.keys = [
|
||||
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDBFZRqiTsOCAJPMqUyMeLd2MbyjdGoyqDVq5/Inhb6EOaM1NUGG4b6FPmYgFLyJIm5LC9BOo6M7npiaiOs/zMqp+hoGLNQUNwm5/G0uy1bjkEfKdUTdGnJ2+M9rkxrR1c+KXrjkiqECqTbnPE4mJbGyVxBW2MwMeP5w8c0DB5KO528PetvHMPPQuEdXyZzDI4kKtVpMlJoPIrIGlNFX0G/wrgXcM4zU1snOTuYGqZnWW++4kBsgIlRKpf/bLJyUMTp30eLVr0fQ6OMBtj1tzUUBaaowU6VGYQQDU/rIh/NpkA2cEVPXZegM4OohkAqrJBFPIAg90WD9Z/SyQlz0Jn8PpAloP0Cuq2vVRr+QLEwxqGiFq91YQ2VtwksMHwJGVrXRCNegpxTZQijWMEd+o0FD2cEd7Ftw6v2L6g12GJ3QGX/q0d/u0GongLLa9fPXl4VoAu7AL+cUcbX/SS7RCG8kYAR3DwOazVbK0NWEdwvWdoSU4lZ3j2at1xqMGjHjyLiTeUqZBjm+Sl5MJWIYNg+8hnONljvggg4SzDFDAkgVLZtOCaZibsMA1ucGR7VRCM09uoaEI4/ZS5pCBtYcp8X67Bv67Og8s2NFf5sUfYBPPKpdBSs+dEPycNVff6JlmzfNiyzLawacGKIDWYSgkOl43N/5ehtpsL3HMZ+5SVNIw=="
|
||||
];
|
||||
};
|
||||
security.sudo.wheelNeedsPassword = false;
|
||||
|
||||
environment.systemPackages = [ pkgs.age pkgs.vim ];
|
||||
})
|
||||
];
|
||||
};
|
||||
|
||||
# Primary Raspberry Pi 4
|
||||
pi-main = mkHost {
|
||||
system = "aarch64-linux";
|
||||
hostPath = ./hosts/pi-main/default.nix;
|
||||
};
|
||||
|
||||
# Future second machine (placeholder — uncomment and configure when ready)
|
||||
# pi-secondary = mkHost {
|
||||
# system = "x86_64-linux"; # or aarch64-linux for another Pi
|
||||
# hostPath = ./hosts/pi-secondary/default.nix;
|
||||
# };
|
||||
|
||||
|
||||
@@ -14,6 +14,34 @@
|
||||
# -------------------------------------------------------------------------
|
||||
networking.hostName = "pi-main";
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# WiFi — static IP, always connect to home network
|
||||
# -------------------------------------------------------------------------
|
||||
networking.wireless = {
|
||||
enable = true;
|
||||
# secretsFile is read by wpa_supplicant at runtime; values are literal
|
||||
# (not env vars). The key name after "ext:" must match a line in the file
|
||||
# formatted as: key_name=the-actual-password
|
||||
secretsFile = config.sops.secrets."wifi/psk".path;
|
||||
networks."Zakobar".pskRaw = "ext:wifi_psk";
|
||||
};
|
||||
|
||||
# Static IP on wlan0
|
||||
networking.interfaces.wlan0.ipv4.addresses = [{
|
||||
address = "192.168.1.100";
|
||||
prefixLength = 24;
|
||||
}];
|
||||
networking.defaultGateway = "192.168.1.1";
|
||||
networking.nameservers = [ "1.1.1.1" "8.8.8.8" ];
|
||||
|
||||
# Disable DHCP on wlan0 — we're using a static address
|
||||
networking.useDHCP = false;
|
||||
networking.interfaces.wlan0.useDHCP = false;
|
||||
|
||||
# The secret file must contain exactly one line: wifi_psk=<your-password>
|
||||
# Add it with: sops secrets/secrets.yaml → wifi/psk: "wifi_psk=YourPassword"
|
||||
sops.secrets."wifi/psk" = { owner = "root"; mode = "0400"; };
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Admin user
|
||||
# -------------------------------------------------------------------------
|
||||
@@ -22,7 +50,7 @@
|
||||
extraGroups = [ "wheel" "podman" ];
|
||||
# Paste your SSH public key here
|
||||
openssh.authorizedKeys.keys = [
|
||||
"ssh-ed25519 AAAA... your-key-here"
|
||||
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDBFZRqiTsOCAJPMqUyMeLd2MbyjdGoyqDVq5/Inhb6EOaM1NUGG4b6FPmYgFLyJIm5LC9BOo6M7npiaiOs/zMqp+hoGLNQUNwm5/G0uy1bjkEfKdUTdGnJ2+M9rkxrR1c+KXrjkiqECqTbnPE4mJbGyVxBW2MwMeP5w8c0DB5KO528PetvHMPPQuEdXyZzDI4kKtVpMlJoPIrIGlNFX0G/wrgXcM4zU1snOTuYGqZnWW++4kBsgIlRKpf/bLJyUMTp30eLVr0fQ6OMBtj1tzUUBaaowU6VGYQQDU/rIh/NpkA2cEVPXZegM4OohkAqrJBFPIAg90WD9Z/SyQlz0Jn8PpAloP0Cuq2vVRr+QLEwxqGiFq91YQ2VtwksMHwJGVrXRCNegpxTZQijWMEd+o0FD2cEd7Ftw6v2L6g12GJ3QGX/q0d/u0GongLLa9fPXl4VoAu7AL+cUcbX/SS7RCG8kYAR3DwOazVbK0NWEdwvWdoSU4lZ3j2at1xqMGjHjyLiTeUqZBjm+Sl5MJWIYNg+8hnONljvggg4SzDFDAkgVLZtOCaZibsMA1ucGR7VRCM09uoaEI4/ZS5pCBtYcp8X67Bv67Og8s2NFf5sUfYBPPKpdBSs+dEPycNVff6JlmzfNiyzLawacGKIDWYSgkOl43N/5ehtpsL3HMZ+5SVNIw=="
|
||||
];
|
||||
};
|
||||
|
||||
@@ -34,7 +62,7 @@
|
||||
homey.storage = {
|
||||
# Replace with the actual by-id path of your USB drive.
|
||||
# Find it: ls -la /dev/disk/by-id/ | grep -v part
|
||||
device = "/dev/disk/by-id/REPLACE-WITH-YOUR-DRIVE-ID";
|
||||
device = "/dev/disk/by-id/usb-WD_Ext_HDD_1021_5743415A4146313531393031-0:0-part1";
|
||||
mountPoint = "/mnt/data";
|
||||
fsType = "ext4";
|
||||
};
|
||||
@@ -66,7 +94,7 @@
|
||||
# "sftp:user@nas.local:/backups/homey"
|
||||
# "b2:your-bucket-name:homey"
|
||||
# "rclone:remote:homey"
|
||||
homey.backup.repository = "sftp:REPLACE-WITH-BACKUP-DESTINATION";
|
||||
homey.backup.repository = "s3:https://s3.us-east-005.backblazeb2.com/zakobar-home-backup";
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Local DNS overrides (optional — makes LAN clients hit the Pi directly
|
||||
|
||||
+15
-49
@@ -2,56 +2,37 @@
|
||||
|
||||
# Hardware configuration for the primary Raspberry Pi 4 (8 GB).
|
||||
#
|
||||
# SD card layout assumed:
|
||||
# /dev/mmcblk0p1 — /boot/firmware (FAT32, ~256 MB)
|
||||
# /dev/mmcblk0p2 — / (ext4)
|
||||
# nixos-raspberrypi's raspberry-pi-4.base module (imported in flake.nix)
|
||||
# provides everything that nixos-hardware.raspberry-pi-4 previously did:
|
||||
# - linuxPackages_rpi4 vendor kernel + matching firmware
|
||||
# - u-boot bootloader with /boot/firmware partition management
|
||||
# - initrd modules (xhci_pci, usbhid, usb_storage, vc4, pcie_brcmstb, etc.)
|
||||
# - config.txt generation
|
||||
#
|
||||
# This file adds only host-specific overrides on top of that.
|
||||
#
|
||||
# External HD:
|
||||
# Set homey.storage.device to the by-id path of your USB drive.
|
||||
# Example: /dev/disk/by-id/usb-WD_Elements_12345-0:0-part1
|
||||
# Find it with: ls -la /dev/disk/by-id/
|
||||
#
|
||||
# To generate this file fresh after installing NixOS on the Pi, run:
|
||||
# nixos-generate-config --show-hardware-config
|
||||
# and merge the output here.
|
||||
# TODO: Verify SD card partition labels after first flash.
|
||||
# The config assumes labels NIXOS_SD (root) and FIRMWARE (boot).
|
||||
# Check with: lsblk -o NAME,LABEL
|
||||
# Update fileSystems entries below if they differ.
|
||||
|
||||
{
|
||||
imports = [
|
||||
(modulesPath + "/installer/scan/not-detected.nix")
|
||||
];
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Boot loader — Raspberry Pi 4 uses U-Boot / extlinux
|
||||
# -------------------------------------------------------------------------
|
||||
boot = {
|
||||
loader = {
|
||||
grub.enable = false;
|
||||
generic-extlinux-compatible.enable = true;
|
||||
};
|
||||
|
||||
# Pi 4 kernel — use the mainline kernel with RPi patches
|
||||
kernelPackages = pkgs.linuxPackages_rpi4;
|
||||
|
||||
# tmpfs for /tmp — keep the SD card writes down
|
||||
tmp.useTmpfs = true;
|
||||
boot.tmp.useTmpfs = true;
|
||||
|
||||
# Modules needed for USB storage (external HD)
|
||||
initrd.availableKernelModules = [ "xhci_pci" "usbhid" "usb_storage" "uas" ];
|
||||
kernelModules = [];
|
||||
extraModulePackages = [];
|
||||
};
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Filesystems
|
||||
# -------------------------------------------------------------------------
|
||||
fileSystems."/" = {
|
||||
device = "/dev/disk/by-label/NIXOS_SD"; # label the root partition NIXOS_SD when flashing
|
||||
device = "/dev/disk/by-label/NIXOS_SD";
|
||||
fsType = "ext4";
|
||||
options = [ "noatime" ];
|
||||
};
|
||||
|
||||
fileSystems."/boot/firmware" = {
|
||||
device = "/dev/disk/by-label/FIRMWARE"; # FAT32 boot partition
|
||||
device = "/dev/disk/by-label/FIRMWARE";
|
||||
fsType = "vfat";
|
||||
options = [ "fmask=0022" "dmask=0022" ];
|
||||
};
|
||||
@@ -61,24 +42,9 @@
|
||||
|
||||
swapDevices = [];
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Hardware
|
||||
# -------------------------------------------------------------------------
|
||||
hardware = {
|
||||
# Enable the RPi firmware (needed for GPU, WiFi, Bluetooth)
|
||||
raspberry-pi."4".apply-overlays-dtmerge.enable = true;
|
||||
|
||||
# Disable GPU memory split for a headless server (gives more RAM to OS)
|
||||
# Set via config.txt if needed: gpu_mem=16
|
||||
};
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Platform
|
||||
# -------------------------------------------------------------------------
|
||||
nixpkgs.hostPlatform = lib.mkDefault "aarch64-linux";
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Power management
|
||||
# -------------------------------------------------------------------------
|
||||
powerManagement.cpuFreqGovernor = lib.mkDefault "ondemand";
|
||||
}
|
||||
|
||||
+44
-3
@@ -8,11 +8,27 @@
|
||||
# Before a backup, Nextcloud is put into maintenance mode and postgres is
|
||||
# pg_dump'd to a file. This ensures consistent DB backups.
|
||||
#
|
||||
# Backup strategy — two tiers:
|
||||
#
|
||||
# 1. Automatic daily backup to an S3-compatible bucket (primary offsite copy).
|
||||
# Set the repository URL to your bucket in hosts/pi-main/default.nix, e.g.:
|
||||
# homey.backup.repository = "s3:https://s3.us-west-002.backblazeb2.com/your-bucket";
|
||||
# S3 credentials are injected via environment variables from sops secrets:
|
||||
# restic/s3_access_key_id → AWS_ACCESS_KEY_ID
|
||||
# restic/s3_secret_access_key → AWS_SECRET_ACCESS_KEY
|
||||
#
|
||||
# 2. Manual offload to a local disk (USB drive plugged into Pi, or workstation disk).
|
||||
# Use scripts/offload-backup.sh --target /path/to/mounted/disk
|
||||
# That script uses `restic copy` to clone snapshots from the S3 repo into a
|
||||
# local restic repo on the target disk, preserving deduplication.
|
||||
#
|
||||
# Secrets consumed from sops:
|
||||
# restic/password
|
||||
# restic/s3_access_key_id (if using S3 backend)
|
||||
# restic/s3_secret_access_key (if using S3 backend)
|
||||
#
|
||||
# The backup repository URL is set per-host in default.nix:
|
||||
# homey.backup.repository = "sftp:user@nas:/backups/homey";
|
||||
# homey.backup.repository = "s3:https://s3.us-west-002.backblazeb2.com/bucket";
|
||||
#
|
||||
# Restore:
|
||||
# restic -r <repo> restore latest --target /mnt/data
|
||||
@@ -59,6 +75,8 @@ in
|
||||
# Secrets
|
||||
# -----------------------------------------------------------------------
|
||||
sops.secrets."restic/password" = { owner = "root"; };
|
||||
sops.secrets."restic/s3_access_key_id" = { owner = "root"; };
|
||||
sops.secrets."restic/s3_secret_access_key" = { owner = "root"; };
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Pre-backup hook: pg_dump + nextcloud maintenance mode
|
||||
@@ -105,7 +123,9 @@ in
|
||||
services.restic.backups.homey = {
|
||||
repository = cfg.repository;
|
||||
passwordFile = config.sops.secrets."restic/password".path;
|
||||
cacheDir = "${dataDir}/restic-cache";
|
||||
|
||||
# Runtime env file written by ExecStartPre (see systemd override below)
|
||||
environmentFile = "/run/restic-homey-secrets.env";
|
||||
|
||||
paths = [
|
||||
"${dataDir}/openldap"
|
||||
@@ -136,10 +156,31 @@ in
|
||||
];
|
||||
};
|
||||
|
||||
# Wire the pre/post hooks around the restic job
|
||||
# Wire the pre/post hooks around the restic job and inject secrets
|
||||
systemd.services."restic-backups-homey" = {
|
||||
requires = [ "homey-backup-pre.service" ];
|
||||
after = [ "homey-backup-pre.service" ];
|
||||
serviceConfig = {
|
||||
# Write runtime env file with actual secret values (restic needs the
|
||||
# raw values; it does not support _FILE suffix env vars).
|
||||
ExecStartPre = [
|
||||
(pkgs.writeShellScript "restic-inject-secrets" ''
|
||||
install -m 0600 /dev/null /run/restic-homey-secrets.env
|
||||
{
|
||||
printf 'AWS_ACCESS_KEY_ID=%s\n' \
|
||||
"$(cat ${config.sops.secrets."restic/s3_access_key_id".path})"
|
||||
printf 'AWS_SECRET_ACCESS_KEY=%s\n' \
|
||||
"$(cat ${config.sops.secrets."restic/s3_secret_access_key".path})"
|
||||
printf 'RESTIC_CACHE_DIR=%s\n' "${dataDir}/restic-cache"
|
||||
} >> /run/restic-homey-secrets.env
|
||||
'')
|
||||
];
|
||||
ExecStopPost = [
|
||||
(pkgs.writeShellScript "restic-cleanup-secrets" ''
|
||||
rm -f /run/restic-homey-secrets.env
|
||||
'')
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services."homey-backup-post" = {
|
||||
|
||||
+25
-20
@@ -18,16 +18,14 @@ let
|
||||
cfg = config.homey.caddy;
|
||||
domain = homeyConfig.domain;
|
||||
|
||||
# Build Caddy with the Cloudflare DNS plugin.
|
||||
# This compiles on the Pi (slow once, cached after).
|
||||
caddyWithCloudflare = pkgs.caddy.override {
|
||||
externalPlugins = [
|
||||
{
|
||||
name = "github.com/caddy-dns/cloudflare";
|
||||
version = "89f16b99c18ef49c8bb470a82f895bce01cbaece";
|
||||
}
|
||||
# Build Caddy with the Cloudflare DNS plugin using the nixos-25.05 API.
|
||||
# `withPlugins` is a passthru function on the caddy package; it uses xcaddy
|
||||
# under the hood to produce a fixed-output derivation.
|
||||
caddyWithCloudflare = pkgs.caddy.withPlugins {
|
||||
plugins = [
|
||||
"github.com/caddy-dns/cloudflare@v0.2.2-0.20250724223520-f589a18c0f5d"
|
||||
];
|
||||
vendorHash = lib.fakeHash; # replace with real hash after first build
|
||||
hash = "sha256-2Fb2fgM7YhWk9kBnnNGb85MJkAkgzXiI1fb6eK3ykIE=";
|
||||
};
|
||||
|
||||
# Reusable Authelia forward_auth snippet
|
||||
@@ -147,29 +145,36 @@ in
|
||||
"torrent.${domain}" = {
|
||||
extraConfig = ''
|
||||
${autheliaForwardAuth}
|
||||
reverse_proxy localhost:9091_transmission
|
||||
reverse_proxy localhost:9092
|
||||
'';
|
||||
# NOTE: transmission uses 9091 too; we'll bind it to 9092 in its
|
||||
# module to avoid a clash with authelia.
|
||||
# NOTE: transmission is bound to 9092 to avoid clash with authelia on 9091.
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Pass Cloudflare token as env var to the caddy systemd unit
|
||||
# Pass Cloudflare token as env var to the caddy systemd unit.
|
||||
#
|
||||
# The caddy-dns/cloudflare plugin reads CLOUDFLARE_API_TOKEN directly.
|
||||
# sops decrypts the secret to a file at runtime; we write a transient
|
||||
# env file to /run/ in ExecStartPre so systemd picks it up via
|
||||
# EnvironmentFile. The file is removed in ExecStopPost.
|
||||
# -----------------------------------------------------------------------
|
||||
systemd.services.caddy = {
|
||||
serviceConfig = {
|
||||
EnvironmentFile = pkgs.writeText "caddy-cf-env"
|
||||
"CLOUDFLARE_API_TOKEN_FILE=${config.sops.secrets."cloudflare/api_token".path}";
|
||||
# Caddy supports _FILE suffix for env vars via its secret file reader,
|
||||
# but cloudflare plugin reads CLOUDFLARE_API_TOKEN directly.
|
||||
# We write a wrapper ExecStartPre to populate the env var from the file:
|
||||
EnvironmentFile = "/run/caddy-secrets.env";
|
||||
ExecStartPre = [
|
||||
(pkgs.writeShellScript "caddy-inject-cf-token" ''
|
||||
export CLOUDFLARE_API_TOKEN=$(cat ${config.sops.secrets."cloudflare/api_token".path})
|
||||
systemctl set-environment CLOUDFLARE_API_TOKEN="$CLOUDFLARE_API_TOKEN"
|
||||
install -m 0600 /dev/null /run/caddy-secrets.env
|
||||
printf 'CLOUDFLARE_API_TOKEN=%s\n' \
|
||||
"$(cat ${config.sops.secrets."cloudflare/api_token".path})" \
|
||||
> /run/caddy-secrets.env
|
||||
'')
|
||||
];
|
||||
ExecStopPost = [
|
||||
(pkgs.writeShellScript "caddy-cleanup-env" ''
|
||||
rm -f /run/caddy-secrets.env
|
||||
'')
|
||||
];
|
||||
};
|
||||
|
||||
+28
-17
@@ -46,32 +46,43 @@ in
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# cloudflared service
|
||||
# NixOS 24.11 ships services.cloudflared natively.
|
||||
#
|
||||
# We use the token-based tunnel approach (cloudflared tunnel run --token).
|
||||
# This needs no credentials file and no local tunnel config — just the
|
||||
# token from the Cloudflare dashboard.
|
||||
#
|
||||
# Rather than using services.cloudflared.tunnels (which requires a
|
||||
# credentialsFile), we create a plain systemd service that runs cloudflared
|
||||
# directly with the token read from the sops secret.
|
||||
# -----------------------------------------------------------------------
|
||||
services.cloudflared = {
|
||||
enable = true;
|
||||
tunnels = {
|
||||
"pi-main" = {
|
||||
# credentialsFile is not used with token-based auth;
|
||||
# the token is passed via environment variable instead.
|
||||
# We override the systemd unit below to inject it.
|
||||
default = "http_status:404";
|
||||
};
|
||||
};
|
||||
users.users.cloudflared = {
|
||||
isSystemUser = true;
|
||||
group = "cloudflared";
|
||||
description = "cloudflared tunnel daemon";
|
||||
};
|
||||
users.groups.cloudflared = {};
|
||||
|
||||
# Inject the tunnel token from the sops secret file
|
||||
systemd.services."cloudflared-tunnel-pi-main" = {
|
||||
systemd.services."cloudflared-tunnel" = {
|
||||
description = "Cloudflare Tunnel (token-based)";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network-online.target" "caddy.service" ];
|
||||
wants = [ "network-online.target" "caddy.service" ];
|
||||
serviceConfig = {
|
||||
ExecStart = lib.mkForce (pkgs.writeShellScript "cloudflared-start" ''
|
||||
Type = "simple";
|
||||
User = "cloudflared";
|
||||
Group = "cloudflared";
|
||||
Restart = "on-failure";
|
||||
RestartSec = "5s";
|
||||
ExecStart = pkgs.writeShellScript "cloudflared-start" ''
|
||||
exec ${pkgs.cloudflared}/bin/cloudflared tunnel \
|
||||
--no-autoupdate \
|
||||
run \
|
||||
--token "$(cat ${config.sops.secrets."cloudflare/tunnel_token".path})"
|
||||
'');
|
||||
'';
|
||||
# Hardening
|
||||
NoNewPrivileges = true;
|
||||
PrivateTmp = true;
|
||||
};
|
||||
after = lib.mkAfter [ "caddy.service" ];
|
||||
wants = lib.mkAfter [ "caddy.service" ];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
+13
-3
@@ -11,10 +11,20 @@
|
||||
nix = {
|
||||
settings = {
|
||||
experimental-features = [ "nix-command" "flakes" ];
|
||||
# Save disk space on Pi
|
||||
auto-optimise-store = true;
|
||||
# Extra binary caches — speeds up aarch64-linux builds significantly
|
||||
substituters = [
|
||||
"https://cache.nixos.org"
|
||||
"https://nix-community.cachix.org"
|
||||
# Pre-built RPi vendor kernel + firmware (linuxPackages_rpi4, etc.)
|
||||
"https://nixos-raspberrypi.cachix.org"
|
||||
];
|
||||
trusted-public-keys = [
|
||||
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
|
||||
"nix-community.cachix.org-1:mB9FkXj6Q3Q4ohOcbM4FJ9Z1X2kCrVK4vZOqsDqqNqk="
|
||||
"nixos-raspberrypi.cachix.org-1:4iMO9LXa8BqhU+Rpg6LQKiGa2lsNh/j2oiYLNOQ5sPI="
|
||||
];
|
||||
};
|
||||
# Weekly garbage collection — keeps the system from filling the SD card
|
||||
gc = {
|
||||
automatic = true;
|
||||
dates = "weekly";
|
||||
@@ -113,5 +123,5 @@
|
||||
# System state version — do not change after first install
|
||||
# (tracks NixOS backwards-compat markers)
|
||||
# -------------------------------------------------------------------------
|
||||
system.stateVersion = "24.11";
|
||||
system.stateVersion = "25.05";
|
||||
}
|
||||
|
||||
+4
-4
@@ -17,8 +17,8 @@
|
||||
creation_rules:
|
||||
- path_regex: secrets/secrets\.yaml$
|
||||
key_groups:
|
||||
- age:
|
||||
- pgp
|
||||
- 076AA297579A0064
|
||||
# - age:
|
||||
# Pi main host key — replace with output of `age-keygen -y /var/lib/sops-nix/key.txt`
|
||||
- AGE-PUBLIC-KEY-PI-MAIN-REPLACE-ME
|
||||
# (Optional) your workstation key for offline editing:
|
||||
# - AGE-PUBLIC-KEY-YOUR-WORKSTATION
|
||||
# - AGE-PUBLIC-KEY-PI-MAIN-REPLACE-ME
|
||||
|
||||
+55
-51
@@ -1,54 +1,58 @@
|
||||
# =============================================================================
|
||||
# Homey secrets — managed by sops-nix
|
||||
#ENC[AES256_GCM,data:+YPw0Wd8uB5cwY/cszPqLohVhIEbju4QwYVS9vT/ci2MxO7Glw2uJbtS6dOLHqpzfU0b0mJ4IqUIaM08yMEM/TeB7hG1ASHWe3+qxyw8,iv:V49PRq4bEcYvcPMUIvBQkhuq28pVxrlRfVXUSzPxnb4=,tag:OSNXutH5EUwA3BujNZ3FoA==,type:comment]
|
||||
#ENC[AES256_GCM,data:QVC3QP3em1O3SYTAuK4kBchpTiwXH10f2R4YgK+t9QaiqZ1PWvo=,iv:R0lFtvg2T/Rllt1uiriTQvNbSw54jr0otU3E6XsIs00=,tag:9fAQCmuZZPUPLuDY8LZEUA==,type:comment]
|
||||
#
|
||||
# THIS FILE MUST BE ENCRYPTED WITH SOPS BEFORE COMMITTING.
|
||||
# It is shown here as a plaintext template so you know what to fill in.
|
||||
#ENC[AES256_GCM,data:IT6BEo5CjYm+15aeWl+S8M3B+SSmjPnhBRvYToWzezIweTl3MGBXtalvkV3NWkxH0EaHpueOMe6r,iv:7BDTiljEa59F13Pephw6MM+sZgL4jbfQafJyt0UU3hY=,tag:ia+7WUAl/45jrYrv3Pylxg==,type:comment]
|
||||
#
|
||||
# Workflow:
|
||||
# 1. Complete the .sops.yaml age key setup.
|
||||
# 2. Fill in the values below.
|
||||
# 3. Run: sops -e -i secrets/secrets.yaml
|
||||
# This encrypts the file in-place. The encrypted version is safe to commit.
|
||||
# 4. To edit later: sops secrets/secrets.yaml
|
||||
#
|
||||
# Ports from old deployment:
|
||||
# - openldap/admin_password ← from k8s secret openldap-admin
|
||||
# - openldap/config_password ← from k8s secret openldap-config
|
||||
# - openldap/ro_password ← from k8s secret openldap-ro
|
||||
# - gitea/admin_password ← from k8s secret gitea-admin-pass
|
||||
# - nextcloud/admin_password ← from k8s secret nextcloud-admin-pass
|
||||
# - nextcloud/postgres_password← from k8s secret nextcloud-postgres-pass
|
||||
# The remaining secrets (authelia JWT, session key, encryption key, gitea
|
||||
# LFS/OAuth2/internal tokens) are regenerated fresh — see notes below.
|
||||
# =============================================================================
|
||||
#ENC[AES256_GCM,data:zqAQYQCg/TRNtjDIdWTsgtRnQbijjYyLdQIAe9GkTubG9PSj7E8m7HFXmfG4eFNZR4S/Ql0dsM5gvLCu,iv:xSH8LMS7vqe2N9L/TOepKWhuIhVxmKN6kuB1iqUEOUw=,tag:rFYurrqfp1Zxggr5tiPKkQ==,type:comment]
|
||||
#ENC[AES256_GCM,data:+YPw0Wd8uB5cwY/cszPqLohVhIEbju4QwYVS9vT/ci2MxO7Glw2uJbtS6dOLHqpzfU0b0mJ4IqUIaM08yMEM/TeB7hG1ASHWe3+qxyw8,iv:V49PRq4bEcYvcPMUIvBQkhuq28pVxrlRfVXUSzPxnb4=,tag:OSNXutH5EUwA3BujNZ3FoA==,type:comment]
|
||||
#ENC[AES256_GCM,data:yj4R8Yetc6EHWvQDu2/eaoY=,iv:Zbqfg9NRHy6ab10kxzq6qsLb7VHfLxhcpP3vUt2i4ns=,tag:udBGjJUupeADD78JQ8BwuQ==,type:comment]
|
||||
openldap/admin_password: ENC[AES256_GCM,data:DtVthpJqLdkI+5wxOMnCfBdqWkg0GSwUtsUeop24kd8=,iv:4e2Xn7B0M8yYEbs0V9ozn8WHJJMCBv6G46bdThufSXc=,tag:BsjKzh8teul6yLEKbvr93g==,type:str]
|
||||
openldap/config_password: ENC[AES256_GCM,data:6b9TIgOcmZfMDAVbJuqOoNS9kyrss/LMvySLyNonlRk=,iv:Jf9/triFouIDv7MY2J9W8ji7E5lUHqzwgBMqrcPuK1g=,tag:zQYZSesPiPVeNVBN1oEiHA==,type:str]
|
||||
openldap/ro_password: ENC[AES256_GCM,data:EHYUlIY24kY9K8opMi9MxSSosReZm5mEmbPFz+NdaXE=,iv:3pfVn4QDvJAVmWYWyX/Kko+K7nsE1yunLXN5uao+ea0=,tag:J954cH7a7Ey6Xq24ut5Jxw==,type:str]
|
||||
#ENC[AES256_GCM,data:upG3X+Z7di17BaWBQ/P0ohY=,iv:k3Kin642n4cJYwfPsQYE/4FokELFNDmMzxJ2D8S28HI=,tag:uYRnpeoCrwGQOEYWo2cBiw==,type:comment]
|
||||
authelia/jwt_secret: ENC[AES256_GCM,data:pXTQ06OGEP1oYFM0mkyL+c/zNRUMgL9x1fCQsMo2bak=,iv:mnOBWBrSn4gTfMXR5PCThs0v9QRDR5pfOQA8u0cuGnI=,tag:YXGq6Hmv/chw8fcEQoNlGA==,type:str]
|
||||
authelia/session_secret: ENC[AES256_GCM,data:EgIyGv/K6xDCxOZWA9tzGoNS4m+p/EOPHL64/eN1oqwar2iJFSanbUfq8doHmN8n9sADmPIKUKaL8+WJWfyjtBBcCn74q5FL+kDu6ZYo4V5cjkj8jUhRC97TIJ+e0lVKFJ4s+i+/OcOsv2TPS/haylGHVn1fnlwvEd3kn/mO73w=,iv:6VPxOkriecJdtm2EBCiKkZBTzmas3DkQuYhivfygCT8=,tag:uXW1tcyAFSkiwMGNiZ663w==,type:str]
|
||||
authelia/storage_encryption_key: ENC[AES256_GCM,data:pM8oQ4t0HQLdUvuRayLOpEwdxzRQlvCOrMtSPIU8Ryo=,iv:AK2jR3Ij/dBplDc1PYXXLK8P327CYRx3kVZUCcIkO5k=,tag:kJSuyOIzT4/RNQXEal1ODA==,type:str]
|
||||
#ENC[AES256_GCM,data:teUPyCgpHCpIb0hXRUg=,iv:lTdYkYxQKHcJGE7lkkcsa8u9ZsZAVqpfauf5SzTv6G0=,tag:uKydCL14BvAaOpUHAMBirg==,type:comment]
|
||||
gitea/admin_password: ENC[AES256_GCM,data:/39FQYn5GQoq/a5chLd4JUvSXTU8tOdzc9uXxNqViiw=,iv:Ysq2QUgkmONGsfj6xHKN3G/eitBX1rm9LLH9REF2h8g=,tag:eiVtlaB/6VdNMEBy4mSrTg==,type:str]
|
||||
gitea/lfs_jwt_secret: ENC[AES256_GCM,data:gyd2OV0qcaaD6FTT9UwLV5vGJ4b/SNtG86oCQqUqB+DlZFLYe91YFNG/wA==,iv:fxD2NFbEYAsmrXaZT030f0MiAol2cwln0mIzLPCE+Lg=,tag:xQtehnHuj18WYeR2UyYeXw==,type:str]
|
||||
gitea/oauth2_jwt_secret: ENC[AES256_GCM,data:M5CzWG1FbjheX4QwDajVsAMl2nyfe4Z1u30D5hjCQbScDBtuw123ZMZjGQ==,iv:vOnMShn9nmLPzxXJqTNnCIf6GT6CrV3lAKrepmI7btc=,tag:pTdrbmZ+hntuwaLiLyUNHQ==,type:str]
|
||||
gitea/internal_token: ENC[AES256_GCM,data:ZbwvPcOseUHAGDr4dwNu9u+qcr0yYYGdH2OjcuXPtgUt7HFq1a9f0Faxiphsh+3OXb1KqLj8USB/1AxSvt5kSYM/vqzSLZ+e1OKy0oO3o8YouCJLhPNkNO6q0eguQF6+,iv:E3APR8h+iNECoThrvy6v4SEdAsfnPITXvhIFT1Ug5qA=,tag:lCxReGAxJyVhwMjxNenvxg==,type:str]
|
||||
#ENC[AES256_GCM,data:r/uPlqg+7UGrM0G2xhmD6Bm1,iv:m/Ineh/mNfo1yUS+B8qtbMr1zRwiE6vw3EZIepB4QUA=,tag:/tB1W2JgyUQNvVWFM9478w==,type:comment]
|
||||
nextcloud/admin_password: ENC[AES256_GCM,data:KwS0kEjTKn+IAtYTD17X4Y/3hT9bUgqKBQ0vfhDK99A=,iv:AbJfw6NWRnnB8zXIO6l3sIWiXXWfM1ePJ5bodNlgjgI=,tag:XSQM8SSnuh3wjyN3IQdArA==,type:str]
|
||||
nextcloud/postgres_password: ENC[AES256_GCM,data:dsdqeQhWFvidqOXopetb3G54Ft56ZhPheTB7uG2JuVc=,iv:ubKH3ihlPXZjPSkvgEYn/teG5SNSh04nb4Lh1e2cX8o=,tag:DWNXJXWjpCU8QEcnt0+phA==,type:str]
|
||||
#ENC[AES256_GCM,data:riBX18BPE4XMBBv20JIEJbM6JS80e1jwiDq44KXMB6T/4Eehf2bgcFUm,iv:lDYdL1IvaBuixcw1BzPQxnM4HYZGA3YSDrJTxvz0QWs=,tag:tux8Mt56yw+7hE7BfgOXVw==,type:comment]
|
||||
cloudflare/api_token: ENC[AES256_GCM,data:te8SJz3sjnWX0MsacbEwYb0IC+SAlUBcSthLmHxpURTdpE3GfeNvjj5Z+il43cpFA33PaUY=,iv:XG2dt0Wc5jDcfGvKtRB1f6CAWXBmgnw+qqzMxDtmOok=,tag:PmEqZoKvqZm2vBxYSNH3Qg==,type:str]
|
||||
cloudflare/tunnel_token: ENC[AES256_GCM,data:HupdN2MFeQ+NPwynI1SM07E7yA5b66lbudKt/pNOemf9Q3l4zrYidLFpiQk6L6ajQpM0WQbEDYG2I1sxybu4fUah79MSZO7BoolYy6l/NDE5G35e3Kw9Yu1cFAyNZJ9s/RU8nG24OAMX+pMOkjk4bX4tzrWUkHmebRJf7iBZxsSys6o83arpyKcucLOfTyyLSRemXF8IXr2MGMypHkPrx+4w5MnY9tyY8JcclaiLDkpbVVDUTarbkg==,iv:sVAnAqAMdTn8HpEwcIz2B57SrPlYqV2/Oi3sYHanYzo=,tag:BmhemprKvn33Wt595MjKcQ==,type:str]
|
||||
#ENC[AES256_GCM,data:GpnZDeOAyr2pZxWHVd++1TMm230hvQ==,iv:jo8kWdd0Pm3d3xewCcyhauiBhI+SYIlWvczKn0PPZTg=,tag:INK0gZhKynkiOgi2ayrSMA==,type:comment]
|
||||
restic/password: ENC[AES256_GCM,data:iZNRA8qNspy7WnK+Dg1OOZj9Gt2Y/AXUG1gKTBGUt+6q7T6Lv5AqbVkN8khwlKyQWK6FNLh3/9ejsM7mybiyog==,iv:XMxMAgVMdCWnDCkdTxL72pbrg8Dy0xz2EYou7AaNgS0=,tag:KW9Tjhql0yF6h81Il1htbw==,type:str]
|
||||
restic/s3_access_key_id: ENC[AES256_GCM,data:XK8GqLHSC76K6z86RbqI4uNwZgcfl5R0Bg==,iv:t9+fGwwGX8PLwr30MJMYdOm02f/+XTcnMhSY1DP+nU0=,tag:fauNjH4lVtHa+L8Bfj8TOg==,type:str]
|
||||
restic/s3_secret_access_key: ENC[AES256_GCM,data:GUx4FPaHWuzNwOju7CQoZc5U2SLG+3GOn0zJvvRXzQ==,iv:Oq0q9a+esPkLygMkGaFFNZOOfMGMFVPeb+yHUcLcNZE=,tag:Rwd0NNyXt+L8IJCCiDJh8w==,type:str]
|
||||
#ENC[AES256_GCM,data:H+rGxOM6euNaSOval0ZXgKlRKQ==,iv:o0kU37iQzWAvTl5T9MK5RpHJ1eqhFftfVMEGMR40Hw8=,tag:rFcrmYZXpOpVdvW/zTul1A==,type:comment]
|
||||
wifi/psk: ENC[AES256_GCM,data:bkZnP8S7yQlaEfH+kN1FfjQqJw==,iv:n1wOv6rXDbGucKryV9qV0fgqXNC/GwDeDlY2k9/hSOI=,tag:LdC2ahrXVBcqLWU5nFHMlQ==,type:str]
|
||||
sops:
|
||||
lastmodified: "2026-04-18T20:53:59Z"
|
||||
mac: ENC[AES256_GCM,data:nEP5XRzdYdFBWp9tqIgxcjjR7+X9ScpUew6SGfE6bKSQjvbwKTCGW6dSOTe7FmpUKrOS+dJnwpPsWKu0jbX/Qm5EtfXaB0GWiiMjfejwshmyULuJKipuq1rC+YX+DmOXoWIiNwKIwd4tBEOfYFBJVLFcoP8DSFjettymT0idvAQ=,iv:RnWzW+2hUScofJVom+csqEhYME8/roIzdRC/YC8opyk=,tag:22rjZO28mjPsp9p3iuoHSQ==,type:str]
|
||||
pgp:
|
||||
- created_at: "2026-04-18T20:12:39Z"
|
||||
enc: |-
|
||||
-----BEGIN PGP MESSAGE-----
|
||||
|
||||
# --- OpenLDAP ---
|
||||
openldap/admin_password: "REPLACE-WITH-OLD-VALUE"
|
||||
openldap/config_password: "REPLACE-WITH-OLD-VALUE"
|
||||
openldap/ro_password: "REPLACE-WITH-OLD-VALUE"
|
||||
|
||||
# --- Authelia (regenerated fresh — these are random strings) ---
|
||||
authelia/jwt_secret: "GENERATE-random-64-chars"
|
||||
authelia/session_secret: "GENERATE-random-64-chars"
|
||||
authelia/storage_encryption_key: "GENERATE-random-64-chars"
|
||||
|
||||
# --- Gitea ---
|
||||
gitea/admin_password: "REPLACE-WITH-OLD-VALUE"
|
||||
# These three are regenerated — gitea will re-derive on first start:
|
||||
gitea/lfs_jwt_secret: "GENERATE-random-43-chars-base64url"
|
||||
gitea/oauth2_jwt_secret: "GENERATE-random-43-chars-base64url"
|
||||
gitea/internal_token: "GENERATE-random-100-alphanum"
|
||||
|
||||
# --- Nextcloud ---
|
||||
nextcloud/admin_password: "REPLACE-WITH-OLD-VALUE"
|
||||
nextcloud/postgres_password: "REPLACE-WITH-OLD-VALUE"
|
||||
|
||||
# --- Cloudflare (DNS-01 ACME + tunnel) ---
|
||||
cloudflare/api_token: "REPLACE-WITH-CF-DNS-EDIT-TOKEN"
|
||||
cloudflare/tunnel_token: "REPLACE-WITH-CF-TUNNEL-TOKEN"
|
||||
|
||||
# --- Restic backup ---
|
||||
restic/password: "GENERATE-random-passphrase"
|
||||
# Repository destination — e.g. "sftp:user@nas:/backups/homey"
|
||||
# or "b2:bucketname:homey" for Backblaze B2
|
||||
# Set the actual repo URL in modules/backup.nix or override per-host.
|
||||
hQIMAwdqopdXmgBkAQ/+OOgkrBhQBXcbxH2Rj3yQ5cDTkH3LZdbBH+vLvEFfoXLk
|
||||
RI12n3y+gQo5Gbs1eD9tJOuBIqYZwG9JTHiv43d6DXRFdY9PlMWaL6HeG6le/dj7
|
||||
/JpirCofXhbL+GzLxQXnEOeMYm0Rhh5a9FbvqOwVkx2cCYlaWDYrZRPXFkjTw0et
|
||||
DYv9a/ZUMAEKwSEJO7kRMpWYiPGI6KkArJrPBm7C6M4j5+KBv29FRSpw/IJiOMtT
|
||||
CFWepDk+RJq+pMRNB91p/OO6YdrwMQJdCRcqC94I3TdxhVKoCCagULoE3vwHzxGQ
|
||||
O5kDDc1GuQbIcNg2bfyWyKv6L9A30JaQT+8t3UMSHxAoWlvZes1y3tvquQeI8m+N
|
||||
JILTmMWHjAplals4u+8BX7MCVolh4zJRNr1xiFy/UamYB70UORf2rjjGvMqOHsM+
|
||||
IPJ2pIqbXDYs3syjKvWQFpxZczGgSPxHPlF9Tm+hu972ub9Ex2uVWntvjnt26H6+
|
||||
/JbdV/7gW95AEkJ+HPjynDvYZ1tRBFGmwBOCsOkOfKmmopKcAooT6qDzC5hZBhBE
|
||||
Yvl9TlC5GEBPnV4dtIxTZrqRqvbt5CvikmCI2h3/pcMWGM8a0iN2K0iNvlKGnKey
|
||||
jlGC+0nQzwLllFtGBgOGKeqG1HQ5yPf2W4Ic7uSVGI3xPHkd5gG1MAHORw/3cP3S
|
||||
XgHadJRTvnNnDsZjT7P8rIYTBnpe2zx+I8N21r+Jh5/hCv8wSl819QaBA4IMC5kt
|
||||
Os9nSYc1KzodkJR35O8Bdy/7H8SF34tXjpyhWvE4OEqEwN7AdI0L0PfOiGMBjms=
|
||||
=7asV
|
||||
-----END PGP MESSAGE-----
|
||||
fp: 076AA297579A0064
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.12.2
|
||||
|
||||
Reference in New Issue
Block a user