Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: Add Linode as a DNS provider #143

Closed
AnujRNair opened this issue Jan 16, 2021 · 24 comments · Fixed by #144
Closed

Feature request: Add Linode as a DNS provider #143

AnujRNair opened this issue Jan 16, 2021 · 24 comments · Fixed by #144
Assignees

Comments

@AnujRNair
Copy link

  1. What's the feature?
    Please add Linode as a DNS provider

  2. Extra information?

  • Within the Linode Cloud manager, create a personalAccessToken - it will be 64 chars. Assign it the domain permissions
  • Obtain your domainId and recordId for the A record you want to update. I did this by inspecting the network tab as I manually updated the record. They will both be integers
  • PUT request to update the record when your IP changes. Here is the JS to do so:
    const newIp = '127.0.0.1';
    const url = `https://api.linode.com/v4/domains/${LINODE_DOMAIN_ID}/records/${LINODE_RECORD_ID}`;
    const response = await fetch(url, {
      headers: {
        Authorization: `Bearer ${LINODE_PERSONAL_ACCESS_TOKEN}`,
        'Content-Type': 'application/json',
      },
      method: 'PUT',
      body: JSON.stringify({
        target: newIp,
      }),
    });

On success, a JSON response with the updated A record will be returned to you.

Thanks so much!

@qdm12
Copy link
Owner

qdm12 commented Jan 17, 2021

Hey there, can you please try with qmcgaw/ddns-updater:linode? The documentation is here, together with the readme.md.

I went the long route for some reason. You should just have to specify your domain, host and token. If you A/AAAA record does not exist, it even creates it automagically. It does a bunch of API calls here and there to find your domain ID and record ID from your domain name.

It's not tested though, so it might not work the first time 😉

@AnujRNair
Copy link
Author

Thanks for the quick add!

I deployed the image and it properly detected my current settings, which is great!
I haven't tried deploying the image when no entry exists to see if it properly adds a new entry, and my current IP has yet to expire so I have yet to test the update, but so far so good.

I can force changes in my config if you'd like to test these 2 use cases before merging in?

Thanks again!

@qdm12
Copy link
Owner

qdm12 commented Jan 18, 2021

I can force changes in my config if you'd like to test these 2 use cases before merging in?

Yes that would be appreciated! 🎖️ And also thanks for the PR review!

my current IP has yet to expire so I have yet to test the update, but so far so good.

You can set your A record to 127.0.0.1 on the web ui and see if the updater picks up the difference. Give it a few minutes eventually for your web ui changes to propagate.

And you can remove the A record and see if the updater creates it on its update period. But don't worry too much if that's troublesome, we can just 'assume' it works and someone can create an issue if it doesn't. It's also related to #129 which is not supported for all the other DNS for now.

@AnujRNair
Copy link
Author

AnujRNair commented Jan 18, 2021

I updated my A record in the Linode manager to point to 127.0.01, and an hour later, the record still hasn't been updated back.

Config from docker compose:

  ddns-updater:
    container_name: ddns
    image: qmcgaw/ddns-updater:linode
    restart: unless-stopped
    networks:
      - default
    ports:
      - 8082:8082/tcp
    volumes:
      - ${CONFIG}/config/ddns/data:/updater/data
    environment:
      - CONFIG={"settings":[{"provider":"linode","domain":"${LINODE_DOMAIN}","host":"${LINODE_HOST}","token":"${LINODE_TOKEN}","ip_version":"ipv4"}]}
      - PERIOD=15m
      - UPDATE_COOLDOWN_PERIOD=10m
      - IP_METHOD=cycle
      - IPV4_METHOD=cycle
      - IPV6_METHOD=cycle
      - HTTP_TIMEOUT=30s
      
      # Web UI
      - LISTENING_PORT=8082
      - ROOT_URL=/

Logs:

🔧  Need help? https://github.com/qdm12/ddns-updater/issues/new
💻  Email? [email protected]
☕  Slack? Join from the Slack button on Github
💸  Help me? https://github.com/sponsors/qdm12
2021-01-17T11:57:08.814-0800	INFO	Found single setting to update record
2021-01-17T11:57:09.356-0800	INFO	Reading history from database: domain ***.com host ***
2021-01-17T11:57:09.357-0800	INFO	healthcheck server: listening on 127.0.0.1:9999
2021-01-17T11:57:09.358-0800	INFO	http server: listening on 0.0.0.0:8082
2021-01-17T11:57:09.359-0800	INFO	backup: disabled
2021-01-17T11:58:56.170-0800	INFO	http server: HTTP GET /
2021-01-17T11:58:56.288-0800	INFO	http server: HTTP GET /favicon.ico
2021-01-17T11:59:04.209-0800	INFO	http server: HTTP GET /
2021-01-17T11:59:04.320-0800	INFO	http server: HTTP GET /favicon.ico
2021-01-17T16:58:52.268-0800	INFO	http server: HTTP GET /
2021-01-17T16:58:52.539-0800	INFO	http server: HTTP GET /favicon.ico
2021-01-17T18:48:27.682-0800	INFO	http server: HTTP GET /
2021-01-17T18:48:27.758-0800	INFO	http server: HTTP GET /favicon.ico
2021-01-17T18:48:41.919-0800	INFO	http server: HTTP GET /
2021-01-17T18:48:41.971-0800	INFO	http server: HTTP GET /favicon.ico

Any more info you need from me?

@qdm12
Copy link
Owner

qdm12 commented Jan 18, 2021

🤔 Strange what do you get with nslookup yourdomain.com? Is it the same IP address you set on the UI? It's strange it doesn't show any error. And I presume the web ui says it succeeded?

@AnujRNair
Copy link
Author

Just saw a new error come through on the dashboard:

Failure (cannot get domain ID: cannot unmarshal update response: json: cannot unmarshal object into Go value of type []struct { ID *int "json:\"id,omitempty\""; Type string "json:\"type\""; Status string "json:\"status\"" }), 7m37s ago

@qdm12
Copy link
Owner

qdm12 commented Jan 18, 2021

That error totally makes sense, silly me. By the way is the error not showing the Docker logs as well? 🤔

Anyway, I just pushed a fix in 7aac7ef the image is now updated (only for amd64 let me know if you need another CPU arch). You can pull and retry, thanks for testing!

@AnujRNair
Copy link
Author

AnujRNair commented Jan 18, 2021

Yup, that's updating the A record properly now, thanks! It's not :( see below

I did eventually get errors in the docker log, but one of the values was off:

WARN	unhealthy: [domain: ***.com | host: *** | provider: luadns | ip: ipv4]: failure (cannot get domain ID ...

The provider is listed as luadns even though I've set linode as my provider

@AnujRNair
Copy link
Author

Ahh, actually the DDNS UI is saying that the A record has been updated, but the record is not actually updated in Linode

2021-01-17T22:29:08.418-0800	INFO	IPv4 address of *** is 127.0.0.1 and your IPv4 address is ***
2021-01-17T22:29:08.485-0800	INFO	Updating record [domain: ***.com | host: *** | provider: luadns | ip: ipv4] to use ***

Maybe because the provider is luadns?

@AnujRNair
Copy link
Author

Sorry, one more thing I spotted - I set my config in docker-compose to check my IP address every 15 mins, but it looks like it is being checked every 1 min?

2021-01-17T22:22:08.615-0800	WARN	unhealthy: lookup IP addresses for ...
2021-01-17T22:23:08.735-0800	WARN	unhealthy: lookup IP addresses for ...
2021-01-17T22:24:08.810-0800	WARN	unhealthy: lookup IP addresses for ...
2021-01-17T22:25:08.935-0800	WARN	unhealthy: lookup IP addresses for ...
2021-01-17T22:26:09.451-0800	WARN	unhealthy: lookup IP addresses for ...
2021-01-17T22:27:10.359-0800	WARN	unhealthy: lookup IP addresses for ...
2021-01-17T22:28:11.070-0800	WARN	unhealthy: lookup IP addresses for ...

My docker-compose config is in a comment above

After attempting to set the new IP, I also didn't get a cool down of 10m as set in my config above

@qdm12
Copy link
Owner

qdm12 commented Jan 18, 2021

The provider is listed as luadns even though I've set linode as my provider

That's just an error in the display as you pointed out in the code review 😉 I fixed it.

but it looks like it is being checked every 1 min

That is just the healthcheck doing:

  1. a DNS lookup of your host+domain and comparing against the IP address it 'thinks' was last successfully updated.
  2. Checking the program to see if any previous update failed

It doesn't do an update. Although now that you're saying it, making it quieter would be an idea too.

but the record is not actually updated in Linode

I pushed some additional commits which:

  • check the IP received in the update response matches the IP sent for update to Linode
  • Log some debug information to know what step fails (get domain id, get record id, create record/update record)

Please docker pull qmcgaw/ddns-updater:linode again and let me know at what step if fails, thanks!

@AnujRNair
Copy link
Author

I realized this morning what happened - the A record for my main domain got updated instead of the sub domain that I wanted to be updated

I have a domain entry for example.com (domain id 123)

Within this entry I have A records for example.com (record id 456) and (subdomain.example.com ( record id 789)

The record id for 456 was updated when 789 should have been updated!

@qdm12
Copy link
Owner

qdm12 commented Jan 18, 2021

Oh ok, I think I might had forgotten to filter records with the host in my code... God I need more unit testing but my weekend coding is lazy 😄 I'll fix it this evening, thanks for your patience!

@AnujRNair
Copy link
Author

AnujRNair commented Jan 18, 2021

Thanks!

Im also curious:

a DNS lookup of your host+domain and comparing against the IP address it 'thinks' was last successfully updated.

Why do the lookup if we're not going to act on this info? Couldn't we limit the lookup to once every "x configured" minutes and act on it then if something has changed?

@qdm12
Copy link
Owner

qdm12 commented Jan 19, 2021

Alright it's now fixed, indeed I forgot to filter on the host.

About the healthcheck, it only:

  1. checks the program internal state for a failed update try
  2. DNS looks up your domain and compares it with its last IP in the program state

And these two operations are 'free' (= you won't get banned), so it runs every 60 seconds.

The 'update' mechanism related with the PERIOD is not free and does:

  1. Get your public IP address from an external website (=rate limited, doing so too quickly will ban you)
  2. Resolve your domain name to an IP address (*or use the last IP stored in the state if it's proxied)
  3. Compare the two. If there is a mismatch, an update is needed... so it does an update.

Now if the healthcheck fails, that's essentially to let the user know something's wrong with for example the authentication etc. You can also configure your container to restart when being unhealthy for example.

On the other hand, I removed the unhealthy: ... logs as you're right, they don't add anything really and spam the logs. One can just check the Docker healthcheck status to know what's wrong. And we can't really do something if it's unhealthy to avoid getting banned for trying too many times.

@AnujRNair
Copy link
Author

ok that makes sense, thanks for the explanation!

Just tested the updater, and the IP updater is now working as expected. I changed the IP on Linode to 127.0.0.1, recreated the ddns container, and it correctly updated it to my current IP address, with the correct logs in docker logs

However, when I stopped the container, removed my A record and started the container again, it didn't create the A record from scratch. Nothing in docker logs.

Maybe because of the cooldown period? Or maybe the DNS has been cached. Either way, I'll leave the container running for another hour to see if the A record is recreated.

On another minor minor note, the docker logs have a variable in them - not sure if it's because this is a separate build away from master:

Running version linode built on {{ steps.vars.outputs.build_date }} (commit f642a3f)

@AnujRNair
Copy link
Author

AnujRNair commented Jan 19, 2021

2021-01-19T10:44:40.891-0800	INFO	IPv4 address of ***.***.com is 127.0.0.1 and your IPv4 address is ***.***.***.***
2021-01-19T10:44:40.891-0800	INFO	Updating record [domain: ***.com | host: *** | provider: linode | ip: ipv4] to use ***.***.***.***
DEBUG: Getting domain ID...
DEBUG: Getting record ID for domain ID ***
DEBUG: Record ID not found, creating it...

Looks like it detects that the record is missing, and tries creating it, but nothing has appeared in my admin panel. No other logs underneath either. I'm also not sure where the 127.0.0.1 has come from, since the record doesn't exist at all - maybe a default value?

On the ddns webpage, the status has been set as SUCCESS (changed to ***), some time ago

@qdm12
Copy link
Owner

qdm12 commented Jan 19, 2021

it didn't create the A record from scratch. Nothing in docker logs.

Normally it doesn't log anything except if there is an error or warning. However, for now I just check we get a HTTP 200 OK status code. I pushed some commits to do additional response processing to check it actually succeeded, maybe the status code check is not enough. Can you try pulling, running and checking the logs again? I also added a debug log with parts of the response received when a record is created. Thanks!

Or maybe the DNS has been cached

Yes that could be why you still see 127.0.0.1 as it takes a few minutes for the changes to propagate on all DNS servers. It could also be a default value, although that's more unlikely.

On another minor minor note, the docker logs have a variable in them

Well that is definitely useful, I already had that mistake in 4 repositories and was about to add it to like 10 more 😄 Thanks!!

@AnujRNair
Copy link
Author

2021-01-19T15:29:08.497-0800	ERROR	cannot create record: malformed IP address received:

This is the new line I see in my logs now!

@qdm12
Copy link
Owner

qdm12 commented Jan 20, 2021

Can yoi the try again? Newer image should print the json received 🤔

@AnujRNair
Copy link
Author

AnujRNair commented Jan 20, 2021

{
  "data": [
    {
      "id": 333,
      "type": "A",
      "name": "",
      "target": "1.2.3.4",
      "priority": 0,
      "weight": 0,
      "port": 0,
      "service": null,
      "protocol": null,
      "ttl_sec": 0,
      "tag": null,
      "created": "2014-03-01T21:31:50",
      "updated": "2021-01-18T07:15:55"
    },
    {
      "id": 888,
      "type": "CNAME",
      "name": "*",
      "target": "***.com",
      "priority": 0,
      "weight": 0,
      "port": 0,
      "service": null,
      "protocol": null,
      "ttl_sec": 0,
      "tag": null,
      "created": "2021-01-11T05:57:09",
      "updated": "2021-01-11T05:57:09"
    },
    {
      "id": 999,
      "type": "A",
      "name": "***",
      "target": "5.6.7.8",
      "priority": 0,
      "weight": 0,
      "port": 0,
      "service": null,
      "protocol": null,
      "ttl_sec": 0,
      "tag": null,
      "created": "2014-03-01T21:31:50",
      "updated": "2014-03-01T21:31:50"
    },
    {
      "id": 101010,
      "type": "A",
      "name": "***",
      "target": "9.0.1.2",
      "priority": 0,
      "weight": 0,
      "port": 0,
      "service": null,
      "protocol": null,
      "ttl_sec": 0,
      "tag": null,
      "created": "2020-01-13T01:34:00",
      "updated": "2021-01-11T05:17:19"
    },
    {
      "id": 111,
      "type": "A",
      "name": "***",
      "target": "3.4.5.6",
      "priority": 0,
      "weight": 0,
      "port": 0,
      "service": null,
      "protocol": null,
      "ttl_sec": 0,
      "tag": null,
      "created": "2014-03-01T21:31:50",
      "updated": "2014-03-01T21:31:50"
    }
  ],
  "page": 1,
  "pages": 1,
  "results": 5
}

There were a few other MX and TXT records which I've removed.
I've replaced any real domain with *** and any numbers with incrementing numbers (i.e. 216.58.195.78 with 1.2.3.4 etc.)

@qdm12
Copy link
Owner

qdm12 commented Jan 20, 2021

Oh god, silly me... That's what happens when you copy pasta! Should be fixed now I think.

@AnujRNair
Copy link
Author

That did it! A record was created from scratch and the correct JSON payload was received back :)
I think that concludes all of the possible combos of how to create and update the A Record - thanks for adding Linode as a provider!

@qdm12
Copy link
Owner

qdm12 commented Jan 20, 2021

Great! Merging in master / :latest. I'll do another release / Docker tag too for it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants