Changes to rpi setup
This commit is contained in:
+45
-4
@@ -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
|
||||
@@ -58,7 +74,9 @@ in
|
||||
# -----------------------------------------------------------------------
|
||||
# Secrets
|
||||
# -----------------------------------------------------------------------
|
||||
sops.secrets."restic/password" = { owner = "root"; };
|
||||
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" = {
|
||||
|
||||
+27
-22
@@ -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,34 +145,41 @@ 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
|
||||
'')
|
||||
];
|
||||
};
|
||||
after = lib.mkAfter [ "podman-authelia.service" ];
|
||||
wants = lib.mkAfter [ "podman-authelia.service" ];
|
||||
after = lib.mkAfter [ "podman-authelia.service" ];
|
||||
wants = lib.mkAfter [ "podman-authelia.service" ];
|
||||
};
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
+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";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user