How to create a DNS Configuration
The Certificate Manager and Certificate Store in Domino 12 opened up new possibilities to request certificates in Domino. The easiest of these is to request a LetsEncrypt certificate for a specific URL using a so called HTTP-01 challenge validation. A limitation of this validation method is that the URL needs to be reachable from the Internet.
A far more powerful and versatile validation method is the dns-01 challenge. This method not only removes the need to make a URL reachable from the Internet, it also allows for wildcard certificates (like the one used for this blog). This method is, however, a bit more complex as it requires some code which is specific for your DNS provider. During Engage, I will give a session on this topic, in which I’ll show how to create the DNS configuration. As you can only do so much in one hour, I’ll refer during this session to articles on my blog with the details, of which this one is the first.
The example case: deSEC
Personally, I like learning by example. A good example says more than pages of theoretical texts. As I said earlier, DNS configurations requires some coding which is specific to your DNS provider. I therefore can’t give the right code for you, as all DNS providers have created their own API on how to add and delete DNS records. Don’t ask me why they all felt the need to reinvent the wheel. They simply did. While the “how” is different for every provider, the “what”, however, is pretty much the same for all of them. This “what” I’ll explain in an example, which should help you a lot to write the “how” yourself.
My example provider is deSEC. deSEC describes themselves as:
deSEC is a free DNS hosting service, designed with security in mind.
Running on open-source software and supported by SSE, deSEC is free for everyone to use.
It’s an open source, community driven and non-profit project. Being Dutch, especially the “free” part, of course, sounds very appealing ;-). It’s also a provider for which Daniel Nashed already created an official CertStore implementation. The cool thing about these community projects is though that they’re more open to learning and after Daniel published his DNS configuration for them, they contacted Daniel to talk about their API. I don’t know if they changed their API as a result of that talk, but when I created my own implementation of a DNS configuration for deSEC and looked at their API, I was able to create a much simpler implementation for their API, which far more resembles the type of configuration you would need to make for the vast majority of DNS providers. That makes them a good example for my presentation and this blog article.
The basics – the dns-01 challenge
The dns-01 challenge, which is described in detail in this RFC document, requires you to create a DNS TXT record with a specified name and value. The fact that you’re able to do this, tells the ACME provider (LetsEncrypt in this example), that you have control over this domain and that they can grant you the certificate you requested. The RFC also tells you what to do after you’ve received your certificate:
The client SHOULD de-provision the resource record(s) provisioned for this challenge once the challenge is complete, i.e., once the “status” field of the challenge has the value “valid” or “invalid”.
So to summarise, your DNS Configuration should contain code to:
- Create TXT records in DNS with a certain name and value
- Delete this TXT record after use
And that’s really all there is to it.
Checking the API
To learn how to create a TXT record and to delete it afterwards, you need to check the API of your DNS provider. You can find the API of deSEC here. You can usually do everything through a provider’s API. Create DNS domains, create subdomains, create A/AAAA/MX/CNAME records etc. Don’t let yourself be overwhelmed. What you should focus on are just 3 things:
- What is the authentication method for the API
- How to create a DNS TXT record for an existing domain
- How to remove this TXT record
Authenticate to the deSEC API
deSEC works with tokens which you can request when you have registered for an account. You can specify for whom or what you use a specific token, which is very handy to know which tokens you can remove.
To authenticate, you need to specify this token in the header.
--header "Authorization: Token mu4W4MHuSc0Hy-GD1h_dnKuZBond"
Other providers for which I’ve created DNS Configurations, used username/password combo’s which needed to be provided in the header or also tokens which needed to be passed along in the url.
Create a DNS TXT record through the deSEC API
The easiest way to figure out the API is, in my experience, when the provider provides a curl command on how to create a TXT record. deSEC does this and this is the line:
curl -X POST https://desec.io/api/v1/domains/{name}/rrsets/ \ --header "Authorization: Token {secret}" \ --header "Content-Type: application/json" --data @- <<< \ '{"subname": "www", "type": "A", "ttl": 3600, "records": ["127.0.0.1", "127.0.0.2"]}'
This line will create an A-record, but it’s easy to determine from this what the line should be to create a TXT record. The last line would look like:
'{"subname": "{acme_challenge_name}", "type": "TXT", "ttl": 360, "records": ["\"{acme_challenge_token}\""]}'
There’s a lot to learn here:
- The API’s url is https://desec.io/api/v1
- The domain where you’ll write the TXT record is provided in the URL
- Creating a record requires a POST command (this is the most common, though I’ve written DNS Configurations where a GET command was required)
- The request requires 2 lines added to the header. The authorization token and the content-type
- The body is in JSON format
It’s also important to know what codes a successful and a non-successful request give. In the case of deSEC it’s 201 Created
for successful requests and 400 Bad Request
for a non-successful request.
Delete a DNS TXT record through the deSEC API
There are 3 ways to delete a TXT record in the deSEC API. Through a PUT request, a PATCH request or a DELETE request. Syntax wise, the DELETE request is by far the easiest. As a curl command, it looks like this:
curl -X DELETE https://desec.io/api/v1/domains/{name}/rrsets/{acme_challenge_name}/TXT \ --header "Authorization: Token {secret}"
There’s no body with a DELETE method, so we can skip one header line. The relevant status message for this request is 204 No Content
, which will be returned regardless of whether a TXT record was actually deleted or there was no record found to delete.
Testing the API
Now that it’s clear how to create and delete a TXT record through the API, we should test our findings. This prevents a lot of troubleshooting at a later moment. My tool of choice for this currently is Postman, which is available for Windows, MacOS and Linux. Another tool which I’ve used in the past is SoapUI. Both tools are free.
The correct syntax for the body including the escape characters was a result of my testing with Postman, as that part was not in the API documentation.
{
"subname": "_acme-challenge",
"type": "TXT",
"ttl": 120,
"records": ["\"9aZFf6MX5JDzgKAssvV20iRdhq8odbtpMHW5np4E_Ig\""]
}
Creating the DNS Configuration document
Now that we have determined how to create and delete DNS TXT records, we can create the DNS Configuration document. Obviously, we need to replace the variable parts of the code by variables. The ‘Insert Field’ button gives you a hint of which variables, but here’s a list to use.
Example value | Variable name | Form | Form label |
---|---|---|---|
https://desec.io/api/v1 | cfg_URL | DNS Configuration | Request URL |
martdj.dedyn.io | cfg_DnsZone | DNS Provider Account | DNS zone |
2XMSQoFRmatyUmLqj32uooFU6 | cfg_AuthToken | DNS Provider Account | Authorization Token |
martdj | cfg_UserName | DNS Provider Account | User name |
Welcome01 | cfg_Password | DNS Provider Account | Password |
_acme-challenge.martdj.dedyn.io | param_DnsTxtName | Certmgr Task | – |
9aZFf6MX5JDzgKAssvV20iRdhq | param_DnsTxtValue | CertMgr Task | – |
*.martdj.dedyn.io | param_Hostname | TLS Credentials | Host names |
martdj.dedyn.io | param_RegisteredDomain | DNS Provider Account | Registered domain |
201 | ret_AddStatus | CertMgr Task | return value Add request |
204 | ret_DelStatus | CertMgr Task | return value Delete request |
Too big for the table are the fields ret_AddResult which could have a value like
{"created":"2023-03-28T17:07:22.544438Z","domain":"martdj.dedyn.io","subname":"_acme-challenge","name":"_acme-challenge.martdj.dedyn.io.","records":["\"8i4jclzEysDHY01U2HdsGDQRMM8tEqLyKbVAXdwprbg\""],"ttl":120,"type":"TXT","touched":"2023-03-28T17:07:22.555461Z"}
and ret_DelResult which for deSEC returns nothing. Changing the curl string to @formula and using the above variables, gets me to this DNS Configuration:
The Query request type in the HTTP Add Request needs some explanation. It’s unnecessary to add it in a DNS where you can be sure there will be no remains of previous attempts to create a TXT record with a similar name. As the DELETE request returns the same status code (204) regardless of whether a record was removed or didn’t exist in the first place, it doesn’t hurt to add it. So it’s simply defensive programming, but you’re free to leave it out.
Creating the DNS Provider Account document
When you’ve created the DNS Configuration, you can create DNS Provider document. The most important field on this form is the Registered Domain, as this field determines if CertMgr will try to use a dns-01 challenge or the default http-01 challenge. If the domain for which a certificate is requested is listed as the registered domain in a DNS Provider document, CertMgr will try the dns-01 challenge.
The rest of the form is pretty self-explanatory. The Account name is not used anywhere, and just for your own administration. Below is my DNS Provider Account for deSEC, which I use for my martdj.dedyn.io domain. It uses the deSec DNS Configuration I used earlier and contains the information for the DNS zone (cfg_DnsZone) and Authorization token (cfg_AuthToken).
Requesting a certificate using a dns-01 challenge
I’ll include this for completeness, but the cool thing is that you don’t need to do anything special from here to request a certificate using a dns-01 challenge. With the DNS Provider above, whenever I request a certificate for any hostname, including a wildcard, in the martdj.dedyn.io domain, CertMgr will use the DNS Provider and DNS Configuration.
Exporting and Importing DNS Configurations
The Certificate Store contains a very handy feature to import and export DNS Configuration documents. This allows you to import DNS Configurations that someone else already made, like the ones provided by HCL.
You can also import the DNS Configuration that I created for deSEC by importing this file. I think these .dxl documents can only be imported properly by a Notes version equal or higher than the Notes version that exported it, which in my case means version Notes 12.0.2.
There’s more to tell still about DNS Configurations, like how to request a wildcard certificate for any domain by creating a free deSec-account and using the DNS provider above. But that’s for another article.