DNS API Tutorial
The DNS API provides a REST interface for managing the records in DNS zones (domains) hosted by Mythic Beasts.
Getting started
-
Create an API key using the customer control panel, and ensure that it has access rights for the DNS API. These can be granted for all zones on your account, for specific zones, or for individual records.
-
For this tutorial, we will use the
curl
command line utility. To make this easier, store the credentials in a.netrc
file in your home directory. These will allow you to usecurl -n
to do HTTP Basic Authentication on each request. Your.netrc
file should look something like this (with the key ID as the login and the secret as the password):
machine api.mythic-beasts.com
login abcd1234
password efgh56778
It's also possible to use HTTP Bearer authentication (see the auth service documentation). This is slightly more efficient if you are making multiple requests.
Simple operations
Listing zones
You can get a list of all zones that your API key has at least some permissions on:
$ curl -n https://api.mythic-beasts.com/dns/v2/zones
{
"zones": [
"example.com"
]
}
Getting records
$ curl -n https://api.mythic-beasts.com/dns/v2/zones/example.com/records
{
"records": [
{
"data": "1.2.3.4",
"host": "test1",
"ttl": 300,
"type": "A"
},
{
"data": "mx.mythic-beasts.com.",
"host": "@",
"ttl": 300,
"type": "MX",
"mx_priority": 10
}
]
}
It's also possible to get records for specific hosts and types:
$ curl -n https://api.mythic-beasts.com/dns/v2/zones/example.com/records/test1/A
{
"records": [
{
"data": "1.2.3.4",
"host": "test1",
"ttl": 300,
"type": "A"
}
]
}
Creating records
New records can be created using POST requests:
$ curl -n -X POST https://api.mythic-beasts.com/dns/v2/zones/example.com/records/test1/A -d data=4.3.2.1
{
"message": "1 records added",
"records_added": 1
}
We now have two A records for test1.example.com:
$ curl -n https://api.mythic-beasts.com/dns/v2/zones/example.com/records/test1/A
{
"records": [
{
"data": "1.2.3.4",
"host": "test1",
"ttl": 300,
"type": "A"
},
{
"data": "4.3.2.1",
"host": "test1",
"ttl": 300,
"type": "A"
}
]
}
Data for new records can be provided in three different ways:
- Form parameters, as shown above. This is the simplest way, particularly if using
curl
, but has limited ability to create multiple records in a single request. - JSON input. Specifying a
Content-Type
ofapplication/json
allows record data to be provided in the same format as the response shown above. - Zone file format, allowing new records to be provided in an the RFC 1035 zone file format, as used by bind.
Updating records
We can replace existing records using PUT requests. PUT requests will replace all records identified by the URL that it is made to. For example:
$ curl -n -X PUT https://api.mythic-beasts.com/dns/v2/zones/example.com/records/test1/A -d data=6.7.8.9
{
"message": "1 record added",
"records_added": 1,
"records_removed": 2
}
This has replaced both of our previous A records with a new one.
We can use query parameters to be more specific about which records we want to replace. For example, if we just want to change a specific MX record, we can select by MX priority.
$ curl -n -X PUT https://api.mythic-beasts.com/dns/v2/zones/example.com/records/@/MX?mx_priority=10 -d data=mx2.mythic-beasts.com -d mx_priority=10
{
"message": "1 records added",
"records_added": 1,
"records_removed": 1
}
Zone file formatted inputs and outputs
Operations that return records can use RFC 1035 zone file format by specifying an Accept
header:
$ curl -n -H "Accept: text/dns" https://api.mythic-beasts.com/dns/v2/zones/example.com/records/test1/A
test1 300 A 1.2.3.4
test1 300 A 4.3.2.1
It's also possible to use this format as input by specificy a Content-Type
of text/dns
. For example, we can pipe the output of ssh-keygen -r
directly into the API in order to create SSHFP records:
ssh-keygen -r test1 | curl -n -X PUT --data-binary @- -H "Content-Type: text/dns" https://api.mythic-beasts.com/dns/v2/zones/example.com/records/test1/SSHFP
{
"message": "8 records added",
"records_added": 8,
"records_removed": 0
}
curl -n -H "Accept: text/dns" https://api.mythic-beasts.com/dns/v2/zones/example.com/records/test1/SSHFP
test1 300 SSHFP 1 1 e579ff6aabc2f0acf714deca53108a0c1ea7d799
test1 300 SSHFP 1 2 7c47d5dfb748ff1fd244b7289d815e83dad8c2c1652b92ac8aed8ff166733d07
test1 300 SSHFP 2 1 c5caf4cc8870acc7fd113e5a7c866822ec0d94de
test1 300 SSHFP 2 2 9f11843fa1d9da318aa4bc09bbcaacaf4a9868c4d83dfc4bad6853d0c9597a31
test1 300 SSHFP 3 1 eb8644f5fcfd555341f2063bd92044075e20da89
test1 300 SSHFP 3 2 60f3e9780f9b87e5b4d6344f2ab46decbf705123e96ef07c3247f714ca220fc4
test1 300 SSHFP 4 1 139426de48381ea46ad75dde4e412bf1c9b11e61
test1 300 SSHFP 4 2 6f094181b510bbb573048835665773eb1a2a65fd4341d95207479ed71296491b
Advanced operations
The previous examples have updated records by POSTing or PUTing to a URL for a specific host and record type. It's also possible to submit updates to the base "all records" URL for the zone. In these case, we need to provide all fields required for the record as parameters. For example:
$ curl -n -X POST https://api.mythic-beasts.com/dns/v2/zones/example.com/records -d host=@ -d type=MX -d mx_priority=10 -d data=mx.mythic-beasts.com.
{
"message": "1 records added",
"records_added": 1
}
Care must be taken when using this with PUT requests as by default it will replace the entire contents of the zone file with your new record(s).
Identifying records to replace
If you want to replace multiple records, it's possible to do this by submitting DELETE requests for the old records, and then POSTing the new records. The problem with this approach is that it's not atomic: there may be a period when your DNS is live with neither the old nor the new records.
A better approach is to use a PUT request to replace the records in a single
operation. To do this, you need specify exactly which records to replace.
This can be done using query string parameters as shown before. If you want to
replace multiple records that can't be selected by a single query, you can
URL-encode separate queries, and provide them as multiple select
parameters.
For example, suppose we want to replace all A
and AAAA
records we can use
two queries, type=A
and type=AAAA
. The =
encodes to %3D
, so we can send
a PUT request to:
https://api.mythic-beasts.com/dns/v2/zones/example.com/records?select=type%3DA&select=type%3DAAAA
It's possibe to filter on multiple fields in each select
by combining them
with and escaped &
(%26
). The filter will select records that match all
of the specified fields in any of the select
parameters.
This approach can be combined with record or host specific paths. For example,
to target the A
and AAAA
records of the test1
host, we could PUT to:
https://api.mythic-beasts.com/dns/v2/zones/example.com/records/test1?select=type%3DA&select=type%3DAAAA
The filter will select records that match the path and all of the fields in any of the select
parameters.
It's possible to see exactly which records will be replaced by making a GET request to the same URL.
Editing your zone file
The ability to export and import records in zone file format makes it easy to do bulk updates on your zone. Your zone may contain some records that cannot be edited. These are generated records, which are created by ANAME records.
In order to edit your zone file, you will need to filter these out, which you can do using the exclude-generated
parameter. To export all the editable fields in your zone:
curl -n -H "Accept: text/dns" 'https://api.mythic-beasts.com/dns/v2/zones/example.com/records?exclude-generated' > my-zone-file
You can now edit your zone file, then replace it with a PUT request to the same URL
cat my-zone-file | curl -n -X PUT --data-binary @- -H "Content-Type: text/dns" 'https://api.mythic-beasts.com/dns/v2/zones/example.com/records?exclude-generated'
As noted above, operations are atomic. If any record in your new zone file is invalid, the import will fail, and your zone file will be unmodified.
Verifying that changes are live
Changes made via the API should be live on our authoritative nameservers in less than two minutes. The API provides a mechanism for checking if the records served by the authoritative nameservers matches the latest changes made via the API. This can be useful when performing DNS-based challenges, such as those used by Let's Encrypt.
To check if records are live, simply specify the verify
parameter to
GET request for a specific host and record type. For example, to verify that
the A
record for www.example.com
reflects the latest changes made, use:
curl -n 'https://api.mythic-beasts.com/dns/v2/zones/example.com/records/www/A?verify' -D-
If the nameservers are up-to-date, a 200
response will be returned in the normal way. Otherwise, this will return 409
, with details of the differences. For example:
HTTP/1.1 409 Inconsistent
Date: Mon, 27 Apr 2020 20:02:48 GMT
Server: Apache/2.4.38 (Debian)
Content-Length: 156
Content-Type: application/json
{
"errors": [
"Received 8 record(s), expected 2 from ns1.mythic-beasts.com.",
"Received 8 record(s), expected 2 from ns2.mythic-beasts.com."
]
}
This makes it easy to script a check. For example, the script below polls the API every 10 seconds to check the requested records.
#!/bin/bash
ZONE=example.com
RECORD=www
TYPE=A
for i in $(seq 1 12); do
RES=$(curl -n https://api.mythic-beasts.com/dns/v2/zones/$ZONE/records/$RECORD/$TYPE?verify -qs -w '%{http_code}' -o /dev/null)
case $RES in
200) echo Records updated
exit 0
;;
409) echo "Not yet updated ($i/12)"
;;
*) echo "Unexpected error: $RES"
exit 1
;;
esac
sleep 10
done
echo Timed out
exit 1