From e12dfa2ca8bb76c43e5b3fc70c3e79db1f4f0f13 Mon Sep 17 00:00:00 2001 From: rasmus-kirk Date: Wed, 28 Feb 2024 10:32:58 +0100 Subject: [PATCH] Added njalla ddns --- README.md | 4 ++ nixarr/default.nix | 102 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) diff --git a/README.md b/README.md index 6316bc7..94ce546 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,10 @@ is greatly appreciated. permissions. - **State Management:** All services support state management and all state that they manage is located by default in `/data/.state/nixarr/*` +- **Optional Dynamic DNS support:** If you use [Njalla](https://njal.la/) + and don't have a static IP, you can use the `nixarr.ddns.njalla.enable` + option to dynamically update a DNS record that points to the dynamic public + IP of your server. - **Optional Automatic Port Forwarding:** This module has a UPNP support that lets services request ports from your router automatically, if you enable it. diff --git a/nixarr/default.nix b/nixarr/default.nix index 4107a43..f957c22 100644 --- a/nixarr/default.nix +++ b/nixarr/default.nix @@ -1,6 +1,7 @@ { config, lib, + pkgs, ... }: with lib; let @@ -69,6 +70,46 @@ in { ''; }; + ddns.njalla = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + **Required options:** + + - [`nixarr.ddns.njalla.keysFile`](#nixarr.ddns.njalla.keysfile) + + Whether or not to enable DDNS for a [Njalla](https://njal.la/) + domain. + ''; + }; + + keysFile = mkOption { + type = with types; nullOr path; + default = null; + description = '' + A path to a JSON-file containing key value pairs of domains and keys. + + To get the keys, create a dynamic njalla record. Upon creation + you should see something like the following command suggested: + + ```sh + curl "https://njal.la/update/?h=jellyfin.example.com&k=zeubesojOLgC2eJC&auto" + ``` + + Then the JSON-file you pass here should contain: + + ```json + { + "jellyfin.example.com": "zeubesojOLgC2eJC" + } + ``` + + You can, of course, add more key-value pairs than just one. + ''; + }; + }; + vpn = { enable = mkOption { type = types.bool; @@ -146,6 +187,13 @@ in { to be set, but it was not. ''; } + { + assertion = cfg.ddns.njalla.enable -> cfg.ddns.njalla.keysFile != null; + message = '' + The nixarr.ddns.njalla.enable option requires the + nixarr.ddns.njalla.keysFile option to be set, but it was not. + ''; + } ]; users.groups = { @@ -230,5 +278,59 @@ in { openTcpPorts = cfg.vpn.openTcpPorts; openUdpPorts = cfg.vpn.openUdpPorts; }; + + systemd.timers = mkIf cfg.ddns.njalla.enable { + ddnsNjalla = { + description = "Timer for setting the Njalla DDNS records"; + + timerConfig = { + OnBootSec = "30"; # Run 30 seconds after system boot + OnCalendar = "hourly"; + Persistent = true; # Run service immediately if last window was missed + RandomizedDelaySec = "5min"; # Run service OnCalendar +- 5min + }; + + wantedBy = ["multi-user.target"]; + }; + }; + + systemd.services = let + ddns-njalla = pkgs.writeShellApplication { + name = "ddns-njalla"; + + runtimeInputs = with pkgs; [ curl jq ]; + + # Thanks chatgpt... + text = '' + # Path to the JSON file + json_file="${cfg.ddns.njalla.keysFile}" + + # Convert the JSON object into a series of tab-separated key-value pairs using jq + # - `to_entries[]`: Convert the object into an array of key-value pairs. + # - `[.key, .value]`: For each pair, create an array containing the key and the value. + # - `@tsv`: Convert the array to a tab-separated string. + # The output will be a series of lines, each containing a key and a value separated by a tab. + jq_command='to_entries[] | [.key, .value] | @tsv' + + # Read the converted output line by line + # - `IFS=$'\t'`: Use the tab character as the field separator. + # - `read -r key val`: For each line, split it into `key` and `val` based on the tab separator. + while IFS=$'\t' read -r key val; do + # For each key-value pair, execute the curl command + # Replace `''${key}` and `''${val}` in the URL with the actual key and value. + curl -s "https://njal.la/update/?h=''${key}&k=''${val}&auto" + done < <(jq -r "$jq_command" "$json_file") + ''; + }; + in mkIf cfg.ddns.njalla.enable { + ddnsNjalla = { + description = "Sets the Njalla DDNS records"; + + serviceConfig = { + ExecStart = getExe ddns-njalla; + Type = "oneshot"; + }; + }; + }; }; }