1
1
import logging
2
2
3
- import tldextract
3
+ import dns . name
4
4
import zope .interface
5
5
from certbot import errors , interfaces
6
6
from certbot .plugins import dns_common
@@ -75,6 +75,7 @@ def _setup_credentials(self) -> None:
75
75
def _perform (self , domain : str , validation_name : str , validation : str ) -> None :
76
76
"""
77
77
Add the validation DNS TXT record to the provided Porkbun domain.
78
+ Moreover, it resolves the canonical name (CNAME) for the provided domain with the acme txt prefix.
78
79
79
80
:param domain: the Porkbun domain for which a TXT record will be created
80
81
:param validation_name: the value to validate the dns challenge
@@ -83,56 +84,44 @@ def _perform(self, domain: str, validation_name: str, validation: str) -> None:
83
84
:raise PluginError: if the TXT record can not be set or something goes wrong
84
85
"""
85
86
86
- self ._domain = Authenticator ._resolve_canonical_name (domain )
87
+ # replace wildcard in domain
88
+ domain = domain .replace ("*" , "" )
89
+ domain = f"{ ACME_TXT_PREFIX } .{ domain } "
87
90
88
- tld = tldextract .TLDExtract (suffix_list_urls = None )
89
- extracted_domain = tld (self ._domain )
91
+ propagation_seconds = self .conf ("propagation_seconds" )
90
92
91
- subdomains = extracted_domain .subdomain
92
- # remove wildcard from subdomains
93
- subdomains = subdomains .replace ("*." , "" )
94
- subdomains = subdomains .replace ("*" , "" )
93
+ try :
94
+ # follow all CNAME and DNAME records
95
+ canonical_name = resolver .canonical_name (domain )
96
+
97
+ if domain != canonical_name .to_text ().rstrip ('.' ) and propagation_seconds < 600 :
98
+ logging .warning ("Make sure your CNAME record is propagated to all DNS servers, "
99
+ "because the default CNAME TTL propagation time is 600 seconds "
100
+ f"and your certbot propagation time is only { propagation_seconds } ." )
101
+
102
+ self ._root_domain = canonical_name .split (3 )[1 ].to_text ().rstrip ('.' )
103
+
104
+ acme_challenge_prefix = dns .name .Name (labels = [ACME_TXT_PREFIX ])
105
+ if acme_challenge_prefix .fullcompare (dns .name .Name (labels = [canonical_name .labels [0 ]]))[0 ] \
106
+ == dns .name .NAMERELN_EQUAL :
107
+ name = canonical_name .split (3 )[0 ].to_text ()
108
+ else :
109
+ name = acme_challenge_prefix .concatenate (canonical_name .split (3 )[0 ]).to_text ()
110
+ except (resolver .NoAnswer , resolver .NXDOMAIN ):
111
+ canonical_name = domain
95
112
96
- if subdomains :
97
- name = f"{ ACME_TXT_PREFIX } .{ subdomains } "
98
- else :
99
- name = ACME_TXT_PREFIX
113
+ self ._root_domain = "." .join (canonical_name .split ('.' )[- 2 :])
100
114
101
- root_domain = f" { extracted_domain . domain } . { extracted_domain . suffix } "
115
+ name = "." . join ( canonical_name . split ( '.' )[: - 2 ])
102
116
103
117
try :
104
- self .record_ids [validation ] = self ._get_porkbun_client ().dns_create (root_domain ,
118
+ self .record_ids [validation ] = self ._get_porkbun_client ().dns_create (self . _root_domain ,
105
119
"TXT" ,
106
120
validation ,
107
121
name = name )
108
122
except Exception as e :
109
123
raise errors .PluginError (e )
110
124
111
- @staticmethod
112
- def _resolve_canonical_name (domain : str ) -> str :
113
- """
114
- Resolve canonical name (CNAME) for the provided domain with the acme txt prefix.
115
-
116
- :param domain: the domain to resolve
117
- :raise PluginError: if something goes wrong when following CNAME
118
- :return: the final resolved domain
119
- """
120
-
121
- # ipv4
122
- try :
123
- return resolver .resolve (f"{ ACME_TXT_PREFIX } .{ domain } " , 'A' ).canonical_name .to_text ().rstrip ('.' )
124
- except resolver .NXDOMAIN as e :
125
- raise errors .PluginError (e )
126
- except resolver .NoAnswer as e :
127
- # only logging and give a second try with ipv6
128
- logging .warning (e )
129
-
130
- # ipv6
131
- try :
132
- return resolver .resolve (f"{ ACME_TXT_PREFIX } .{ domain } " , "AAAA" ).canonical_name .to_text ().rstrip ('.' )
133
- except (resolver .NoAnswer , resolver .NXDOMAIN ) as e :
134
- raise errors .PluginError (e )
135
-
136
125
def _cleanup (self , domain : str , validation_name : str , validation : str ) -> None :
137
126
"""
138
127
Delete the TXT record of the provided Porkbun domain.
@@ -144,15 +133,11 @@ def _cleanup(self, domain: str, validation_name: str, validation: str) -> None:
144
133
:raise PluginError: if the TXT record can not be deleted or something goes wrong
145
134
"""
146
135
147
- tld = tldextract .TLDExtract (suffix_list_urls = None )
148
- extracted_domain = tld (self ._domain )
149
- root_domain = f"{ extracted_domain .domain } .{ extracted_domain .suffix } "
150
-
151
136
# get the record id with the TXT record
152
137
record_id = self .record_ids [validation ]
153
138
154
139
try :
155
- if not self ._get_porkbun_client ().dns_delete (root_domain , record_id ):
140
+ if not self ._get_porkbun_client ().dns_delete (self . _root_domain , record_id ):
156
141
raise errors .PluginError ("TXT for domain {} was not deleted" .format (domain ))
157
142
except Exception as e :
158
143
raise errors .PluginError (e )
0 commit comments