Repository: certbot/certbot

Issue:
Support "new config file" when no vhost / servername found
Using the Apache configurator on our acme-client test machine, I got a cert for one of my hostnames that didn't already exist in any of the Apache configs. At one point I got a screen that said "We were unable to find a vhost with a ServerName or Address of abcde.crud.net. Which virtual host would you like to choose?"

None of the virtual hosts was really suitable, since I hadn't yet set one up for that hostname on that machine. Ideally we should offer a "new vhost" option that would create a file in the appropriate directory. I realize this is probably quite fraught: How do we know if the new file will be included into the main config; what do we set its documentroot to.

As a stopgap, to make the messaging clearer, maybe we should add language like: "If you haven't yet set up Apache for <host> on this machine, we suggest doing that now and running letsencrypt again."



Discussion:
Sounds good.  The menu options display need some tweeking...


Resolving patch:
diff --git a/certbot-apache/certbot_apache/tls_sni_01.py b/certbot-apache/certbot_apache/tls_sni_01.py
--- a/certbot-apache/certbot_apache/tls_sni_01.py
+++ b/certbot-apache/certbot_apache/tls_sni_01.py
@@ -4,6 +4,7 @@
 import logging
 
 from certbot.plugins import common
+from certbot.errors import PluginError, MissingCommandlineFlag
 
 from certbot_apache import obj
 from certbot_apache import parser
@@ -116,12 +117,21 @@ def _mod_config(self):
 
     def _get_addrs(self, achall):
         """Return the Apache addresses needed for TLS-SNI-01."""
-        vhost = self.configurator.choose_vhost(achall.domain, temp=True)
         # TODO: Checkout _default_ rules.
         addrs = set()
         default_addr = obj.Addr(("*", str(
             self.configurator.config.tls_sni_01_port)))
 
+        try:
+            vhost = self.configurator.choose_vhost(achall.domain, temp=True)
+        except (PluginError, MissingCommandlineFlag):
+            # We couldn't find the virtualhost for this domain, possibly
+            # because it's a new vhost that's not configured yet (GH #677),
+            # or perhaps because there were multiple <VirtualHost> sections
+            # in the config file (GH #1042).  See also GH #2600.
+            addrs.add(default_addr)
+            return addrs
+
         for addr in vhost.addrs:
             if "_default_" == addr.get_addr():
                 addrs.add(default_addr)


Test patch:
diff --git a/certbot-apache/certbot_apache/tests/tls_sni_01_test.py b/certbot-apache/certbot_apache/tests/tls_sni_01_test.py
--- a/certbot-apache/certbot_apache/tests/tls_sni_01_test.py
+++ b/certbot-apache/certbot_apache/tests/tls_sni_01_test.py
@@ -4,6 +4,7 @@
 
 import mock
 
+from certbot import errors
 from certbot.plugins import common_test
 
 from certbot_apache import obj
@@ -137,6 +138,16 @@ def test_get_addrs_default(self):
             set([obj.Addr.fromstring("*:443")]),
             self.sni._get_addrs(self.achalls[0]))
 
+    def test_get_addrs_no_vhost_found(self):
+        self.sni.configurator.choose_vhost = mock.Mock(
+            side_effect=errors.MissingCommandlineFlag(
+                "Failed to run Apache plugin non-interactively"))
+
+        # pylint: disable=protected-access
+        self.assertEqual(
+            set([obj.Addr.fromstring("*:443")]),
+            self.sni._get_addrs(self.achalls[0]))
+
 
 if __name__ == "__main__":
     unittest.main()  # pragma: no cover