Repository: certbot/certbot

Issue:
Support requesting IDNA2008 Punycode domains
As a step toward #3614 



Discussion:
I'm about 90% sure this can be accomplished by simply deleting the stanza starting at

https://github.com/certbot/certbot/blob/master/certbot/util.py#L449

If we could get punycode allowed on the staging server, I'd be glad to test it out.

@schoen @rolandshoemaker You should be able to get a local Boulder instance running that supports Punycode with:

```
docker-compose run --service-ports -e BOULDER_CONFIG_DIR=test/config-next boulder 
```

Work is underway to enable IDN support in staging, hopefully should be available by the end of the week.

However, it's my opinion that we should work on landing the Certbot patch to remove the stanza Seth mentioned, without waiting for the flag to reach staging. We have two teams that work relatively closely together. If there are obstacles for Certbot developers to run the latest version of Boulder, we should hear about them and fix them.

Also, I'd like to have the Certbot patch ready in order to more easily test against staging.


Resolving patch:
diff --git a/certbot/util.py b/certbot/util.py
--- a/certbot/util.py
+++ b/certbot/util.py
@@ -437,19 +437,13 @@ def enforce_domain_sanity(domain):
     """
     if isinstance(domain, six.text_type):
         wildcard_marker = u"*."
-        punycode_marker = u"xn--"
     else:
         wildcard_marker = b"*."
-        punycode_marker = b"xn--"
 
     # Check if there's a wildcard domain
     if domain.startswith(wildcard_marker):
         raise errors.ConfigurationError(
             "Wildcard domains are not supported: {0}".format(domain))
-    # Punycode
-    if punycode_marker in domain:
-        raise errors.ConfigurationError(
-            "Punycode domains are not presently supported: {0}".format(domain))
 
     # Unicode
     try:


Test patch:
diff --git a/certbot/tests/cli_test.py b/certbot/tests/cli_test.py
--- a/certbot/tests/cli_test.py
+++ b/certbot/tests/cli_test.py
@@ -346,11 +346,12 @@ def test_certonly_bad_args(self):
         except errors.PluginSelectionError as e:
             self.assertTrue('The requested bad_auth plugin does not appear' in str(e))
 
+    def test_punycode_ok(self):
+        # Punycode is now legal, so no longer an error; instead check
+        # that it's _not_ an error (at the initial sanity check stage)
+        util.enforce_domain_sanity('this.is.xn--ls8h.tld')
+
     def test_check_config_sanity_domain(self):
-        # Punycode
-        self.assertRaises(errors.ConfigurationError,
-                          self._call,
-                          ['-d', 'this.is.xn--ls8h.tld'])
         # FQDN
         self.assertRaises(errors.ConfigurationError,
                           self._call,
diff --git a/certbot/tests/display/ops_test.py b/certbot/tests/display/ops_test.py
--- a/certbot/tests/display/ops_test.py
+++ b/certbot/tests/display/ops_test.py
@@ -244,8 +244,8 @@ def test_get_valid_domains(self):
         all_valid = ["example.com", "second.example.com",
                      "also.example.com", "under_score.example.com",
                      "justtld"]
-        all_invalid = ["xn--ls8h.tld", "*.wildcard.com", "uniçodé.com"]
-        two_valid = ["example.com", "xn--ls8h.tld", "also.example.com"]
+        all_invalid = ["öóòps.net", "*.wildcard.com", "uniçodé.com"]
+        two_valid = ["example.com", "úniçøde.com", "also.example.com"]
         self.assertEqual(get_valid_domains(all_valid), all_valid)
         self.assertEqual(get_valid_domains(all_invalid), [])
         self.assertEqual(len(get_valid_domains(two_valid)), 2)
@@ -266,10 +266,6 @@ def test_choose_manually(self, mock_util):
             unicode_error = UnicodeEncodeError('mock', u'', 0, 1, 'mock')
             mock_sli.side_effect = unicode_error
             self.assertEqual(_choose_names_manually(), [])
-        # Punycode and no retry
-        mock_util().input.return_value = (display_util.OK,
-                                          "xn--ls8h.tld")
-        self.assertEqual(_choose_names_manually(), [])
         # Valid domains
         mock_util().input.return_value = (display_util.OK,
                                           ("example.com,"