From f57f35b1ddc488d2d28bbc387f1c27fe470fb2cc Mon Sep 17 00:00:00 2001 From: Noah Swartz Date: Tue, 26 Jul 2016 15:57:11 -0700 Subject: [PATCH] Start work on multivhost support in Apache * get through parsing * not slice * add mult vhost per file * idx line backwards * blocks be wrong * always close ifmod * let's not mess up indexes * don't double add multi * fix some lint, only dedupe multi * tests * fix lint * in progress bit flip * try to pick the right vhost * take Dominic's suggestion * don't redo search * add ancestor * we now support multiple vhosts * yay * add docstrings --- certbot-apache/certbot_apache/configurator.py | 176 ++++++++++------ certbot-apache/certbot_apache/obj.py | 3 +- .../certbot_apache/tests/configurator_test.py | 64 +++++- .../multi_vhosts/apache2/apache2.conf | 196 ++++++++++++++++++ .../multi_vhosts/apache2/envvars | 29 +++ .../multi_vhosts/apache2/ports.conf | 15 ++ .../apache2/sites-available/default.conf | 22 ++ .../apache2/sites-enabled/placeholder.conf | 0 certbot-apache/certbot_apache/tests/util.py | 17 ++ certbot/reverter.py | 2 +- 10 files changed, 453 insertions(+), 71 deletions(-) create mode 100644 certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multi_vhosts/apache2/apache2.conf create mode 100644 certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multi_vhosts/apache2/envvars create mode 100644 certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multi_vhosts/apache2/ports.conf create mode 100644 certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multi_vhosts/apache2/sites-available/default.conf create mode 100644 certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multi_vhosts/apache2/sites-enabled/placeholder.conf diff --git a/certbot-apache/certbot_apache/configurator.py b/certbot-apache/certbot_apache/configurator.py index 39d25619d..aec1d692b 100644 --- a/certbot-apache/certbot_apache/configurator.py +++ b/certbot-apache/certbot_apache/configurator.py @@ -138,6 +138,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): self._enhance_func = {"redirect": self._enable_redirect, "ensure-http-header": self._set_http_header, "staple-ocsp": self._enable_ocsp_stapling} + self._skeletons = {} @property def mod_ssl_conf(self): @@ -589,6 +590,9 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): if realpath not in vhost_paths.keys(): vhs.append(new_vhost) vhost_paths[realpath] = new_vhost.filep + elif (realpath in vhost_paths.keys() + and new_vhost.path.endswith("]") and new_vhost not in vhs): + vhs.append(new_vhost) elif realpath == new_vhost.filep: # Prefer "real" vhost paths instead of symlinked ones # ex: sites-enabled/vh.conf -> sites-available/vh.conf @@ -792,20 +796,21 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): avail_fp = nonssl_vhost.filep ssl_fp = self._get_ssl_vhost_path(avail_fp) - self._copy_create_ssl_vhost_skeleton(avail_fp, ssl_fp) + vhost_num = -1 + if nonssl_vhost.path.endswith("]"): + # augeas doesn't zero index for whatever reason + vhost_num = int(nonssl_vhost.path[-2]) - 1 + self._copy_create_ssl_vhost_skeleton(avail_fp, ssl_fp, vhost_num) # Reload augeas to take into account the new vhost self.aug.load() # Get Vhost augeas path for new vhost vh_p = self.aug.match("/files%s//* [label()=~regexp('%s')]" % (self._escape(ssl_fp), parser.case_i("VirtualHost"))) - if len(vh_p) != 1: - logger.error("Error: should only be one vhost in %s", avail_fp) - raise errors.PluginError("Currently, we only support " - "configurations with one vhost per file") - else: - # This simplifies the process - vh_p = vh_p[0] + temp_vh = vh_p[0] + if self._skeletons[ssl_fp]: + temp_vh = vh_p[len(self._skeletons[ssl_fp]) -1] + vh_p = temp_vh # Update Addresses self._update_ssl_vhosts_addrs(vh_p) @@ -822,6 +827,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): # We know the length is one because of the assertion above # Create the Vhost object ssl_vhost = self._create_vhost(vh_p) + ssl_vhost.ancestor = nonssl_vhost self.vhosts.append(ssl_vhost) # NOTE: Searches through Augeas seem to ruin changes to directives @@ -875,7 +881,38 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): # Sift line if it redirects the request to a HTTPS site return target.startswith("https://") - def _copy_create_ssl_vhost_skeleton(self, avail_fp, ssl_fp): + def _section_blocks(self, blocks): + """A helper function for _create_block_segments that makes + a list of line numbers to not include in the return. + + :param list blocks: A list of indexes of where vhosts start and end. + + """ + out = [] + while len(blocks) > 1: + start = blocks[0] + end = blocks[1] + 1 + out += range(start, end) + blocks = blocks[2:] + return out + + def _create_block_segments(self, orig_file_list, vhost_num): + """A helper function for _copy_create_ssl_vhost_skeleton + that slices the appropriate vhost from the origin conf file. + + :param list orig_file_list: the original file converted to a list of strings. + "param int vhost_num: Which vhost the vhost is in the origin multivhost file. + + """ + blocks = [idx for idx, line in enumerate(orig_file_list) + if line.lstrip().startswith(" skeleton. :param str avail_fp: Pointer to the original available non-ssl vhost @@ -891,65 +928,77 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): try: with open(avail_fp, "r") as orig_file: - with open(ssl_fp, "w") as new_file: - new_file.write("\n") + orig_file_list = [line for line in orig_file] + if vhost_num != -1: + orig_file_list = self._create_block_segments(orig_file_list, vhost_num) - comment = ("# Some rewrite rules in this file were " - "disabled on your HTTPS site,\n" - "# because they have the potential to create " - "redirection loops.\n") + if ssl_fp in self._skeletons: + bit = "a" + self._skeletons[ssl_fp].append(avail_fp) + else: + bit = "w" + self._skeletons[ssl_fp] = [avail_fp] - for line in orig_file: - A = line.lstrip().startswith("RewriteCond") - B = line.lstrip().startswith("RewriteRule") + with open(ssl_fp, bit) as new_file: + new_file.write("\n") - if not (A or B): - new_file.write(line) - continue + comment = ("# Some rewrite rules in this file were " + "disabled on your HTTPS site,\n" + "# because they have the potential to create " + "redirection loops.\n") - # A RewriteRule that doesn't need filtering - if B and not self._sift_rewrite_rule(line): - new_file.write(line) - continue + orig_file_list = iter(orig_file_list) + for line in orig_file_list: + A = line.lstrip().startswith("RewriteCond") + B = line.lstrip().startswith("RewriteRule") - # A RewriteRule that does need filtering - if B and self._sift_rewrite_rule(line): + if not (A or B): + new_file.write(line) + continue + + # A RewriteRule that doesn't need filtering + if B and not self._sift_rewrite_rule(line): + new_file.write(line) + continue + + # A RewriteRule that does need filtering + if B and self._sift_rewrite_rule(line): + if not sift: + new_file.write(comment) + sift = True + new_file.write("# " + line) + continue + + # We save RewriteCond(s) and their corresponding + # RewriteRule in 'chunk'. + # We then decide whether we comment out the entire + # chunk based on its RewriteRule. + chunk = [] + if A: + chunk.append(line) + line = next(orig_file_list) + + # RewriteCond(s) must be followed by one RewriteRule + while not line.lstrip().startswith("RewriteRule"): + chunk.append(line) + line = next(orig_file_list) + + # Now, current line must start with a RewriteRule + chunk.append(line) + + if self._sift_rewrite_rule(line): if not sift: new_file.write(comment) sift = True - new_file.write("# " + line) + + new_file.write(''.join( + ['# ' + l for l in chunk])) + continue + else: + new_file.write(''.join(chunk)) continue - # We save RewriteCond(s) and their corresponding - # RewriteRule in 'chunk'. - # We then decide whether we comment out the entire - # chunk based on its RewriteRule. - chunk = [] - if A: - chunk.append(line) - line = next(orig_file) - - # RewriteCond(s) must be followed by one RewriteRule - while not line.lstrip().startswith("RewriteRule"): - chunk.append(line) - line = next(orig_file) - - # Now, current line must start with a RewriteRule - chunk.append(line) - - if self._sift_rewrite_rule(line): - if not sift: - new_file.write(comment) - sift = True - - new_file.write(''.join( - ['# ' + l for l in chunk])) - continue - else: - new_file.write(''.join(chunk)) - continue - - new_file.write("\n") + new_file.write("\n") except IOError: logger.fatal("Error writing/reading to file in make_vhost_ssl") raise errors.PluginError("Unable to write/read in make_vhost_ssl") @@ -962,6 +1011,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): "the potential to create redirection loops.".format(avail_fp, ssl_fp), reporter.MEDIUM_PRIORITY) + self.aug.set("/augeas/files%s/mtime" %(self._escape(ssl_fp)), "0") + self.aug.set("/augeas/files%s/mtime" %(self._escape(avail_fp)), "0") def _update_ssl_vhosts_addrs(self, vh_path): ssl_addrs = set() @@ -1008,12 +1059,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): self.parser.add_dir(vh_path, "Include", self.mod_ssl_conf) def _add_servername_alias(self, target_name, vhost): - fp = self._escape(vhost.filep) - vh_p = self.aug.match("/files%s//* [label()=~regexp('%s')]" % - (fp, parser.case_i("VirtualHost"))) - if not vh_p: - return - vh_path = vh_p[0] + vh_path = vhost.path if (self.parser.find_dir("ServerName", target_name, start=vh_path, exclude=False) or self.parser.find_dir("ServerAlias", target_name, @@ -1508,6 +1554,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): def _get_http_vhost(self, ssl_vhost): """Find appropriate HTTP vhost for ssl_vhost.""" # First candidate vhosts filter + if ssl_vhost.ancestor: + return ssl_vhost.ancestor candidate_http_vhs = [ vhost for vhost in self.vhosts if not vhost.ssl ] diff --git a/certbot-apache/certbot_apache/obj.py b/certbot-apache/certbot_apache/obj.py index 30cb24844..fd743fe79 100644 --- a/certbot-apache/certbot_apache/obj.py +++ b/certbot-apache/certbot_apache/obj.py @@ -123,7 +123,7 @@ class VirtualHost(object): # pylint: disable=too-few-public-methods strip_name = re.compile(r"^(?:.+://)?([^ :$]*)") def __init__(self, filep, path, addrs, ssl, enabled, name=None, - aliases=None, modmacro=False): + aliases=None, modmacro=False, ancestor=None): # pylint: disable=too-many-arguments """Initialize a VH.""" @@ -135,6 +135,7 @@ class VirtualHost(object): # pylint: disable=too-few-public-methods self.ssl = ssl self.enabled = enabled self.modmacro = modmacro + self.ancestor = ancestor def get_names(self): """Return a set of all names.""" diff --git a/certbot-apache/certbot_apache/tests/configurator_test.py b/certbot-apache/certbot_apache/tests/configurator_test.py index 45e701bd5..3dc3790a1 100644 --- a/certbot-apache/certbot_apache/tests/configurator_test.py +++ b/certbot-apache/certbot_apache/tests/configurator_test.py @@ -655,11 +655,6 @@ class MultipleVhostsTest(util.ApacheTest): len(self.config.parser.find_dir( directive, None, self.vh_truth[1].path, False)), 0) - def test_make_vhost_ssl_extra_vhs(self): - self.config.aug.match = mock.Mock(return_value=["p1", "p2"]) - self.assertRaises( - errors.PluginError, self.config.make_vhost_ssl, self.vh_truth[0]) - def test_make_vhost_ssl_bad_write(self): mock_open = mock.mock_open() # This calls open @@ -1345,6 +1340,65 @@ class AugeasVhostsTest(util.ApacheTest): self.config.choose_vhost(name) self.assertEqual(mock_select.call_count, 0) +class MultiVhostsTest(util.ApacheTest): + """Test vhosts with illegal names dependant on augeas version.""" + # pylint: disable=protected-access + + def setUp(self): # pylint: disable=arguments-differ + td = "debian_apache_2_4/multi_vhosts" + cr = "debian_apache_2_4/multi_vhosts/apache2" + vr = "debian_apache_2_4/multi_vhosts/apache2/sites-available" + super(MultiVhostsTest, self).setUp(test_dir=td, + config_root=cr, + vhost_root=vr) + + self.config = util.get_apache_configurator( + self.config_path, self.vhost_path, self.config_dir, self.work_dir) + self.vh_truth = util.get_vh_truth( + self.temp_dir, "debian_apache_2_4/multi_vhosts") + + def tearDown(self): + shutil.rmtree(self.temp_dir) + shutil.rmtree(self.config_dir) + shutil.rmtree(self.work_dir) + + def test_make_vhost_ssl(self): + ssl_vhost = self.config.make_vhost_ssl(self.vh_truth[1]) + + self.assertEqual( + ssl_vhost.filep, + os.path.join(self.config_path, "sites-available", + "default-le-ssl.conf")) + + self.assertEqual(ssl_vhost.path, + "/files" + ssl_vhost.filep + "/IfModule/VirtualHost") + self.assertEqual(len(ssl_vhost.addrs), 1) + self.assertEqual(set([obj.Addr.fromstring("*:443")]), ssl_vhost.addrs) + self.assertEqual(ssl_vhost.name, "banana.vomit.com") + self.assertTrue(ssl_vhost.ssl) + self.assertFalse(ssl_vhost.enabled) + + self.assertTrue(self.config.parser.find_dir( + "SSLCertificateFile", None, ssl_vhost.path, False)) + self.assertTrue(self.config.parser.find_dir( + "SSLCertificateKeyFile", None, ssl_vhost.path, False)) + + self.assertEqual(self.config.is_name_vhost(self.vh_truth[1]), + self.config.is_name_vhost(ssl_vhost)) + + def test_make_2nd_vhost_ssl(self): + _ = self.config.make_vhost_ssl(self.vh_truth[0]) + _ = self.config.make_vhost_ssl(self.vh_truth[1]) + self.assertEqual( + len(self.config._skeletons[self.config._get_ssl_vhost_path(self.vh_truth[0].filep)]), 2) + + def test_cover_is_stupid_and_I_hate_it(self): + http_vhost = obj.VirtualHost(None, None, None, False, False, name="Noah") + ssl_vhost = obj.VirtualHost(None, None, None, False, False, name="Noah") + self.config.vhosts.append(http_vhost) + self.assertEqual(self.config._get_http_vhost(ssl_vhost), http_vhost) + + if __name__ == "__main__": unittest.main() # pragma: no cover diff --git a/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multi_vhosts/apache2/apache2.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multi_vhosts/apache2/apache2.conf new file mode 100644 index 000000000..2a5bb7be2 --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multi_vhosts/apache2/apache2.conf @@ -0,0 +1,196 @@ +# This is the main Apache server configuration file. It contains the +# configuration directives that give the server its instructions. +# See http://httpd.apache.org/docs/2.4/ for detailed information about +# the directives and /usr/share/doc/apache2/README.Debian about Debian specific +# hints. +# +# +# Summary of how the Apache 2 configuration works in Debian: +# The Apache 2 web server configuration in Debian is quite different to +# upstream's suggested way to configure the web server. This is because Debian's +# default Apache2 installation attempts to make adding and removing modules, +# virtual hosts, and extra configuration directives as flexible as possible, in +# order to make automating the changes and administering the server as easy as +# possible. + +# It is split into several files forming the configuration hierarchy outlined +# below, all located in the /etc/apache2/ directory: +# +# /etc/apache2/ +# |-- apache2.conf +# | `-- ports.conf +# |-- mods-enabled +# | |-- *.load +# | `-- *.conf +# |-- conf-enabled +# | `-- *.conf +# `-- sites-enabled +# `-- *.conf +# +# +# * apache2.conf is the main configuration file (this file). It puts the pieces +# together by including all remaining configuration files when starting up the +# web server. +# +# * ports.conf is always included from the main configuration file. It is +# supposed to determine listening ports for incoming connections which can be +# customized anytime. +# +# * Configuration files in the mods-enabled/, conf-enabled/ and sites-enabled/ +# directories contain particular configuration snippets which manage modules, +# global configuration fragments, or virtual host configurations, +# respectively. +# +# They are activated by symlinking available configuration files from their +# respective *-available/ counterparts. These should be managed by using our +# helpers a2enmod/a2dismod, a2ensite/a2dissite and a2enconf/a2disconf. See +# their respective man pages for detailed information. +# +# * The binary is called apache2. Due to the use of environment variables, in +# the default configuration, apache2 needs to be started/stopped with +# /etc/init.d/apache2 or apache2ctl. Calling /usr/bin/apache2 directly will not +# work with the default configuration. + + +# Global configuration + +# +# The accept serialization lock file MUST BE STORED ON A LOCAL DISK. +# +Mutex file:${APACHE_LOCK_DIR} default + +# +# PidFile: The file in which the server should record its process +# identification number when it starts. +# This needs to be set in /etc/apache2/envvars +# +PidFile ${APACHE_PID_FILE} + +# +# Timeout: The number of seconds before receives and sends time out. +# +Timeout 300 + +# +# KeepAlive: Whether or not to allow persistent connections (more than +# one request per connection). Set to "Off" to deactivate. +# +KeepAlive On + +# +# MaxKeepAliveRequests: The maximum number of requests to allow +# during a persistent connection. Set to 0 to allow an unlimited amount. +# We recommend you leave this number high, for maximum performance. +# +MaxKeepAliveRequests 100 + +# +# KeepAliveTimeout: Number of seconds to wait for the next request from the +# same client on the same connection. +# +KeepAliveTimeout 5 + + +# These need to be set in /etc/apache2/envvars +User ${APACHE_RUN_USER} +Group ${APACHE_RUN_GROUP} + +# +# HostnameLookups: Log the names of clients or just their IP addresses +# e.g., www.apache.org (on) or 204.62.129.132 (off). +# The default is off because it'd be overall better for the net if people +# had to knowingly turn this feature on, since enabling it means that +# each client request will result in AT LEAST one lookup request to the +# nameserver. +# +HostnameLookups Off + +# ErrorLog: The location of the error log file. +# If you do not specify an ErrorLog directive within a +# container, error messages relating to that virtual host will be +# logged here. If you *do* define an error logfile for a +# container, that host's errors will be logged there and not here. +# +ErrorLog ${APACHE_LOG_DIR}/error.log + +# +# LogLevel: Control the severity of messages logged to the error_log. +# Available values: trace8, ..., trace1, debug, info, notice, warn, +# error, crit, alert, emerg. +# It is also possible to configure the log level for particular modules, e.g. +# "LogLevel info ssl:warn" +# +LogLevel warn + +# Include module configuration: +IncludeOptional mods-enabled/*.load +IncludeOptional mods-enabled/*.conf + +# Include list of ports to listen on +Include ports.conf + + +# Sets the default security model of the Apache2 HTTPD server. It does +# not allow access to the root filesystem outside of /usr/share and /var/www. +# The former is used by web applications packaged in Debian, +# the latter may be used for local directories served by the web server. If +# your system is serving content from a sub-directory in /srv you must allow +# access here, or in any related virtual host. + + Options FollowSymLinks + AllowOverride None + Require all denied + + + + AllowOverride None + Require all granted + + + + Options Indexes FollowSymLinks + AllowOverride None + Require all granted + + +# AccessFileName: The name of the file to look for in each directory +# for additional configuration directives. See also the AllowOverride +# directive. +# +AccessFileName .htaccess + +# +# The following lines prevent .htaccess and .htpasswd files from being +# viewed by Web clients. +# + + Require all denied + + +# The following directives define some format nicknames for use with +# a CustomLog directive. +# +# These deviate from the Common Log Format definitions in that they use %O +# (the actual bytes sent including headers) instead of %b (the size of the +# requested file), because the latter makes it impossible to detect partial +# requests. +# +# Note that the use of %{X-Forwarded-For}i instead of %h is not recommended. +# Use mod_remoteip instead. +# +LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined +LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined +LogFormat "%h %l %u %t \"%r\" %>s %O" common +LogFormat "%{Referer}i -> %U" referer +LogFormat "%{User-agent}i" agent + +# Include of directories ignores editors' and dpkg's backup files, +# see README.Debian for details. + +# Include generic snippets of statements +IncludeOptional conf-enabled/*.conf + +# Include the virtual host configurations: +IncludeOptional sites-enabled/*.conf + +# vim: syntax=apache ts=4 sw=4 sts=4 sr noet diff --git a/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multi_vhosts/apache2/envvars b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multi_vhosts/apache2/envvars new file mode 100644 index 000000000..a13d9a89e --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multi_vhosts/apache2/envvars @@ -0,0 +1,29 @@ +# envvars - default environment variables for apache2ctl + +# this won't be correct after changing uid +unset HOME + +# for supporting multiple apache2 instances +if [ "${APACHE_CONFDIR##/etc/apache2-}" != "${APACHE_CONFDIR}" ] ; then + SUFFIX="-${APACHE_CONFDIR##/etc/apache2-}" +else + SUFFIX= +fi + +# Since there is no sane way to get the parsed apache2 config in scripts, some +# settings are defined via environment variables and then used in apache2ctl, +# /etc/init.d/apache2, /etc/logrotate.d/apache2, etc. +export APACHE_RUN_USER=www-data +export APACHE_RUN_GROUP=www-data +# temporary state file location. This might be changed to /run in Wheezy+1 +export APACHE_PID_FILE=/var/run/apache2/apache2$SUFFIX.pid +export APACHE_RUN_DIR=/var/run/apache2$SUFFIX +export APACHE_LOCK_DIR=/var/lock/apache2$SUFFIX +# Only /var/log/apache2 is handled by /etc/logrotate.d/apache2. +export APACHE_LOG_DIR=/var/log/apache2$SUFFIX + +## The locale used by some modules like mod_dav +export LANG=C + +export LANG + diff --git a/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multi_vhosts/apache2/ports.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multi_vhosts/apache2/ports.conf new file mode 100644 index 000000000..5daec58c1 --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multi_vhosts/apache2/ports.conf @@ -0,0 +1,15 @@ +# If you just change the port or add more ports here, you will likely also +# have to change the VirtualHost statement in +# /etc/apache2/sites-enabled/000-default.conf + +Listen 80 + + + Listen 443 + + + + Listen 443 + + +# vim: syntax=apache ts=4 sw=4 sts=4 sr noet diff --git a/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multi_vhosts/apache2/sites-available/default.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multi_vhosts/apache2/sites-available/default.conf new file mode 100644 index 000000000..6ab206b2d --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multi_vhosts/apache2/sites-available/default.conf @@ -0,0 +1,22 @@ + + + ServerName banana.vomit.net + ServerAdmin webmaster@localhost + DocumentRoot /var/www/html + + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined + + + + + + ServerName banana.vomit.com + ServerAdmin webmaster@localhost + DocumentRoot /var/www/html + + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined + + +# vim: syntax=apache ts=4 sw=4 sts=4 sr noet diff --git a/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multi_vhosts/apache2/sites-enabled/placeholder.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multi_vhosts/apache2/sites-enabled/placeholder.conf new file mode 100644 index 000000000..e69de29bb diff --git a/certbot-apache/certbot_apache/tests/util.py b/certbot-apache/certbot_apache/tests/util.py index 3c33a0e19..90880ac5d 100644 --- a/certbot-apache/certbot_apache/tests/util.py +++ b/certbot-apache/certbot_apache/tests/util.py @@ -164,5 +164,22 @@ def get_vh_truth(temp_dir, config_name): set([obj.Addr.fromstring("10.2.3.4:443")]), True, True, "ocspvhost.com")] return vh_truth + if config_name == "debian_apache_2_4/multi_vhosts": + prefix = os.path.join( + temp_dir, config_name, "apache2/sites-available") + aug_pre = "/files" + prefix + vh_truth = [ + obj.VirtualHost( + os.path.join(prefix, "default.conf"), + os.path.join(aug_pre, "default.conf/VirtualHost[1]"), + set([obj.Addr.fromstring("*:80")]), + False, True, "ip-172-30-0-17"), + obj.VirtualHost( + os.path.join(prefix, "default.conf"), + os.path.join(aug_pre, "default.conf/VirtualHost[2]"), + set([obj.Addr.fromstring("*:80")]), + False, True, "banana.vomit.com")] + return vh_truth + return None # pragma: no cover diff --git a/certbot/reverter.py b/certbot/reverter.py index 32355782e..34feafc7e 100644 --- a/certbot/reverter.py +++ b/certbot/reverter.py @@ -491,7 +491,7 @@ class Reverter(object): else: logger.warning( "File: %s - Could not be found to be deleted %s - " - "LE probably shut down unexpectedly", + "Certbot probably shut down unexpectedly", os.linesep, path) except (IOError, OSError): logger.fatal(