From 140935c12062dd99726223d78bda4797e6a3469d Mon Sep 17 00:00:00 2001 From: Christine Elisabeth Koppel Date: Tue, 3 Mar 2026 10:42:04 +0100 Subject: [PATCH] Update the home-manager new hash. Added dynamic public IPv4 address updater to traefik. --- .../sway_greetd_homemanager.nix | 2 +- nix-system-configs/modules/system/traefik.nix | 142 ++++++++++++++++++ 2 files changed, 143 insertions(+), 1 deletion(-) diff --git a/nix-system-configs/modules/desktop-manager/sway_greetd_homemanager.nix b/nix-system-configs/modules/desktop-manager/sway_greetd_homemanager.nix index cd34a7d..2f759b4 100644 --- a/nix-system-configs/modules/desktop-manager/sway_greetd_homemanager.nix +++ b/nix-system-configs/modules/desktop-manager/sway_greetd_homemanager.nix @@ -6,7 +6,7 @@ }: let home-manager = builtins.fetchTarball { url = "https://github.com/nix-community/home-manager/archive/release-25.11.tar.gz"; - sha256 = "1kqxy6r4ahnbazmpa4pncdp62najdikdaw8hvrv8nl6qxvbmf9fy"; + sha256 = "8C+y46xA9bxcchj9GeDPJaRUDApaA3sy2fhJr1bTbUw="; }; cfg = config.services.forgejo; srv = cfg.settings.server; diff --git a/nix-system-configs/modules/system/traefik.nix b/nix-system-configs/modules/system/traefik.nix index 6c50686..7191ddf 100644 --- a/nix-system-configs/modules/system/traefik.nix +++ b/nix-system-configs/modules/system/traefik.nix @@ -5,6 +5,39 @@ ... }: let choose = paths: builtins.head (builtins.filter (p: builtins.pathExists p) paths); + # Package the upstream cloudflare-ddns-updater from GitHub into the Nix store + upstreamOwner = "K0p1-Git"; + upstreamRepo = "cloudflare-ddns-updater"; + upstreamRev = "e9906c3aa0b73c26e3473618ad7a69db853e669d"; + upstreamSrc = pkgs.fetchFromGitHub { + owner = upstreamOwner; + repo = upstreamRepo; + rev = upstreamRev; + sha256 = "0j1pv57hk9rhr1kxqqjxg71y0d2d1hi3b4yiq908x5kcv3abbina"; + }; + + cloudflare-ddns-pkg = pkgs.stdenv.mkDerivation { + pname = "cloudflare-ddns-updater"; + version = upstreamRev; + src = upstreamSrc; + # no build required for a script-only repo + buildPhase = '': ''; + installPhase = '' + mkdir -p $out/bin $out/lib/cloudflare-ddns + cp -r $src/* $out/lib/cloudflare-ddns/ || true + if [ -x "$out/lib/cloudflare-ddns/update.sh" ]; then + cp "$out/lib/cloudflare-ddns/update.sh" $out/bin/cloudflare-ddns + chmod +x $out/bin/cloudflare-ddns + else + # If upstream layout changes, keep the repo contents available under $out/lib + true + fi + ''; + meta = with pkgs.lib; { + description = "Cloudflare DDNS updater"; + license = licenses.mit; + }; + }; in { options.local = { hostname = lib.mkOption { @@ -562,6 +595,115 @@ in { }; }; + # PRG Cloudflare DDNS updater + environment.etc."cloudflare-ddns/update.sh" = { + text = let + domain = "prg-radio.org"; + records = ["git" "grafana" "anubis" "wavelog" "partdb" "mail" "mailadmin" "@"]; + recordsStr = lib.concatStringsSep " " records; + zoneId = "9fde8d0fa53502f2d1b7e0b1d3765d49"; + envFile = "/home/traefikprg/cloudflare/cloudflare.env"; + in '' + #!/usr/bin/env bash + set -euuo pipefail + + # Load environment variables (must contain CLOUDFLARE_API_TOKEN) + if [ -f "${envFile}" ]; then + # shellcheck disable=SC1090 + source "${envFile}" + fi + + if [ -z "$CLOUDFLARE_API_TOKEN" ]; then + echo "CLOUDFLARE_API_TOKEN is not set in ${envFile}, aborting" >&2 + exit 2 + fi + + API_BASE="https://api.cloudflare.com/client/v4" + + # get current public IPv4 + CURRENT_IP=$(curl -4 -sS https://ipv4.icanhazip.com | tr -d '\n') || true + if [ -z "$CURRENT_IP" ]; then + echo "Failed to detect public IPv4 address" >&2 + exit 3 + fi + + jq=${pkgs.jq}/bin/jq + + update_or_create() { + local fqdn="$1" + # Query existing A records for fqdn + res=$(curl -sS -X GET "$API_BASE/zones/${zoneId}/dns_records?type=A&name=$fqdn" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json") + + ok=$(echo "$res" | $jq -r '.success') + if [ "$ok" != "true" ]; then + echo "Cloudflare API query failed for $fqdn: $res" >&2 + return 1 + fi + + existing_ip=$(echo "$res" | $jq -r '.result[0].content // empty') + record_id=$(echo "$res" | $jq -r '.result[0].id // empty') + + if [ -n "$existing_ip" ] && [ "$existing_ip" = "$CURRENT_IP" ]; then + echo "$fqdn is already set to $CURRENT_IP, skipping" + return 0 + fi + + payload=$(cat < $CURRENT_IP" + curl -sS -X PUT "$API_BASE/zones/${zoneId}/dns_records/$record_id" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + --data "$payload" | $jq -r '.' + else + echo "Creating record $fqdn -> $CURRENT_IP" + curl -sS -X POST "$API_BASE/zones/${zoneId}/dns_records" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + --data "$payload" | $jq -r '.' + fi + } + + echo "Starting PRG Cloudflare DDNS updater: setting IP=$CURRENT_IP" + for r in ${recordsStr}; do + if [ "$r" = "@" ]; then + fqdn="${domain}" + else + fqdn="$r.${domain}" + fi + update_or_create "$fqdn" || true + done + ''; + mode = "0755"; + group = "root"; + }; + + systemd.services."prg-cloudflare-ddns-updater" = { + description = "PRG Cloudflare DDNS updater"; + wantedBy = ["multi-user.target"]; + serviceConfig = { + Type = "oneshot"; + EnvironmentFile = "/home/traefikprg/cloudflare/cloudflare.env"; + ExecStart = "${pkgs.bash}/bin/bash /etc/cloudflare-ddns/update.sh"; + }; + }; + + systemd.timers."prg-cloudflare-ddns-updater" = { + description = "Run PRG Cloudflare DDNS updater periodically"; + wantedBy = ["timers.target"]; + timerConfig = { + OnBootSec = "1m"; + OnUnitActiveSec = "10m"; + Persistent = true; + }; + }; + # Enable Tailscale for remote access to Traefik dashboard and configuration services.tailscale.enable = true;