troglobit / inadyn

In-a-Dyn is a dynamic DNS client with multiple SSL/TLS library support
https://troglobit.com/projects/inadyn/
GNU General Public License v2.0
930 stars 139 forks source link

pokbun API does not work, leaks secret key in debug logs #483

Open lunik1 opened 2 months ago

lunik1 commented 2 months ago

I am running the latest head (7d576c4d00d312597c2b9c06c00529d2dde5ac89) and trying to update the DNS records of a domain whose DNS is managed by porkbun.

This config fails to update the DNS records:

provider porkbun.com {
    username = <apikey>
    secretapikey = <secretapikey>
    hostname = <my_hostname>
}

Debug log error:

HTTP 404: Unexpected status code.
Zone '<my_hostname>' not found.
Error response from DDNS server, exiting!
Error code 48: DDNS server response not OK

After looking into the code there seem to be a few issues with it:

Firstly, the example configuration seems to be wrong

provider porkbun.com {
    checkip-server = checkip.porkbun.com
    username = zone.com
    password = api_token #Create a unique custom api token with the following permissions: Zone.Zone - Read, Zone.DNS - Edit.
    hostname = yourhostname.yourdomain.com
    ttl = 400 #optional, by default is 300 seconds
}

When an API token is created with porkbun there does not seem to be a way to restrict its permissions. Also, an API token consists of both an API key and a Secret API Key: in the code the username and password are used for these respectively, but this is not reflected in the example. I have tried setting the username to my domain name, as in the example, but I get the same issue.

The code seems to use the wrong url for the porkbun API: api.porkbun.com/client/v4. The official documentation uses porkbun.com/api/json/v3/ - there is no API v4 as far as I can make out. I suspect this is the cause of the issue as this api.porkbun.com/client/v4 address does not seem to work using curl in the command line while porkbun.com/api/json/v3/ does (although I haven't been able to reproduce exactly, I get stuck in a redirect loop rather than hitting a 404).

Finally, with debug logging enabled, the code will print the secret API key when making a request. This should be censored or there should be a warning that debug logging can contain sensitive information.

henfri commented 1 month ago

Thanks for the Analysis. Have you tried to Change the endpoint URL in porkbun.c?

henfri commented 1 month ago

Well, I have tried it now. It is not that simple:

GET /api/json/v3/dns/retrieve/x.name HTTP/1.0
Host: porkbun.com
User-Agent: inadyn/2.12.0 https://github.com/troglobit/inadyn/issues
Accept: */*
Content-Type: application/json
Content-Length: 68

inadyn[3046142]: Successfully sent HTTPS request!
inadyn[3046142]: Successfully received HTTPS response (912/8191 bytes)!
inadyn[3046142]: Response:
HTTP/1.1 400 Method Not Allowed
Date: Sat, 25 May 2024 07:18:17 GMT
Content-Type: application/json
Connection: close
Server: openresty
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload

{"status":"ERROR","message":"All HTTP request must use POST."}

Changing it to GET in porkbun.c gives me {"status":"ERROR","message":"All API requests must provide minimal required data."} Which is pretty odd, as the only data required is the api-keys - which are provided apparently in the json.

Here my changes up to now: -the first two lines -post instead of get

#define API_HOST "porkbun.com"
#define API_URL "/api/json/v3"

/* https://kb.porkbun.com/article/190-getting-started-with-the-porkbun-api */
static const char *PORKBUN_ZONE_ID_REQUEST = "POST " API_URL "/dns/retrieve/%s HTTP/1.0\r\n"    \
        "Host: " API_HOST "\r\n"                \
        "User-Agent: %s\r\n"                    \
        "Accept: */*\r\n"                               \
        "Content-Type: application/json\r\n" \
        "Content-Length: %zd\r\n\r\n" \
        "{\"apikey\":\"%s\",\"secretapikey\":\"%s\"}";

static const char *PORKBUN_HOSTNAME_ID_REQUEST_BY_NAME  = "POST " API_URL "/dns/retrieve/%s HTTP/1.0\r\n"       \
        "Host: " API_HOST "\r\n"                \
        "User-Agent: %s\r\n"                    \
        "Accept: */*\r\n"                               \
        "Content-Type: application/json\r\n" \
        "Content-Length: %zd\r\n\r\n" \
        "{\"apikey\":\"%s\",\"secretapikey\":\"%s\"}";

The complete file https://paste.debian.net/hidden/1c957794/