Repository: certbot/certbot

Issue:
Nginx plugin domain selection
With a fresh install of Nginx on Debian Jessie, I'm given a prompt like this when running Certbot with the Nginx plugin:

```
Which names would you like to activate HTTPS for?
-------------------------------------------------------------------------------
1: _
-------------------------------------------------------------------------------
Select the appropriate numbers separated by commas and/or spaces (Enter 'c' to
cancel):
```

This is because the Nginx config contains `server_name _;`, however, this designates a catch-all/default vhost and requesting a certificate for this name will fail.

More information on the possible values here we need to handle can be found [here](https://nginx.org/en/docs/http/server_names.html#miscellaneous_names).



Discussion:

Resolving patch:
diff --git a/certbot-nginx/certbot_nginx/configurator.py b/certbot-nginx/certbot_nginx/configurator.py
--- a/certbot-nginx/certbot_nginx/configurator.py
+++ b/certbot-nginx/certbot_nginx/configurator.py
@@ -308,7 +308,25 @@ def get_all_names(self):
                     except (socket.error, socket.herror, socket.timeout):
                         continue
 
-        return all_names
+        return self._get_filtered_names(all_names)
+
+    def _get_filtered_names(self, all_names):
+        """Removes names that aren't considered valid by Let's Encrypt.
+
+        :param set all_names: all names found in the Nginx configuration
+
+        :returns: all found names that are considered valid by LE
+        :rtype: set
+
+        """
+        filtered_names = set()
+        for name in all_names:
+            try:
+                filtered_names.add(util.enforce_le_validity(name))
+            except errors.ConfigurationError as error:
+                logger.debug('Not suggesting name "%s"', name)
+                logger.debug(error)
+        return filtered_names
 
     def _get_snakeoil_paths(self):
         # TODO: generate only once
diff --git a/certbot/util.py b/certbot/util.py
--- a/certbot/util.py
+++ b/certbot/util.py
@@ -390,12 +390,45 @@ def __call__(self, unused1, unused2, unused3, option_string=None):
                  help=argparse.SUPPRESS, nargs=nargs)
 
 
+def enforce_le_validity(domain):
+    """Checks that Let's Encrypt will consider domain to be valid.
+
+    :param str domain: FQDN to check
+    :type domain: `str` or `unicode`
+    :returns: The domain cast to `str`, with ASCII-only contents
+    :rtype: str
+    :raises ConfigurationError: for invalid domains and cases where Let's
+                                Encrypt currently will not issue certificates
+
+    """
+    domain = enforce_domain_sanity(domain)
+    if not re.match("^[A-Za-z0-9.-]*$", domain):
+        raise errors.ConfigurationError(
+            "{0} contains an invalid character. "
+            "Valid characters are A-Z, a-z, 0-9, ., and -.".format(domain))
+
+    labels = domain.split(".")
+    if len(labels) < 2:
+        raise errors.ConfigurationError(
+            "{0} needs at least two labels".format(domain))
+    for label in labels:
+        if label.startswith("-"):
+            raise errors.ConfigurationError(
+                'label "{0}" in domain "{1}" cannot start with "-"'.format(
+                    label, domain))
+        if label.endswith("-"):
+            raise errors.ConfigurationError(
+                'label "{0}" in domain "{1}" cannot end with "-"'.format(
+                    label, domain))
+    return domain
+
+
 def enforce_domain_sanity(domain):
     """Method which validates domain value and errors out if
     the requirements are not met.
 
     :param domain: Domain to check
-    :type domains: `str` or `unicode`
+    :type domain: `str` or `unicode`
     :raises ConfigurationError: for invalid domains and cases where Let's
                                 Encrypt currently will not issue certificates
 


Test patch:
diff --git a/certbot-nginx/certbot_nginx/tests/configurator_test.py b/certbot-nginx/certbot_nginx/tests/configurator_test.py
--- a/certbot-nginx/certbot_nginx/tests/configurator_test.py
+++ b/certbot-nginx/certbot_nginx/tests/configurator_test.py
@@ -66,10 +66,8 @@ def test_get_all_names(self, mock_gethostbyaddr):
         mock_gethostbyaddr.return_value = ('155.225.50.69.nephoscale.net', [], [])
         names = self.config.get_all_names()
         self.assertEqual(names, set(
-            ["*.www.foo.com", "somename", "another.alias",
-             "alias", "localhost", ".example.com", r"~^(www\.)?(example|bar)\.",
-             "155.225.50.69.nephoscale.net", "*.www.example.com",
-             "example.*", "www.example.org", "myhost"]))
+            ["155.225.50.69.nephoscale.net",
+             "www.example.org", "another.alias"]))
 
     def test_supported_enhancements(self):
         self.assertEqual(['redirect'], self.config.supported_enhancements())
diff --git a/certbot/tests/util_test.py b/certbot/tests/util_test.py
--- a/certbot/tests/util_test.py
+++ b/certbot/tests/util_test.py
@@ -330,6 +330,34 @@ def test_help(self):
         self.assertTrue("--old-option" not in stdout.getvalue())
 
 
+class EnforceLeValidity(unittest.TestCase):
+    """Test enforce_le_validity."""
+    def _call(self, domain):
+        from certbot.util import enforce_le_validity
+        return enforce_le_validity(domain)
+
+    def test_sanity(self):
+        self.assertRaises(errors.ConfigurationError, self._call, u"..")
+
+    def test_invalid_chars(self):
+        self.assertRaises(
+            errors.ConfigurationError, self._call, u"hello_world.example.com")
+
+    def test_leading_hyphen(self):
+        self.assertRaises(
+            errors.ConfigurationError, self._call, u"-a.example.com")
+
+    def test_trailing_hyphen(self):
+        self.assertRaises(
+            errors.ConfigurationError, self._call, u"a-.example.com")
+
+    def test_one_label(self):
+        self.assertRaises(errors.ConfigurationError, self._call, u"com")
+
+    def test_valid_domain(self):
+        self.assertEqual(self._call(u"example.com"), u"example.com")
+
+
 class EnforceDomainSanityTest(unittest.TestCase):
     """Test enforce_domain_sanity."""