From f765e3d030026a07913e4a5473cb092d2b76b15b Mon Sep 17 00:00:00 2001 From: Colin Date: Tue, 11 Jul 2023 00:55:04 +0000 Subject: [PATCH] sane-ip-check: also store the upnp gateway --- modules/services/dyn-dns.nix | 35 +++++++++-- pkgs/additional/sane-scripts/default.nix | 9 +-- .../sane-scripts/src/lib/sane_bt.py | 1 + .../sane-scripts/src/lib/sane_ssdp.py | 1 + .../additional/sane-scripts/src/sane-ip-check | 60 +++++++++++++++++-- .../sane-scripts/src/sane-ip-check-upnp | 27 --------- 6 files changed, 89 insertions(+), 44 deletions(-) create mode 120000 pkgs/additional/sane-scripts/src/lib/sane_bt.py create mode 120000 pkgs/additional/sane-scripts/src/lib/sane_ssdp.py delete mode 100755 pkgs/additional/sane-scripts/src/sane-ip-check-upnp diff --git a/modules/services/dyn-dns.nix b/modules/services/dyn-dns.nix index 5708bd5a..89f0747f 100644 --- a/modules/services/dyn-dns.nix +++ b/modules/services/dyn-dns.nix @@ -6,8 +6,8 @@ let getIp = pkgs.writeShellScript "dyn-dns-query-wan" '' # preferred method and fallback # OPNsense router broadcasts its UPnP endpoint every 30s - timeout 60 ${pkgs.sane-scripts.ip-check-upnp}/bin/sane-ip-check-upnp || \ - ${pkgs.sane-scripts.ip-check}/bin/sane-ip-check + timeout 60 ${pkgs.sane-scripts.ip-check}/bin/sane-ip-check --json || \ + ${pkgs.sane-scripts.ip-check}/bin/sane-ip-check --json --no-upnp ''; in { @@ -24,6 +24,14 @@ in description = "where to store the latest WAN IPv4 address"; }; + upnpPath = mkOption { + default = "/var/lib/uninsane/upnp.txt"; + type = types.str; + description = '' + where to store the address of the UPNP device (if any) that can be used to create port forwards. + ''; + }; + ipCmd = mkOption { default = "${getIp}"; type = types.path; @@ -54,18 +62,33 @@ in wantedBy = cfg.restartOnChange; before = cfg.restartOnChange; - script = '' + script = let + jq = "${pkgs.jq}/bin/jq"; + sed = "${pkgs.gnused}/bin/sed"; + in '' mkdir -p "$(dirname '${cfg.ipPath}')" - newIp=$(${cfg.ipCmd}) + mkdir -p "$(dirname '${cfg.upnpPath}')" + newIpDetails=$(${cfg.ipCmd}) + newIp=$(echo "$newIpDetails" | ${jq} ".wan" | ${sed} 's/^"//' | ${sed} 's/"$//') + newUpnp=$(echo "$newIpDetails" | ${jq} ".upnp" | ${sed} 's/^"//' | ${sed} 's/"$//') oldIp=$(cat '${cfg.ipPath}' || true) + oldUpnp=$(cat '${cfg.upnpPath}' || true) + # systemd path units are triggered on any file write action, # regardless of content change. so only update the file if our IP *actually* changed - if [ "$newIp" != "$oldIp" ] + if [ "$newIp" != "$oldIp" -a -n "$newIp" ] then echo "$newIp" > '${cfg.ipPath}' echo "WAN ip changed $oldIp -> $newIp" fi - exit $(test -f '${cfg.ipPath}') + + if [ "$newUpnp" != "$oldUpnp" -a -n "$newUpnp" ] + then + echo "$newUpnp" > '${cfg.upnpPath}' + echo "UPNP changed $oldUpnp -> $newUpnp" + fi + + exit $(test -f '${cfg.ipPath}' -a '${cfg.upnpPath}') ''; }; diff --git a/pkgs/additional/sane-scripts/default.nix b/pkgs/additional/sane-scripts/default.nix index a2a4cb81..0c9af92d 100644 --- a/pkgs/additional/sane-scripts/default.nix +++ b/pkgs/additional/sane-scripts/default.nix @@ -86,16 +86,11 @@ let src = ./src; pkgs = [ "git" ]; }; - ip-check = static-nix-shell.mkBash { + ip-check = static-nix-shell.mkPython3Bin { pname = "sane-ip-check"; src = ./src; - pkgs = [ "curl" "gnugrep" ]; - }; - ip-check-upnp = static-nix-shell.mkPython3Bin { - pname = "sane-ip-check-upnp"; - src = ./src; pkgs = [ "miniupnpc" ]; - pyPkgs = [ "sane-lib.ssdp" ]; + pyPkgs = [ "requests" "sane-lib.ssdp" ]; }; ip-port-forward = static-nix-shell.mkPython3Bin { pname = "sane-ip-port-forward"; diff --git a/pkgs/additional/sane-scripts/src/lib/sane_bt.py b/pkgs/additional/sane-scripts/src/lib/sane_bt.py new file mode 120000 index 00000000..0d8ed276 --- /dev/null +++ b/pkgs/additional/sane-scripts/src/lib/sane_bt.py @@ -0,0 +1 @@ +bt/sane_bt.py \ No newline at end of file diff --git a/pkgs/additional/sane-scripts/src/lib/sane_ssdp.py b/pkgs/additional/sane-scripts/src/lib/sane_ssdp.py new file mode 120000 index 00000000..42dbabf1 --- /dev/null +++ b/pkgs/additional/sane-scripts/src/lib/sane_ssdp.py @@ -0,0 +1 @@ +ssdp/sane_ssdp.py \ No newline at end of file diff --git a/pkgs/additional/sane-scripts/src/sane-ip-check b/pkgs/additional/sane-scripts/src/sane-ip-check index 15c454ef..f20beb21 100755 --- a/pkgs/additional/sane-scripts/src/sane-ip-check +++ b/pkgs/additional/sane-scripts/src/sane-ip-check @@ -1,5 +1,57 @@ #!/usr/bin/env nix-shell -#!nix-shell -i bash -p curl -p gnugrep -ip=$(curl --silent https://ipinfo.io/ip) -echo "$ip" | grep -P " *^\d+\.\d+\.\d+\.\d+ *$" -exit $? +#!nix-shell -i python3 -p "python3.withPackages (ps: [ ps.requests ps.sane-lib.ssdp ])" -p miniupnpc + +# best to run this with an external timeout. e.g. +# - `timeout 60 sane-ip-check` + +import json +import logging +import requests +import subprocess +import sys + +from sane_ssdp import get_any_wan + +logger = logging.getLogger(__name__) + +def get_wan_fallback(): + "untrusted method in which to get the WAN IP" + r = requests.get("https://ipinfo.io/ip") + ip = r.text.strip() + if any(c not in "0123456789." for c in ip): + logging.warn("invalid IP from ipinfo.ip", ip) + return "" + else: + return ip + +if __name__ == '__main__': + logging.basicConfig() + + format = "plaintext" + try_upnp = True + + for arg in sys.argv[1:]: + if arg == "-v": + logging.getLogger().setLevel(logging.INFO) + elif arg == "-vv": + logging.getLogger().setLevel(logging.DEBUG) + elif arg == "--json": + format = "json" + elif arg == "--no-upnp": + try_upnp = False + else: + raise RuntimeError(f"invalid CLI argument {arg!r}") + + upnp_details = get_any_wan() if try_upnp else None + if upnp_details: + root_dev, _lan_ip, wan_ip = upnp_details + else: + root_dev, wan_ip = "", get_wan_fallback() + + if format == "plaintext": + print(wan_ip) + elif format == "json": + print(json.dumps(dict( + wan=wan_ip, + upnp=root_dev, + ))) diff --git a/pkgs/additional/sane-scripts/src/sane-ip-check-upnp b/pkgs/additional/sane-scripts/src/sane-ip-check-upnp deleted file mode 100755 index 9c45b815..00000000 --- a/pkgs/additional/sane-scripts/src/sane-ip-check-upnp +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env nix-shell -#!nix-shell -i python3 -p "python3.withPackages (ps: [ ps.sane-lib.ssdp ])" -p miniupnpc - -# best to run this with an external timeout. e.g. -# - `timeout 60 sane-ip-check-upnp` - -import logging -import os -import sys - -d = os.path.dirname(__file__) -sys.path.insert(0, d) - -from lib.sane_ssdp import get_any_wan - -if __name__ == '__main__': - logging.basicConfig() - - for arg in sys.argv[1:]: - if arg == "-v": - logging.getLogger().setLevel(logging.INFO) - elif arg == "-vv": - logging.getLogger().setLevel(logging.DEBUG) - else: - raise RuntimeError(f"invalid CLI argument {arg!r}") - _rootdev, _lan_ip, wan_ip = get_any_wan() - print(wan_ip)