Trouble issuing cert for a subdomain on ns1

My domain is: testletsencrypt.com

I ran this command: certbot certonly --csr test2.testletsencrypt.com.csr --dns-nsone --dns-nsone-credentials creds.txt

It produced this output:

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator dns-nsone, Installer None
Starting new HTTPS connection (1): acme-v02.api.letsencrypt.org
Performing the following challenges:
dns-01 challenge for test2.testletsencrypt.com
Starting new HTTPS connection (1): api.nsone.net
Cleaning up challenges
Starting new HTTPS connection (1): api.nsone.net
Error determining zone identifier: 404 Client Error: Not Found.

My web server is (include version): N/A

The operating system my web server runs on is (include version): CentOS Linux release 7.6.1810 (Core)

My hosting provider, if applicable, is: N/A

I can login to a root shell on my machine (yes or no, or I don’t know): yes

I’m using a control panel to manage my site (no, or provide the name and version of the control panel): No

The version of my client is (e.g. output of certbot --version or certbot-auto --version if you’re using Certbot): 0.34.2

When I dug into the 404, it is unable to get to /v1/zones/test2.testletsencrypt.com. When I try in my browser, I see that is indeed a 404, but /v1/zones/testletsencrypt.com is a 200.

I am attempting to get a cert for test2.testletsencrypt.com (that’s the CN in the CSR). When I try adding -d testletsencrypt.com, it complains that the domain does not match the CN. However, ns1 does not consider test2.teestletsencrypt.com to be a zone. Is there a way with certbot to say “Use this zone for authentication but my cert is some subdomain of that zone?”

Hi @lukemassa

checking your name servers there are different Serial Numbers ( https://check-your-website.server-daten.de/?q=testletsencrypt.com#soa-entries ):

Domain: testletsencrypt.com
Primary: dns1.p01.nsone.net
Mail: hostmaster.nsone.net
Serial: 1560966683
Refresh: 43200
Retry: 7200
Expire: 1209600
TTL: 3600
num Entries: 2
Domain: testletsencrypt.com
Primary: dns1.p01.nsone.net
Mail: hostmaster.nsone.net
Serial: 1560967830
Refresh: 43200
Retry: 7200
Expire: 1209600
TTL: 3600
num Entries: 2

Perhaps that's

the result.

I’m not sure how that would matter; my zone appears to be delegated to ns1 correctly, it’s just whatever resolvers that site is going to have out-of-sync view of it.

However, my problem is not with DNS entries per se; my confusion is that the certbot tool is considering “test2.testletsencrypt.com” as a zone, whereas I think I want it to consider “testletsencrypt.com” as the zone, and then have it able to issue subdomains. Am I thinking about this incorrectly?

Just to be sure, I ran this from three geographically separate servers (Arizona, US; Virginia, US; and Oxford, UK) and they all had the same SOA record for all four of the currently configured NS records

[lmassa ~]$  dig -t SOA testletsencrypt.com @dns1.p01.nsone.net. | grep '^test'
testletsencrypt.com.	300	IN	SOA	dns1.p01.nsone.net. hostmaster.nsone.net. 1560970452 43200 7200 1209600 3600
[lmassa ~]$  dig -t SOA testletsencrypt.com @dns2.p01.nsone.net. | grep '^test'
testletsencrypt.com.	300	IN	SOA	dns1.p01.nsone.net. hostmaster.nsone.net. 1560970452 43200 7200 1209600 3600
[lmassa ~]$  dig -t SOA testletsencrypt.com @dns3.p01.nsone.net. | grep '^test'
testletsencrypt.com.	300	IN	SOA	dns1.p01.nsone.net. hostmaster.nsone.net. 1560970452 43200 7200 1209600 3600
[lmassa ~]$  dig -t SOA testletsencrypt.com @dns4.p01.nsone.net. | grep '^test'
testletsencrypt.com.	300	IN	SOA	dns1.p01.nsone.net. hostmaster.nsone.net. 1560970452 43200 7200 1209600 3600

Where does certbot use zones?

Rereading your first post.

That sounds simple. You use the --csr parameter. So if you add -d testletsencrypt.com, you have to create a new csr with both domain names.

The DNS authentication plugins may use zones, so there may be a question about how to configure the plugin's behavior.

1 Like

If I add a -d testletsencrypt.com, it seems like it expects that to be part of the CSR:

certbot certonly -d testletsencrypt.com --csr test2.testletsencrypt.com.csr --dns-nsone --dns-nsone-credentials creds.txt
Inconsistent domain requests:
From the CSR: test2.testletsencrypt.com
From command line/config: testletsencrypt.com, test2.testletsencrypt.com

I don’t want testletsencrypt.com on my cert, I only want test2.testletsencrypt.com. It looks like certbot wants to put the challenge response at _acme_challenge.test2.testletsencrypt.com. Is there anyway I can put the challenge response at _acme_challenge.testletsencrypt.com and have that work for the subdomains?

If not, and I have to do the challenge for the specific name every time, this might be an issue with the plugin, because the URL it constructed to go to does not exist on the ns1 side.

Looking https://certbot-dns-nsone.readthedocs.io/en/stable/, it looks like each example included -d example.com, which as far as I can tell would work, but that would mean I’d have to put the bare domain on every one of my certs…

Then you shouldn't add -d testletsencrypt.com to your certificate.

Yes, that's required if you want to use dns-01 validation.

I don't understand your setup.

If you want test2.testletsencrypt.com as domain name, you have to create a TXT entry _acme-challenge.test2.testletsencrypt.com.

Ah ok, then the problem must be in the plugin. It’s making an API call to a non-existent domain in my ns1 account (test2.testletsencrypt.com), whereas it should be making a call to testletsencrypt.com and creating a record _acme-challenge.test2 in that zone

1 Like

Yeah, so I edited this code: https://github.com/certbot/certbot/blob/0cc56677e28fd29f82644037e35dd747e4278fff/certbot-dns-nsone/certbot_dns_nsone/dns_nsone.py#L50 to use the domain testletsencrypt.com and it worked fine and I was able to issue the certificate.

    def _perform(self, domain, validation_name, validation):
        if domain == "test2.testletsencrypt.com":
            domain = 'testletsencrypt.com'
        self._get_nsone_client().add_txt_record(domain, validation_name, validation)

    def _cleanup(self, domain, validation_name, validation):
        if domain == "test2.testletsencrypt.com":
            domain = 'testletsencrypt.com'
        self._get_nsone_client().del_txt_record(domain, validation_name, validation)
1 Like

Yes, this sounds like a bug or configuration issue with the plugin, or with NS1’s API.

Without reading the source code or NS1’s documentation, what I would’ve expected is for the plugin to make some sort of requests to NS1’s API to figure out what the correct zone is – possibly requests that would fail with an error like that – and make changes to the correct zone.

Clearly something is wrong. Maybe the plugin has a bug; maybe NS1’s API changed recently or something.

Just for fun, can you try getting a certificate for testletsencrypt.com and test2.testletsencrypt.com? You can just use the staging environment. E.g.:

certbot certonly --dry-run --dns-nsone --dns-nsone-credentials creds.txt -d testletsencrypt.com -d test2.testletsencrypt.com

I’m curious if it will put one record, or both records, in the correct zone.

Edit: I mean with the unedited plugin.

1 Like

I ran that command with the unedited code and got the same 404.

Do you think it makes sense for me to hunt around the code for some method that takes a domain and returns the top level domain (not sure how well this will be defined) and submit an MR?

It looks like Digital Ocean (https://github.com/certbot/certbot/blob/0cc56677e28fd29f82644037e35dd747e4278fff/certbot-dns-digitalocean/certbot_dns_digitalocean/dns_digitalocean.py#L140) uses base_domain_name_guesses (https://github.com/certbot/certbot/blob/0cc56677e28fd29f82644037e35dd747e4278fff/certbot/plugins/dns_common.py#L320) I wonder if that’s the right approach?

Or maybe better yet it looks like ns1 has an API we can call: https://ns1.com/api#get-search-zones-and-records

[root@~ ]# curl -sS https://api.nsone.net/v1/search?q=test2.testletsencrypt.com -H "X-NSONE-Key: $(cat creds.txt  | awk '{print $3}')" | jq .
[
  {
    "domain": "test2.testletsencrypt.com",
    "type": "A",
    "zone": "testletsencrypt.com"
  }
]

This does mean that the record has to exist first, not sure if that violates Let’s Encrypt/ACME conventions

It does. :sweat: Let's Encrypt has no requirement that subdomains exist. And Certbot normally doesn't, either. It would be unfortunate if one of the plugins had to.

I can't speak for the Certbot developers, but it would be better if there was another way to do it.

1 Like

I’m fine with following the pattern in the digital ocean code: get all your zones via the API, then start chopping up the domain until you find one that matches and call that the zone.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.