From 39e044b757239c348bdda144538216c6154e0b6c Mon Sep 17 00:00:00 2001 From: Christine Elisabeth Koppel Date: Mon, 9 Feb 2026 22:40:44 +0100 Subject: [PATCH 1/6] Added extra notes, manual database restore script (preliminary) and added teamspeak.nix configuration. --- .../modules/songsheet/wavelog/notes.md | 7 + .../modules/system/teamspeak.nix | 66 ++++++++ .../manual_database_restore.zsh | 147 ++++++++++++++++++ 3 files changed, 220 insertions(+) create mode 100644 nix-system-configs/modules/songsheet/wavelog/notes.md create mode 100644 nix-system-configs/modules/system/teamspeak.nix create mode 100644 nix-system-configs/modules/system_scripts/backup_strategem/manual_database_restore.zsh diff --git a/nix-system-configs/modules/songsheet/wavelog/notes.md b/nix-system-configs/modules/songsheet/wavelog/notes.md new file mode 100644 index 0000000..686e298 --- /dev/null +++ b/nix-system-configs/modules/songsheet/wavelog/notes.md @@ -0,0 +1,7 @@ +How to change passwords in case of a major heck up +```zsh +sudo podman exec -it -u www-data partdb sh +# inside container: +cd /var/www/html +php bin/console partdb:users:set-password USERNAME +``` \ No newline at end of file diff --git a/nix-system-configs/modules/system/teamspeak.nix b/nix-system-configs/modules/system/teamspeak.nix new file mode 100644 index 0000000..113919a --- /dev/null +++ b/nix-system-configs/modules/system/teamspeak.nix @@ -0,0 +1,66 @@ +{ + config, + pkgs, + lib, + ... +}: { + options.local = { + hostname = lib.mkOption { + type = lib.types.str; + default = "nixos-default"; + description = "System hostname"; + }; + username = lib.mkOption { + type = lib.types.str; + default = "user"; + description = "Primary user username"; + }; + userDescription = lib.mkOption { + type = lib.types.str; + default = "NixOS User"; + description = "Primary user description"; + }; + address = lib.mkOption { + type = lib.types.str; + default = "10.1.1.100"; + description = "Static IP address"; + }; + }; + + imports = [ + ./modules/desktop-manager/sway_greetd_homemanager.nix + ./modules/local/hostname_username.nix + ./modules/local/networking_local.nix + ./modules/bootloader/seabios-assigned-iso-at-birth.nix + ./modules/lix-default.nix + # Optionally: ./modules/toolsets/remote_building.nix + # + # + ## Compose modules for Portainer service + ./modules/songsheet/wavelog/docker-compose.nix + ./modules/secrets-config/sops-composesongsheet.nix + ]; + + config = { + local.hostname = "christine-teamspeak"; + local.username = "teamspeak_christine"; + local.userDescription = "NixOS Teamspeak Service"; + local.address = "10.1.1.248"; + + services.teamspeak3 = { + enable = true; + openFirewall = true; + }; + + networking.firewall.allowedTCPPorts = [ + 80 + 443 + ]; + networking.firewall.allowedUDPPorts = [ + 80 + 443 + ]; + + system.stateVersion = "25.11"; + }; +} diff --git a/nix-system-configs/modules/system_scripts/backup_strategem/manual_database_restore.zsh b/nix-system-configs/modules/system_scripts/backup_strategem/manual_database_restore.zsh new file mode 100644 index 0000000..eb82bdf --- /dev/null +++ b/nix-system-configs/modules/system_scripts/backup_strategem/manual_database_restore.zsh @@ -0,0 +1,147 @@ +#!/usr/bin/env zsh + +set -euo pipefail + +PATH_TO_BACKUPGPG_KEY=${PATH_TO_BACKUPGPG_KEY:-} +PATH_TO_BACKUP_FILE=${PATH_TO_BACKUP_FILE:-} +PATH_TO_OUTPUT_DIR=${PATH_TO_OUTPUT_DIR:-} + +KEEP=false + +# TIL You can do these kind of scripts. +usage() { + cat <] [-k ] [-o ] [--keep] + +Decrypt and unpack a .gz.gpg backup file for inspection. The script will +create a temporary GNUPGHOME (if a key is provided), decrypt the file, and +if needed gunzip it to produce a .sql file. The resulting SQL file path will +be printed and a short preview will be shown. + +Options: + -f Path to the .sql.gz.gpg backup file (or set PATH_TO_BACKUP_FILE) + -k Path to a private key file to import (optional; or set PATH_TO_BACKUPGPG_KEY) + -o Output directory for the final .sql file (defaults to current working directory) + --keep Keep temporary files (do not delete temp GNUPGHOME/dir) + -h, --help Show this message +EOF + exit 1 +} + +# Simple arg parsing to allow flags +while [[ $# -gt 0 ]]; do + case "$1" in + -f) + PATH_TO_BACKUP_FILE="$2"; shift 2;; + -k) + PATH_TO_BACKUPGPG_KEY="$2"; shift 2;; + -o) + PATH_TO_OUTPUT_DIR="$2"; shift 2;; + --keep) + KEEP=true; shift;; + -h|--help) + usage;; + *) + echo "Unknown argument: $1" >&2; usage;; + esac +done + +if [[ -z "$PATH_TO_BACKUP_FILE" ]]; then + echo "Error: no backup file provided." >&2 + usage +fi + +if [[ ! -f "$PATH_TO_BACKUP_FILE" ]]; then + echo "Error: backup file does not exist: $PATH_TO_BACKUP_FILE" >&2 + exit 2 +fi + +command -v gpg >/dev/null 2>&1 || { echo "gpg not found in PATH" >&2; exit 3; } +command -v gunzip >/dev/null 2>&1 || { echo "gunzip not found in PATH" >&2; exit 3; } +command -v file >/dev/null 2>&1 || { echo "file command not found in PATH" >&2; exit 3; } + +TMPDIR=$(mktemp -d) +trap '[[ "$KEEP" = false ]] && rm -rf "$TMPDIR"' EXIT + +# If a key is provided, use an isolated GNUPGHOME so we don't touch the user's keyring. +if [[ -n "$PATH_TO_BACKUPGPG_KEY" ]]; then + if [[ ! -f "$PATH_TO_BACKUPGPG_KEY" ]]; then + echo "Private key file not found: $PATH_TO_BACKUPGPG_KEY" >&2 + exit 4 + fi + export GNUPGHOME="$TMPDIR/gnupg" + # Create GNUPGHOME and explicitly set secure permissions + mkdir -p "$GNUPGHOME" + chmod 0700 "$GNUPGHOME" + echo "Importing provided private key into temporary GNUPGHOME..." + gpg --batch --import "$PATH_TO_BACKUPGPG_KEY" >/dev/null 2>&1 +fi + +# Determine output directory: prefer PATH_TO_OUTPUT_DIR, otherwise current directory +if [[ -n "$PATH_TO_OUTPUT_DIR" ]]; then + OUTDIR="$PATH_TO_OUTPUT_DIR" +else + OUTDIR="$(pwd)" +fi + +mkdir -p "$OUTDIR" + +# Keep decrypted intermediate in TMPDIR, final .sql in OUTDIR (PWD by default) +DECRYPTED="$TMPDIR/backup.decrypted" + +# Derive a reasonable final filename from the backup file name +orig_base=$(basename -- "$PATH_TO_BACKUP_FILE") +name_no_gpg=${orig_base%.gpg} +name_no_gz=${name_no_gpg%.gz} +if [[ "$name_no_gz" == *.sql ]]; then + final_name="$name_no_gz" +else + final_name="${name_no_gz}.sql" +fi +FINAL_SQL="$OUTDIR/$final_name" +# If file exists, append timestamp to avoid accidental overwrite +if [[ -e "$FINAL_SQL" ]]; then + ts=$(date +%Y%m%d%H%M%S) + FINAL_SQL="$OUTDIR/${name_no_gz}.${ts}.sql" + echo "Notice: output file already existed; writing to $FINAL_SQL" +fi + +echo "Decrypting: $PATH_TO_BACKUP_FILE" +# Decrypt to a temporary file. gpg will prompt for passphrase if needed (user must be present) +if ! gpg --batch --yes --decrypt --output "$DECRYPTED" "$PATH_TO_BACKUP_FILE"; then + echo "gpg decryption failed" >&2 + exit 5 +fi + +# Detect if the decrypted file is gzip compressed +if file --brief --mime-type "$DECRYPTED" | grep -q gzip; then + echo "Detected gzip compressed payload; decompressing to: $FINAL_SQL" + if ! gunzip -c "$DECRYPTED" > "$FINAL_SQL"; then + echo "gunzip failed" >&2 + exit 6 + fi +else + # Not gzip: assume plain SQL or other plain text + echo "Writing decrypted payload to: $FINAL_SQL" + cp "$DECRYPTED" "$FINAL_SQL" +fi + +echo "Unpacked SQL available at: $FINAL_SQL" + +# Show a short preview +echo +echo "--- File preview (first 80 lines) ---" +head -n 80 "$FINAL_SQL" || true +echo "--- end preview ---" + +echo +if [[ "$KEEP" = true ]]; then + echo "Temporary files (including GNUPGHOME) retained under: $TMPDIR" + echo "Final SQL file retained at: $FINAL_SQL" +else + echo "Temporary files (excluding final SQL) were stored under: $TMPDIR (removed on exit)" + echo "Final SQL file is at: $FINAL_SQL" +fi + +# Print final path so callers can copy it if they used --keep or scripting +echo "$FINAL_SQL" From 5dba269c1803aaeb8c162d16f0ac56df380a112f Mon Sep 17 00:00:00 2001 From: Christine Elisabeth Koppel Date: Mon, 9 Feb 2026 23:14:44 +0100 Subject: [PATCH 2/6] Setup Teamspeak load balancers --- nix-system-configs/modules/system/traefik.nix | 59 ++++++++++++++++++- 1 file changed, 56 insertions(+), 3 deletions(-) diff --git a/nix-system-configs/modules/system/traefik.nix b/nix-system-configs/modules/system/traefik.nix index 36abf3f..ae660ae 100644 --- a/nix-system-configs/modules/system/traefik.nix +++ b/nix-system-configs/modules/system/traefik.nix @@ -67,6 +67,16 @@ ]; }; }; + # TeamSpeak entry points + teamspeak-voice = { + address = ":9987/udp"; + }; + teamspeak-squery = { + address = ":10022/tcp"; + }; + teamspeak-data = { + address = ":30033/tcp"; + }; }; log = { level = "INFO"; @@ -124,6 +134,49 @@ ]; }; }; + + # TCP routing for TeamSpeak + tcp.routers = { + teamspeak-squery = { + rule = "HostSNI(`*`)"; + service = "teamspeak-squery"; + entryPoints = ["teamspeak-squery"]; + }; + teamspeak-data = { + rule = "HostSNI(`*`)"; + service = "teamspeak-data"; + entryPoints = ["teamspeak-data"]; + }; + }; + + tcp.services = { + teamspeak-squery.loadBalancer = { + servers = [ + {address = "10.1.1.249:10022";} + ]; + }; + teamspeak-data.loadBalancer = { + servers = [ + {address = "10.1.1.249:30033";} + ]; + }; + }; + + # UDP routing for TeamSpeak voice + udp.routers = { + teamspeak-voice = { + entryPoints = ["teamspeak-voice"]; + service = "teamspeak-voice"; + }; + }; + + udp.services = { + teamspeak-voice.loadBalancer = { + servers = [ + {address = "10.1.1.249:9987";} + ]; + }; + }; }; }; @@ -147,9 +200,9 @@ wants = ["acme-finished-prg-radio.org.target"]; }; - networking.firewall.allowedTCPPorts = [80 443]; - networking.firewall.allowedUDPPorts = [80 443]; + networking.firewall.allowedTCPPorts = [80 443 10022 30033]; + networking.firewall.allowedUDPPorts = [80 443 9987]; system.stateVersion = "25.11"; }; -} +} \ No newline at end of file From 139345f84ec0ab8d3202958e0627e4978bc41be0 Mon Sep 17 00:00:00 2001 From: Christine Elisabeth Koppel Date: Mon, 9 Feb 2026 23:48:43 +0100 Subject: [PATCH 3/6] Oops put wrong address :3 --- nix-system-configs/modules/system/traefik.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nix-system-configs/modules/system/traefik.nix b/nix-system-configs/modules/system/traefik.nix index ae660ae..cdfb311 100644 --- a/nix-system-configs/modules/system/traefik.nix +++ b/nix-system-configs/modules/system/traefik.nix @@ -152,12 +152,12 @@ tcp.services = { teamspeak-squery.loadBalancer = { servers = [ - {address = "10.1.1.249:10022";} + {address = "10.1.1.248:10022";} ]; }; teamspeak-data.loadBalancer = { servers = [ - {address = "10.1.1.249:30033";} + {address = "10.1.1.248:30033";} ]; }; }; @@ -173,7 +173,7 @@ udp.services = { teamspeak-voice.loadBalancer = { servers = [ - {address = "10.1.1.249:9987";} + {address = "10.1.1.248:9987";} ]; }; }; From d2349014329c38008dc992b1b6c989865cd7b1cc Mon Sep 17 00:00:00 2001 From: Christine Elisabeth Koppel Date: Tue, 10 Feb 2026 00:29:03 +0100 Subject: [PATCH 4/6] Add Matrix Conduit service configuration to Nix setup --- .../modules/system/teamspeak.nix | 28 +++++++++++++++++++ nix-system-configs/modules/system/traefik.nix | 15 +++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/nix-system-configs/modules/system/teamspeak.nix b/nix-system-configs/modules/system/teamspeak.nix index 113919a..f05443e 100644 --- a/nix-system-configs/modules/system/teamspeak.nix +++ b/nix-system-configs/modules/system/teamspeak.nix @@ -52,13 +52,41 @@ openFirewall = true; }; + ## Add Conduit for Matrix + + # See https://search.nixos.org/options?channel=unstable&query=services.matrix-conduit. + # and https://docs.conduit.rs/configuration.html + services.matrix-conduit = { + enable = true; + settings.global = { + allow_registration = true; + # You will need this token when creating your first account. + registration_token = "testtoken1234"; + # server_name = yourDomainName; + port = 12244; + # Bind to all interfaces so Traefik on the network can reach Conduit. + address = "0.0.0.0"; + database_backend = "rocksdb"; + server_name = "https://lgbtq.prg-radio.org"; + + # See https://docs.conduit.rs/turn.html, and https://github.com/element-hq/synapse/blob/develop/docs/turn-howto.md for more details + # turn_uris = [ + # "turn:your.turn.url?transport=udp" + # "turn:your.turn.url?transport=tcp" + # ]; + # turn_secret = "your secret"; + }; + }; + networking.firewall.allowedTCPPorts = [ 80 443 + 12244 ]; networking.firewall.allowedUDPPorts = [ 80 443 + 12244 ]; system.stateVersion = "25.11"; diff --git a/nix-system-configs/modules/system/traefik.nix b/nix-system-configs/modules/system/traefik.nix index cdfb311..11e1bfb 100644 --- a/nix-system-configs/modules/system/traefik.nix +++ b/nix-system-configs/modules/system/traefik.nix @@ -114,6 +114,13 @@ entryPoints = ["websecure"]; tls = {}; }; + + matrix = { + rule = "Host(`lgbtq.prg-radio.org`)"; + service = "matrix"; + entryPoints = ["websecure"]; + tls = {}; + }; }; http.services = { forgejo.loadBalancer = { @@ -133,6 +140,12 @@ {url = "http://10.1.1.249:8087";} ]; }; + + matrix.loadBalancer = { + servers = [ + { url = "http://10.1.1.248:12244"; } + ]; + }; }; # TCP routing for TeamSpeak @@ -205,4 +218,4 @@ system.stateVersion = "25.11"; }; -} \ No newline at end of file +} From a027b994463bbd3d09d4c59b59516528ef77b101 Mon Sep 17 00:00:00 2001 From: Christine Elisabeth Koppel Date: Tue, 10 Feb 2026 00:35:21 +0100 Subject: [PATCH 5/6] Fix server_name URL in Conduit configuration --- nix-system-configs/modules/system/teamspeak.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nix-system-configs/modules/system/teamspeak.nix b/nix-system-configs/modules/system/teamspeak.nix index f05443e..11499d3 100644 --- a/nix-system-configs/modules/system/teamspeak.nix +++ b/nix-system-configs/modules/system/teamspeak.nix @@ -67,7 +67,7 @@ # Bind to all interfaces so Traefik on the network can reach Conduit. address = "0.0.0.0"; database_backend = "rocksdb"; - server_name = "https://lgbtq.prg-radio.org"; + server_name = "lgbtq.prg-radio.org"; # See https://docs.conduit.rs/turn.html, and https://github.com/element-hq/synapse/blob/develop/docs/turn-howto.md for more details # turn_uris = [ From 34abb21e0b6849a1cd8e3d5d8b55137ad27eff85 Mon Sep 17 00:00:00 2001 From: Christine Elisabeth Koppel Date: Tue, 10 Feb 2026 00:58:40 +0100 Subject: [PATCH 6/6] Enhance configuration for Matrix to enable federation --- .../modules/system/teamspeak.nix | 2 +- nix-system-configs/modules/system/traefik.nix | 44 ++++++++++++------- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/nix-system-configs/modules/system/teamspeak.nix b/nix-system-configs/modules/system/teamspeak.nix index 11499d3..5b5ab6a 100644 --- a/nix-system-configs/modules/system/teamspeak.nix +++ b/nix-system-configs/modules/system/teamspeak.nix @@ -61,7 +61,7 @@ settings.global = { allow_registration = true; # You will need this token when creating your first account. - registration_token = "testtoken1234"; + #registration_token = "testtoken1234"; # server_name = yourDomainName; port = 12244; # Bind to all interfaces so Traefik on the network can reach Conduit. diff --git a/nix-system-configs/modules/system/traefik.nix b/nix-system-configs/modules/system/traefik.nix index 11e1bfb..ffe915e 100644 --- a/nix-system-configs/modules/system/traefik.nix +++ b/nix-system-configs/modules/system/traefik.nix @@ -67,6 +67,10 @@ ]; }; }; + # Federation entrypoint: external TLS on 8448 + federation = { + address = ":8448"; + }; # TeamSpeak entry points teamspeak-voice = { address = ":9987/udp"; @@ -100,7 +104,13 @@ entryPoints = ["websecure"]; tls = {}; }; - + # Matrix HTTP router for client requests (Element etc.) + matrix = { + rule = "Host(`lgbtq.prg-radio.org`)"; + service = "matrix"; + entryPoints = ["websecure"]; + tls = {}; + }; wavelog = { rule = "Host(`wavelog.prg-radio.org`)"; service = "wavelog"; @@ -114,13 +124,6 @@ entryPoints = ["websecure"]; tls = {}; }; - - matrix = { - rule = "Host(`lgbtq.prg-radio.org`)"; - service = "matrix"; - entryPoints = ["websecure"]; - tls = {}; - }; }; http.services = { forgejo.loadBalancer = { @@ -128,7 +131,11 @@ {url = "http://10.1.1.4:3000";} ]; }; - + matrix.loadBalancer = { + servers = [ + {url = "http://10.1.1.248:12244";} + ]; + }; wavelog.loadBalancer = { servers = [ {url = "http://10.1.1.249:8086";} @@ -140,12 +147,6 @@ {url = "http://10.1.1.249:8087";} ]; }; - - matrix.loadBalancer = { - servers = [ - { url = "http://10.1.1.248:12244"; } - ]; - }; }; # TCP routing for TeamSpeak @@ -160,6 +161,12 @@ service = "teamspeak-data"; entryPoints = ["teamspeak-data"]; }; + # Federation TCP router: incoming on :8448 -> Conduit backend + matrix-federation = { + rule = "HostSNI(`*`)"; + service = "matrix-federation"; + entryPoints = ["federation"]; + }; }; tcp.services = { @@ -173,6 +180,11 @@ {address = "10.1.1.248:30033";} ]; }; + matrix-federation.loadBalancer = { + servers = [ + {address = "10.1.1.248:12244";} + ]; + }; }; # UDP routing for TeamSpeak voice @@ -213,7 +225,7 @@ wants = ["acme-finished-prg-radio.org.target"]; }; - networking.firewall.allowedTCPPorts = [80 443 10022 30033]; + networking.firewall.allowedTCPPorts = [80 443 10022 30033 8448]; networking.firewall.allowedUDPPorts = [80 443 9987]; system.stateVersion = "25.11";