nrdxp / cfdyndns

CloudFlare Dynamic DNS Client
MIT License
12 stars 12 forks source link

bug: missing field `multiple_railguns_allowed` #57

Closed diogotcorreia closed 2 months ago

diogotcorreia commented 3 months ago
May 28 21:05:05 bro systemd[1]: Started CloudFlare Dynamic DNS Client.
░░ Subject: A start job for unit cfdyndns.service has finished successfully
░░ Defined-By: systemd
░░ Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
░░
░░ A start job for unit cfdyndns.service has finished successfully.
░░
░░ The job identifier is 1078646.
May 28 21:05:06 bro cfdyndns-start[212683]:  INFO  cfdyndns::ip > XXX.XXX.XXX.XXX
May 28 21:05:07 bro cfdyndns-start[212683]: Error: error decoding response body: missing field `multiple_railguns_allowed` at line 1 column 578
May 28 21:05:07 bro systemd[1]: cfdyndns.service: Main process exited, code=exited, status=1/FAILURE

Seems like a change in Cloudflare's API?

Config: https://github.com/diogotcorreia/dotfiles/blob/bf6f0ee0fbf956d0983308f7270d7b7698e8d57d/hosts/bro/default.nix#L76-L80 Keep in mind that I'm using a patch from #56 as well.

cc @nrdxp

ToyVo commented 3 months ago

After encountering this issue, I've ended up switching to a systemd service and I figured I would share, I've made it so that I do not need to manually find the zone and record ids like other example code I found does. Note the customized parts of the domains, the interface (enp2s0), and the api token, which has edit dns permissions on all zones and read permissions on zones.

systemd.services.cfdyndns = {
  serviceConfig.Type = "oneshot";
  after = [ "network.target" ];
  path = with pkgs; [curl iproute2 gawk dig jq];
  script = ''
    declare -a DOMAINS=(
      "*.diekvoss.net"
      "mc.toyvo.dev"
    )
    TOKEN=${builtins.readFile  ./cfapitoken}

    function put_record() {
      curl -sS -X PUT \
        -H "Content-Type: application/json" \
        -H "Authorization: Bearer $TOKEN" \
        -d "{\"type\":\"A\",\"name\":\"$3\",\"content\":\"$4\",\"ttl\":1,\"proxied\":false}" \
        "https://api.cloudflare.com/client/v4/zones/$1/dns_records/$2"
    }

    function get_ip() {
      curl -sS \
         -H "Content-Type: application/json" \
         -H "Authorization: Bearer $TOKEN" \
         https://api.cloudflare.com/client/v4/zones/$1/dns_records/$2 | jq -r '.result.content'
    }

    function get_zone() {
      curl -sS \
         -H "Content-Type: application/json" \
         -H "Authorization: Bearer $TOKEN" \
         https://api.cloudflare.com/client/v4/zones?name=$1 | jq -r '.result.[].id'
    }

    function get_record() {
      curl -sS \
         -H "Content-Type: application/json" \
         -H "Authorization: Bearer $TOKEN" \
         https://api.cloudflare.com/client/v4/zones/$1/dns_records?name=$2 | jq -r '.result.[].id'
    }

    NEW_IP=$(ip addr show dev enp2s0 | awk '/inet / {print $2}' | cut -d '/' -f1)
    echo "The IP Address of this machine is $NEW_IP"
    for DOMAIN in "''${DOMAINS[@]}"
    do
        CURRENT_IP=$(dig +short $DOMAIN)
        echo "DNS for $DOMAIN is currently set to $CURRENT_IP"
        if [ "$CURRENT_IP" != "$NEW_IP" ]; then
          echo "DNS for $DOMAIN Doesn't point to $NEW_IP, checking for confirmation..."
          BASE_DOMAIN=$(awk -F'.' '{gsub(/^\*\./, ""); print $(NF-1) "." $NF}' <<< "$DOMAIN")
          echo "Base for $DOMAIN is $BASE_DOMAIN"
          ZONE=$(get_zone "$BASE_DOMAIN")
          echo "Zone ID for $BASE_DOMAIN is $ZONE"
          RECORD=$(get_record "$ZONE" "$DOMAIN")
          echo "Record ID for $DOMAIN is $RECORD"
          CONFIRM_IP=$(get_ip "$ZONE" "$RECORD")
          echo "DNS for $DOMAIN is confirmed set to $CONFIRM_IP"
          if [ "$CONFIRM_IP" != "$NEW_IP" ]; then
            echo "Updating DNS record for $DOMAIN to $NEW_IP"
            put_record "$ZONE" "$RECORD" "$DOMAIN" "$NEW_IP"
          else
            echo "DNS record for $DOMAIN is already set to $NEW_IP, skipping update. Assuming TTL."
          fi
        else
          echo "DNS record for $DOMAIN is $NEW_IP, skipping update."
        fi
    done
  '';
  startAt = "*:0/5";
};

Note I did not design this to handle 2tld like .co.uk etc

diogotcorreia commented 3 months ago

This is related to https://github.com/cloudflare/cloudflare-rs/issues/236 Doesn't seem fixable until the upstream crate is fixed :/

nrdxp commented 3 months ago

It sounds as simple as removing a field. I'll take a look upstream later it should be quite a simple crate patch that we can apply quickly :crossed_fingers:

diogotcorreia commented 3 months ago

I ended up doing the same as @ToyVo and created a bash script as a replacement to cfdyndns (and it does less requests since it's not restricted by cloudflare crate's terrible design). Mine should support all kinds of TLDs and domains, and it gets the public IP through DNS (using Cloudflare's 1.1.1.1), meaning the machine can be behind NAT. It also uses systemd credentials instead of putting the token in the world-readable nix store (like the module of this project in nixpkgs).

If anyone wants to copy it: https://github.com/diogotcorreia/dotfiles/commit/7d586119e8fb05fac06f431c1fd8288df2d7d457

I appreciate all the work @nrdxp has done for this project, but it seems cloudflare's crate is giving some headaches at the moment :sweat_smile: Hope it works again at some point :crossed_fingers:

Wyn-Price commented 3 months ago

I've made a PR to fix this (https://github.com/nrdxp/cfdyndns/pull/60), and a PR on the upstream cloudflare-rs (https://github.com/cloudflare/cloudflare-rs/pull/240), although considering they haven't yet merged https://github.com/cloudflare/cloudflare-rs/pull/232 yet I don't have high hopes that mine will be merged anytime soon. I'm happy to update the nixpkgs when this gets merged, unless anybody else wants to.

For now, you can override the source to point to my repo, as shown in https://nixos.wiki/wiki/Overlays#Overriding_a_version

ToyVo commented 3 months ago

It also uses systemd credentials instead of putting the token in the world-readable nix store

Haha yeah I really need to put in the work for sops