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

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

Open
lunik1 opened this issue May 12, 2024 · 3 comments
Open

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

lunik1 opened this issue May 12, 2024 · 3 comments

Comments

@lunik1
Copy link

lunik1 commented May 12, 2024

I am running the latest head (7d576c4) 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
Copy link

henfri commented May 25, 2024

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

@henfri
Copy link

henfri commented May 25, 2024

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/

@joshcohen
Copy link

The error @henfri is because the Content-Length is not being calculated properly. I haven't gotten this to work, but here is a summary of what I found (including henfri's prior work):

  1. Correct API_URL and HTTP Method from GET to POST
#define CHECK(fn)       { rc = (fn); if (rc) goto cleanup; }

#define API_HOST "api.porkbun.com"
-#define API_URL "/client/v4"
+#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 = "GET " API_URL "/dns/retrieve/%s HTTP/1.0\r\n"    \
+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"                               \
@@ -36,7 +36,7 @@ static const char *PORKBUN_ZONE_ID_REQUEST = "GET " API_URL "/dns/retrieve/%s HT
       "Content-Length: %zd\r\n\r\n" \
       "{\"apikey\":\"%s\",\"secretapikey\":\"%s\"}";

-static const char *PORKBUN_HOSTNAME_ID_REQUEST_BY_NAME = "GET " API_URL "/dns/retrieve/%s HTTP/1.0\r\n"        \
+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"                               \
  1. Correct SUCCESS token case
@@ -123,8 +123,10 @@ static int check_success(const char *json, const jsmntok_t tokens[], const int n
        for (i = 1; i < num_tokens; i++) {
                int set;

-               if (jsoneq(json, tokens + i, "success") != 0)
+               if (jsoneq(json, tokens + i, "SUCCESS") != 0) {
  1. Correct calculation of Content-Length in setup API call. Originally, POST body length was set to len(api key), but the correct length is len({"apikey":"$API_KEY","secretapikey":,"$SECRET_API_KEY"}).
@@ -294,7 +304,7 @@ static int setup(ddns_t *ctx, ddns_info_t *info, ddns_alias_t *hostname)
                       PORKBUN_ZONE_ID_REQUEST,
                       zone_name,
                       info->user_agent,
-                      strlen(info->creds.username),
+                      strlen("{\"apikey\":\"") + strlen(info->creds.username) + strlen("\",\"secretapikey\":\"") + strlen(info->creds.password) + strlen("\"}"),
                       info->creds.username,
                       info->creds.password);

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

No branches or pull requests

3 participants