0

I always getting the same error (Create domain 'example.com' failed) (or any domain) when trying to create new DNS zone with Powerdns API call.

My request:

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,'http://127.0.0.1:953/api/v1/servers/localhost/zones');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\"name\":\"example.com.\", \"kind\":\"Native\", \"masters\": [], \"nameservers\":[\"ns1.example.com.\", \"ns2.example.com.\"]}");
$headers = array();
$headers[] = 'X-Api-Key: MY-KEY';
$headers[] = 'Accept: application/json';
$headers[] = 'Content-Type: application/json';
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$result = curl_exec($ch);
if (curl_errno($ch)) {
    echo 'Error:' . curl_error($ch);
}
curl_close($ch);

Similar api call to get a list of DNS zones is working. GET request work properly but not POST. Can anyone help, please?

My pdns.conf file is:

api=yes
api-key=MY-KEY

Maybe i have to change something in settings of my powerdns, idk really. I would be very grateful for any help!

Server response after POST request is 422 error (Unprocessable Entity)

HTTP/1.1 422 Unprocessable Entity Access-Control-Allow-Origin: * Connection: close Content-Length: 52 Content-Security-Policy: default-src 'self'; style-src 'self' 'unsafe-inline' Content-Type: application/json Server: PowerDNS/4.4.1 X-Content-Type-Options: nosniff X-Frame-Options: deny X-Permitted-Cross-Domain-Policies: none X-Xss-Protection: 1; mode=block

This is my pdns.conf file
cat /etc/pdns/pdns.conf
bind-ignore-broken-records=yes
setuid=named
setgid=named
launch=bind
log-dns-queries=yes
loglevel=5
bind-config=/etc/named.conf
bind-dnssec-db=/var/cpanel/pdns/dnssec.db
local-address-nonexist-fail=no
distributor-threads=1
disable-axfr=yes
webserver=yes
api=yes
webserver-address=127.0.0.1
webserver-allow-from=0.0.0.0/0
webserver-password=SERVER-KEY
#gmysql-dnssec=no
webserver-port=953
api-key=MY-KEY
upgrade-unknown-types=1
Emin4ik
  • 3
  • 5
  • Did you look at your server logfiles, as they may give more information on what was wrong? – Patrick Mevzek Sep 05 '22 at 17:19
  • Yes it is return 422 error myserver pdns_server[138302]: [webserver] affd4b0a-67ab-4d57-84ec-112a017a3002 127.0.0.1:46800 "POST /api/v1/servers/localhost/zones HTTP/1.1" 422 420 Something like this @PatrickMevzek "Unprocessable Entity" Error 422 – Emin4ik Sep 05 '22 at 17:29
  • 422 means the input is not accepted because malformed. I would take inspiration from https://stackoverflow.com/a/60304664/6368697 on how to properly pass the JSON structure and in a far simpler way with `json_encode` otherwise manually you need to encode part of the content. The content-type header is also probably mandatory. Otherwise I would recommend you use GET to retrieve current zones (create one manually outside of the API) so that you have clear examples of structures to use, and you can reuse them during POST. – Patrick Mevzek Sep 05 '22 at 19:00
  • I don't have any way to test this, but I assume the problem is because you forgot to include the `Content-Type` request header. – Quentin Sep 05 '22 at 19:56
  • Logging the response body may provide a more specific error message. – Quentin Sep 05 '22 at 19:58
  • @Quentin only if the body is the request post data. Content-Type: is a response header. Accept: would be the correct request header. You should have read the documentation you gave me. It says to use the Accept: request header. – Misunderstood Sep 05 '22 at 22:04
  • The 422 HTTP Status code is because the API did not get the data fields. From the API manual: `Input validation failed: 422 Unprocessable Entity` – Misunderstood Sep 05 '22 at 22:17
  • @Misunderstood — `Content-Type` is both a request header and a response header. `Accept` is the header to tell the server what the client will support in the response. When you're making a POST request then you should specify what format the request body is in using the `Content-Type` request header. [Here's an example on MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type#examples). – Quentin Sep 05 '22 at 23:22
  • @Quentin I was mistaken about the type of request and how the POSTFIELDS becomes the body. I had a test setup and had a hard time receiving the post data with a json Content-Type. The $_POST was empty. As was $_FILES. But I noticed the Content-Length remained the same. It was just a matter of finding where it was. I revised my answer. On one hand I was correct about the post data being an array. But in this case the post data is not really post data. – Misunderstood Sep 06 '22 at 01:25

2 Answers2

0

Your code looks correct.

One big problem with your response.
Content-Length: 52
It should be Content-Length: 111.

I have been testing your code with two apps
One to send the curl request
One to receive it and respond with the request details.

Early on in my testing when I removed the Content-Type: application/json, I would see a content length of just over 50 bytes.

Below is my app. It gets 111 bytes.
If you change your curl url to http://eatled.com/receiveheader.php
You should get this:

Response
Content-Length: 111
Content-Type: application/json
Accept: application/json
Accept-Encoding: deflate, gzip, br
Host: eatled.com
X-Api-Key: MY-KEY

BODY

{"name":"example.com.", "kind":"Native", "masters": [], "nameservers":["ns1.example.com.", "ns2.example.com."]}


array (
  'name' => 'example.com.',
  'kind' => 'Native',
  'masters' => 
  array (
  ),
  'nameservers' => 
  array (
    0 => 'ns1.example.com.',
    1 => 'ns2.example.com.',
  ),
)

LINK TO MY CURL SANDBOX

The source code:
sandbox.php

<?php
header('Content-Type: text/plain; charset=UTF-8');
$jsn = "{\"name\":\"example.com.\", \"kind\":\"Native\", \"masters\": [], \"nameservers\":[\"ns1.example.com.\", \"ns2.example.com.\"]}";
$data = json_decode($post,1);
$post = $jsn;
$request = array();
$request[] = "Content-Type: application/json";
$request[] = "Accept: application/json";
$request[] = "X-Api-Key: MY-KEY";
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPHEADER, $request);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);    
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);   
curl_setopt($ch, CURLOPT_URL, 'http://eatled.com/receiveheader.php'); 
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_TIMEOUT,10);
curl_setopt($ch, CURLOPT_FAILONERROR,true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_ENCODING,"");
curl_setopt($ch, CURLOPT_VERBOSE, true);
curl_setopt($ch, CURLOPT_POSTFIELDS,$post);
$data = curl_exec($ch);
$info = rawurldecode(var_export(curl_getinfo($ch),true));
echo "\nResponse\n$data";
echo "\ncurl_getinfo =\n$info";
?>

receiveheaders.php

<?php
header('Content-Type: text/plain; charset=UTF-8');

foreach (getallheaders() as $name => $value) {
    echo "$name: $value\n";
}
echo "\nBODY\n";
$jsn = file_get_contents('php://input');
echo "\n$jsn\n\n\n";
$data = json_decode($jsn,1);
var_export($data);
echo "\n\$_POST\n";
var_export($_POST);
echo "\n\$_FILES\n";
var_export($_FILES);
echo "\n\$_SERVER\n";
var_export($_SERVER);
?>

END OF UPDATE



Add these two headers.

$headers[] = 'Accept: application/json';
$headers[] = 'Content-Type: application/json';

Right now the error you are getting is because the request content-type is

'CONTENT_TYPE' => 'application/x-www-form-urlencoded',

If use just used the content-type header and not accept, you'd get a different error. The API manual says you cannot use curl's default Accept: */*

Misunderstood
  • 5,534
  • 1
  • 18
  • 25
  • Thanks for your reply @Misunderstood. But this didn`t solve the problem I still get the same error. Output is: HTTP/1.1 422 Unprocessable Entity Access-Control-Allow-Origin: * Connection: close Content-Length: 52 Content-Security-Policy: default-src 'self'; style-src 'self' 'unsafe-inline' Content-Type: application/json Server: PowerDNS/4.4.1 X-Content-Type-Options: nosniff X-Frame-Options: deny X-Permitted-Cross-Domain-Policies: none X-Xss-Protection: 1; mode=block {"error": "Creating domain 'mexamples.com.' failed"} – Emin4ik Sep 06 '22 at 05:11
  • When i use PDNS library from Github i`m getting the same error (exonet/powerdns-php) $powerdns = new Powerdns('127.0.0.1', 'MY_KEY'); $zone = $powerdns->createZone( 'example.com', ['ns1.example.com.', 'ns2.example.com.'] ); – Emin4ik Sep 06 '22 at 05:40
  • About Content-length. When i`m adding $headers[] = 'Accept: application/json'; in request headers there is only $headers[] = 'Content-Type: application/json'; and i think that`s why Content-Length: 52 after that i tried directly on server send curl how posted @N.Kaewkhat and it return COntent-Length: 111 but with the same error 422. Thank u @Misunderstood or spending your time on me – Emin4ik Sep 06 '22 at 18:17
  • Trying 127.0.0.1:953... * TCP_NODELAY set * Connected to 127.0.0.1 (127.0.0.1) port 953 (#0) > POST /api/v1/servers/localhost/zones HTTP/1.1 > Host: 127.0.0.1:953 > User-Agent: curl/7.68.0 > Content-Type: application/json > X-Api-Key: MY-KEY > Accept: application/json > Content-Length: 111 – Emin4ik Sep 06 '22 at 18:19
  • It appears the curl is working. So I'd guess there is something wrong with the parameters passed in the JSON. Did a search and only found one example of creating a zone. That example also had records. https://jpmens.net/2015/01/09/a-look-at-the-powerdns-rest-api/ – Misunderstood Sep 06 '22 at 23:56
  • 1
    Yes, tried this one too with no success :-( Anyway thank u very much for your time. I'll let know if something works out. @Misunderstood – Emin4ik Sep 07 '22 at 10:25
  • How do you think if i will create DNS zones from WHM(cPanel) API, Will it be the same if i create with PowerDNS API? – Emin4ik Sep 09 '22 at 05:48
  • If you understand DNS zones well enough to use WHM then yes, use WHM. I am a big fan of WHM and cPanel. Many years ago I tried to comprehend DNS Zones. That was not a viable effort. I hired someone to take care of that for me. I think you are a smart guy and WHM will work well for you. – Misunderstood Sep 09 '22 at 06:53
-1

Can you try call the API using curl command on the maching running pDNS webserver?

curl -v \
 -H 'Content-Type: application/json' \
 -H 'X-Api-Key: MY-KEY' \
 -H 'Accept: application/json' \
 -X POST -d '{"name":"example.com.", "kind":"Native", "masters": [], "nameservers":["ns1.example.com.", "ns2.example.com."]}' \
 http://127.0.0.1:953/api/v1/servers/localhost/zones

Because I don't know your setup, so I guess that it had something relate to network. If curl request work, you should check webserver-allow-from configuration, you may try to set it to accept from all (0.0.0.0,::).

N.Kaewkhat
  • 19
  • 3
  • Tried it on server the same error HTTP/1.1 422 Unprocessable Entity Now will try to change accept from all to 0.0.0.0,:: – Emin4ik Sep 06 '22 at 18:11
  • The same error on the server with webserver-allow-from = 0.0.0.0/0 . I really don`t understand why GET works but not POST. I also have WHM(cPanel) on this server and can create DNS zones with his interface with no problem. What wrong with Powerdns? – Emin4ik Sep 06 '22 at 18:41