diff --git a/.pylintrc b/.pylintrc index d510fddfd..36d8c286f 100644 --- a/.pylintrc +++ b/.pylintrc @@ -41,7 +41,7 @@ load-plugins=linter_plugin # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use"--disable=all --enable=classes # --disable=W" -disable=fixme,locally-disabled,abstract-class-not-used,abstract-class-little-used,bad-continuation,too-few-public-methods,no-self-use,invalid-name,too-many-instance-attributes,cyclic-import +disable=fixme,locally-disabled,abstract-class-not-used,abstract-class-little-used,bad-continuation,too-few-public-methods,no-self-use,invalid-name,too-many-instance-attributes,cyclic-import,duplicate-code # abstract-class-not-used cannot be disabled locally (at least in # pylint 1.4.1), same for abstract-class-little-used diff --git a/CHANGELOG.md b/CHANGELOG.md index 1aaef4af1..4acfc0401 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,42 @@ Certbot adheres to [Semantic Versioning](http://semver.org/). +## 0.20.0 - 2017-12-06 + +### Added + +* Certbot's ACME library now recognizes URL fields in challenge objects in + preparation for Let's Encrypt's new ACME endpoint. The value is still + accessible in our ACME library through the name "uri". + +### Changed + +* The Apache plugin now parses some distro specific Apache configuration files + on non-Debian systems allowing it to get a clearer picture on the running + configuration. Internally, these changes were structured so that external + contributors can easily write patches to make the plugin work in new Apache + configurations. +* Certbot better reports network failures by removing information about + connection retries from the error output. +* An unnecessary question when using Certbot's webroot plugin interactively has + been removed. + +### Fixed + +* Certbot's NGINX plugin no longer sometimes incorrectly reports that it was + unable to deploy a HTTP->HTTPS redirect when requesting Certbot to enable a + redirect for multiple domains. +* Problems where the Apache plugin was failing to find directives and + duplicating existing directives on openSUSE have been resolved. +* An issue running the test shipped with Certbot and some our DNS plugins with + older versions of mock have been resolved. +* On some systems, users reported strangely interleaved output depending on + when stdout and stderr were flushed. This problem was resolved by having + Certbot regularly flush these streams. + +More details about these changes can be found on our GitHub repo: +https://github.com/certbot/certbot/milestone/44?closed=1 + ## 0.19.0 - 2017-10-04 ### Added diff --git a/acme/acme/__init__.py b/acme/acme/__init__.py index e8a0b16a8..618dda200 100644 --- a/acme/acme/__init__.py +++ b/acme/acme/__init__.py @@ -10,3 +10,12 @@ supported version: `draft-ietf-acme-01`_. https://github.com/ietf-wg-acme/acme/tree/draft-ietf-acme-acme-01 """ +import sys +import warnings + +if sys.version_info[:2] == (3, 3): + warnings.warn( + "Python 3.3 support will be dropped in the next release of " + "acme. Please upgrade your Python version.", + PendingDeprecationWarning, + ) #pragma: no cover diff --git a/acme/acme/messages.py b/acme/acme/messages.py index 4b4fa5003..2ac29941e 100644 --- a/acme/acme/messages.py +++ b/acme/acme/messages.py @@ -325,13 +325,26 @@ class ChallengeBody(ResourceBody): """ __slots__ = ('chall',) - uri = jose.Field('uri') + # ACMEv1 has a "uri" field in challenges. ACMEv2 has a "url" field. This + # challenge object supports either one, but should be accessed through the + # name "uri". In Client.answer_challenge, whichever one is set will be + # used. + _uri = jose.Field('uri', omitempty=True, default=None) + _url = jose.Field('url', omitempty=True, default=None) status = jose.Field('status', decoder=Status.from_json, omitempty=True, default=STATUS_PENDING) validated = fields.RFC3339Field('validated', omitempty=True) error = jose.Field('error', decoder=Error.from_json, omitempty=True, default=None) + def __init__(self, **kwargs): + kwargs = dict((self._internal_name(k), v) for k, v in kwargs.items()) + # pylint: disable=star-args + super(ChallengeBody, self).__init__(**kwargs) + + def encode(self, name): + return super(ChallengeBody, self).encode(self._internal_name(name)) + def to_partial_json(self): jobj = super(ChallengeBody, self).to_partial_json() jobj.update(self.chall.to_partial_json()) @@ -343,9 +356,23 @@ class ChallengeBody(ResourceBody): jobj_fields['chall'] = challenges.Challenge.from_json(jobj) return jobj_fields + @property + def uri(self): + """The URL of this challenge.""" + return self._url or self._uri + def __getattr__(self, name): return getattr(self.chall, name) + def __iter__(self): + # When iterating over fields, use the external name 'uri' instead of + # the internal '_uri'. + for name in super(ChallengeBody, self).__iter__(): + yield name[1:] if name == '_uri' else name + + def _internal_name(self, name): + return '_' + name if name == 'uri' else name + class ChallengeResource(Resource): """Challenge Resource. @@ -358,10 +385,10 @@ class ChallengeResource(Resource): authzr_uri = jose.Field('authzr_uri') @property - def uri(self): # pylint: disable=missing-docstring,no-self-argument - # bug? 'method already defined line None' - # pylint: disable=function-redefined - return self.body.uri # pylint: disable=no-member + def uri(self): + """The URL of the challenge body.""" + # pylint: disable=function-redefined,no-member + return self.body.uri class Authorization(ResourceBody): diff --git a/acme/acme/messages_test.py b/acme/acme/messages_test.py index 631f0ce4d..c9e5c2cf1 100644 --- a/acme/acme/messages_test.py +++ b/acme/acme/messages_test.py @@ -283,6 +283,9 @@ class ChallengeBodyTest(unittest.TestCase): 'detail': 'Unable to communicate with DNS server', } + def test_encode(self): + self.assertEqual(self.challb.encode('uri'), self.challb.uri) + def test_to_partial_json(self): self.assertEqual(self.jobj_to, self.challb.to_partial_json()) diff --git a/acme/setup.py b/acme/setup.py index c28e0c152..d04b84739 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.20.0.dev0' +version = '0.21.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/certbot-apache/certbot_apache/apache_util.py b/certbot-apache/certbot_apache/apache_util.py new file mode 100644 index 000000000..b4a24f137 --- /dev/null +++ b/certbot-apache/certbot_apache/apache_util.py @@ -0,0 +1,96 @@ +""" Utility functions for certbot-apache plugin """ +import os + +from certbot import util + +def get_mod_deps(mod_name): + """Get known module dependencies. + + .. note:: This does not need to be accurate in order for the client to + run. This simply keeps things clean if the user decides to revert + changes. + .. warning:: If all deps are not included, it may cause incorrect parsing + behavior, due to enable_mod's shortcut for updating the parser's + currently defined modules (`.ApacheParser.add_mod`) + This would only present a major problem in extremely atypical + configs that use ifmod for the missing deps. + + """ + deps = { + "ssl": ["setenvif", "mime"] + } + return deps.get(mod_name, []) + + +def get_file_path(vhost_path): + """Get file path from augeas_vhost_path. + + Takes in Augeas path and returns the file name + + :param str vhost_path: Augeas virtual host path + + :returns: filename of vhost + :rtype: str + + """ + if not vhost_path or not vhost_path.startswith("/files/"): + return None + + return _split_aug_path(vhost_path)[0] + + +def get_internal_aug_path(vhost_path): + """Get the Augeas path for a vhost with the file path removed. + + :param str vhost_path: Augeas virtual host path + + :returns: Augeas path to vhost relative to the containing file + :rtype: str + + """ + return _split_aug_path(vhost_path)[1] + + +def _split_aug_path(vhost_path): + """Splits an Augeas path into a file path and an internal path. + + After removing "/files", this function splits vhost_path into the + file path and the remaining Augeas path. + + :param str vhost_path: Augeas virtual host path + + :returns: file path and internal Augeas path + :rtype: `tuple` of `str` + + """ + # Strip off /files + file_path = vhost_path[6:] + internal_path = [] + + # Remove components from the end of file_path until it becomes valid + while not os.path.exists(file_path): + file_path, _, internal_path_part = file_path.rpartition("/") + internal_path.append(internal_path_part) + + return file_path, "/".join(reversed(internal_path)) + + +def parse_define_file(filepath, varname): + """ Parses Defines from a variable in configuration file + + :param str filepath: Path of file to parse + :param str varname: Name of the variable + + :returns: Dict of Define:Value pairs + :rtype: `dict` + + """ + return_vars = {} + # Get list of words in the variable + a_opts = util.get_var_from_file(varname, filepath).split() + for i, v in enumerate(a_opts): + # Handle Define statements and make sure it has an argument + if v == "-D" and len(a_opts) >= i+2: + var_parts = a_opts[i+1].partition("=") + return_vars[var_parts[0]] = var_parts[2] + return return_vars diff --git a/certbot-apache/certbot_apache/configurator.py b/certbot-apache/certbot_apache/configurator.py index 8b6c578d6..5a33346ea 100644 --- a/certbot-apache/certbot_apache/configurator.py +++ b/certbot-apache/certbot_apache/configurator.py @@ -3,6 +3,7 @@ import fnmatch import logging import os +import pkg_resources import re import socket import time @@ -19,6 +20,7 @@ from certbot import util from certbot.plugins import common from certbot.plugins.util import path_surgery +from certbot_apache import apache_util from certbot_apache import augeas_configurator from certbot_apache import constants from certbot_apache import display_ops @@ -85,27 +87,50 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): description = "Apache Web Server plugin - Beta" + OS_DEFAULTS = dict( + server_root="/etc/apache2", + vhost_root="/etc/apache2/sites-available", + vhost_files="*", + logs_root="/var/log/apache2", + version_cmd=['apache2ctl', '-v'], + apache_cmd="apache2ctl", + restart_cmd=['apache2ctl', 'graceful'], + conftest_cmd=['apache2ctl', 'configtest'], + enmod=None, + dismod=None, + le_vhost_ext="-le-ssl.conf", + handle_mods=False, + handle_sites=False, + challenge_location="/etc/apache2", + MOD_SSL_CONF_SRC=pkg_resources.resource_filename( + "certbot_apache", "options-ssl-apache.conf") + ) + + def constant(self, key): + """Get constant for OS_DEFAULTS""" + return self.OS_DEFAULTS.get(key) + @classmethod def add_parser_arguments(cls, add): - add("enmod", default=constants.os_constant("enmod"), + add("enmod", default=cls.OS_DEFAULTS["enmod"], help="Path to the Apache 'a2enmod' binary.") - add("dismod", default=constants.os_constant("dismod"), + add("dismod", default=cls.OS_DEFAULTS["dismod"], help="Path to the Apache 'a2dismod' binary.") - add("le-vhost-ext", default=constants.os_constant("le_vhost_ext"), + add("le-vhost-ext", default=cls.OS_DEFAULTS["le_vhost_ext"], help="SSL vhost configuration extension.") - add("server-root", default=constants.os_constant("server_root"), + add("server-root", default=cls.OS_DEFAULTS["server_root"], help="Apache server root directory.") add("vhost-root", default=None, help="Apache server VirtualHost configuration root") - add("logs-root", default=constants.os_constant("logs_root"), + add("logs-root", default=cls.OS_DEFAULTS["logs_root"], help="Apache server logs directory") add("challenge-location", - default=constants.os_constant("challenge_location"), + default=cls.OS_DEFAULTS["challenge_location"], help="Directory path for challenge configuration.") - add("handle-modules", default=constants.os_constant("handle_mods"), + add("handle-modules", default=cls.OS_DEFAULTS["handle_mods"], help="Let installer handle enabling required modules for you." + "(Only Ubuntu/Debian currently)") - add("handle-sites", default=constants.os_constant("handle_sites"), + add("handle-sites", default=cls.OS_DEFAULTS["handle_sites"], help="Let installer handle enabling sites for you." + "(Only Ubuntu/Debian currently)") util.add_deprecated_argument(add, argument_name="ctl", nargs=1) @@ -166,7 +191,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): raise errors.NoInstallationError("Problem in Augeas installation") # Verify Apache is installed - restart_cmd = constants.os_constant("restart_cmd")[0] + restart_cmd = self.constant("restart_cmd")[0] if not util.exe_exists(restart_cmd): if not path_surgery(restart_cmd): raise errors.NoInstallationError( @@ -192,20 +217,20 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): # Parse vhost-root if defined on cli if not self.conf("vhost-root"): - self.vhostroot = constants.os_constant("vhost_root") + self.vhostroot = self.constant("vhost_root") else: self.vhostroot = os.path.abspath(self.conf("vhost-root")) - self.parser = parser.ApacheParser( - self.aug, self.conf("server-root"), self.conf("vhost-root"), - self.version, configurator=self) + self.parser = self.get_parser() + # Check for errors in parsing files with Augeas self.check_parsing_errors("httpd.aug") # Get all of the available vhosts self.vhosts = self.get_virtual_hosts() - install_ssl_options_conf(self.mod_ssl_conf, self.updated_mod_ssl_conf_digest) + self.install_ssl_options_conf(self.mod_ssl_conf, + self.updated_mod_ssl_conf_digest) # Prevent two Apache plugins from modifying a config at once try: @@ -230,6 +255,12 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): self.aug.remove("/test/path") return matches + def get_parser(self): + """Initializes the ApacheParser""" + return parser.ApacheParser( + self.aug, self.conf("server-root"), self.conf("vhost-root"), + self.version, configurator=self) + def deploy_cert(self, domain, cert_path, key_path, chain_path=None, fullchain_path=None): """Deploys certificate to specified virtual host. @@ -585,7 +616,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): if addr.get_port() == "443": is_ssl = True - filename = get_file_path(self.aug.get("/augeas/files%s/path" % get_file_path(path))) + filename = apache_util.get_file_path( + self.aug.get("/augeas/files%s/path" % apache_util.get_file_path(path))) if filename is None: return None @@ -624,7 +656,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): new_vhost = self._create_vhost(path) if not new_vhost: continue - internal_path = get_internal_aug_path(new_vhost.path) + internal_path = apache_util.get_internal_aug_path(new_vhost.path) realpath = os.path.realpath(new_vhost.filep) if realpath not in file_paths: file_paths[realpath] = new_vhost.filep @@ -640,7 +672,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): for v in vhs: if v.filep == file_paths[realpath]: internal_paths[realpath].remove( - get_internal_aug_path(v.path)) + apache_util.get_internal_aug_path(v.path)) else: new_vhs.append(v) vhs = new_vhs @@ -828,7 +860,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): Duplicates vhost and adds default ssl options New vhost will reside as (nonssl_vhost.path) + - ``certbot_apache.constants.os_constant("le_vhost_ext")`` + ``self.constant("le_vhost_ext")`` .. note:: This function saves the configuration @@ -1702,17 +1734,13 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): return redirects - def enable_site(self, vhost): """Enables an available site, Apache reload required. .. note:: Does not make sure that the site correctly works or that all modules are enabled appropriately. - - .. todo:: This function should number subdomains before the domain - vhost - - .. todo:: Make sure link is not broken... + .. note:: The distribution specific override replaces functionality + of this method where available. :param vhost: vhost to enable :type vhost: :class:`~certbot_apache.obj.VirtualHost` @@ -1724,39 +1752,16 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): if vhost.enabled: return - # Handle non-debian systems - if not self.conf("handle-sites"): - if not self.parser.parsed_in_original(vhost.filep): - # Add direct include to root conf - self.parser.add_include(self.parser.loc["default"], vhost.filep) - vhost.enabled = True - return + if not self.parser.parsed_in_original(vhost.filep): + # Add direct include to root conf + logger.info("Enabling site %s by adding Include to root configuration", + vhost.filep) + self.save_notes += "Enabled site %s\n" % vhost.filep + self.parser.add_include(self.parser.loc["default"], vhost.filep) + vhost.enabled = True + return - enabled_path = ("%s/sites-enabled/%s" % - (self.parser.root, os.path.basename(vhost.filep))) - self.reverter.register_file_creation(False, enabled_path) - try: - os.symlink(vhost.filep, enabled_path) - except OSError as err: - if os.path.islink(enabled_path) and os.path.realpath( - enabled_path) == vhost.filep: - # Already in shape - vhost.enabled = True - return - else: - logger.warning( - "Could not symlink %s to %s, got error: %s", enabled_path, - vhost.filep, err.strerror) - errstring = ("Encountered error while trying to enable a " + - "newly created VirtualHost located at {0} by " + - "linking to it from {1}") - raise errors.NotSupportedError(errstring.format(vhost.filep, - enabled_path)) - vhost.enabled = True - logger.info("Enabling available site: %s", vhost.filep) - self.save_notes += "Enabled site %s\n" % vhost.filep - - def enable_mod(self, mod_name, temp=False): + def enable_mod(self, mod_name, temp=False): # pylint: disable=unused-argument """Enables module in Apache. Both enables and reloads Apache so module is active. @@ -1764,64 +1769,18 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): :param str mod_name: Name of the module to enable. (e.g. 'ssl') :param bool temp: Whether or not this is a temporary action. - :raises .errors.NotSupportedError: If the filesystem layout is not - supported. - :raises .errors.MisconfigurationError: If a2enmod or a2dismod cannot be - run. + .. note:: The distribution specific override replaces functionality + of this method where available. + + :raises .errors.MisconfigurationError: We cannot enable modules in + generic fashion. """ - # Support Debian specific setup - avail_path = os.path.join(self.parser.root, "mods-available") - enabled_path = os.path.join(self.parser.root, "mods-enabled") - if not os.path.isdir(avail_path) or not os.path.isdir(enabled_path): - raise errors.NotSupportedError( - "Unsupported directory layout. You may try to enable mod %s " - "and try again." % mod_name) - - deps = _get_mod_deps(mod_name) - - # Enable all dependencies - for dep in deps: - if (dep + "_module") not in self.parser.modules: - self._enable_mod_debian(dep, temp) - self._add_parser_mod(dep) - - note = "Enabled dependency of %s module - %s" % (mod_name, dep) - if not temp: - self.save_notes += note + os.linesep - logger.debug(note) - - # Enable actual module - self._enable_mod_debian(mod_name, temp) - self._add_parser_mod(mod_name) - - if not temp: - self.save_notes += "Enabled %s module in Apache\n" % mod_name - logger.info("Enabled Apache %s module", mod_name) - - # Modules can enable additional config files. Variables may be defined - # within these new configuration sections. - # Reload is not necessary as DUMP_RUN_CFG uses latest config. - self.parser.update_runtime_variables() - - def _add_parser_mod(self, mod_name): - """Shortcut for updating parser modules.""" - self.parser.modules.add(mod_name + "_module") - self.parser.modules.add("mod_" + mod_name + ".c") - - def _enable_mod_debian(self, mod_name, temp): - """Assumes mods-available, mods-enabled layout.""" - # Generate reversal command. - # Try to be safe here... check that we can probably reverse before - # applying enmod command - if not util.exe_exists(self.conf("dismod")): - raise errors.MisconfigurationError( - "Unable to find a2dismod, please make sure a2enmod and " - "a2dismod are configured correctly for certbot.") - - self.reverter.register_undo_command( - temp, [self.conf("dismod"), mod_name]) - util.run_script([self.conf("enmod"), mod_name]) + mod_message = ("Apache needs to have module \"{0}\" active for the " + + "requested installation options. Unfortunately Certbot is unable " + + "to install or enable it for you. Please install the module, and " + + "run Certbot again.") + raise errors.MisconfigurationError(mod_message.format(mod_name)) def restart(self): """Runs a config test and reloads the Apache server. @@ -1840,7 +1799,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): """ try: - util.run_script(constants.os_constant("restart_cmd")) + util.run_script(self.constant("restart_cmd")) except errors.SubprocessError as err: raise errors.MisconfigurationError(str(err)) @@ -1851,7 +1810,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): """ try: - util.run_script(constants.os_constant("conftest_cmd")) + util.run_script(self.constant("conftest_cmd")) except errors.SubprocessError as err: raise errors.MisconfigurationError(str(err)) @@ -1867,11 +1826,11 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): """ try: - stdout, _ = util.run_script(constants.os_constant("version_cmd")) + stdout, _ = util.run_script(self.constant("version_cmd")) except errors.SubprocessError: raise errors.PluginError( "Unable to run %s -v" % - constants.os_constant("version_cmd")) + self.constant("version_cmd")) regex = re.compile(r"Apache/([0-9\.]*)", re.IGNORECASE) matches = regex.findall(stdout) @@ -1943,86 +1902,15 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): if not self._chall_out: self.revert_challenge_config() self.restart() - self.parser.init_modules() + self.parser.reset_modules() + + def install_ssl_options_conf(self, options_ssl, options_ssl_digest): + """Copy Certbot's SSL options file into the system's config dir if required.""" + + # XXX if we ever try to enforce a local privilege boundary (eg, running + # certbot for unprivileged users via setuid), this function will need + # to be modified. + return common.install_version_controlled_file(options_ssl, options_ssl_digest, + self.constant("MOD_SSL_CONF_SRC"), constants.ALL_SSL_OPTIONS_HASHES) -def _get_mod_deps(mod_name): - """Get known module dependencies. - - .. note:: This does not need to be accurate in order for the client to - run. This simply keeps things clean if the user decides to revert - changes. - .. warning:: If all deps are not included, it may cause incorrect parsing - behavior, due to enable_mod's shortcut for updating the parser's - currently defined modules (`.ApacheConfigurator._add_parser_mod`) - This would only present a major problem in extremely atypical - configs that use ifmod for the missing deps. - - """ - deps = { - "ssl": ["setenvif", "mime"] - } - return deps.get(mod_name, []) - - -def get_file_path(vhost_path): - """Get file path from augeas_vhost_path. - - Takes in Augeas path and returns the file name - - :param str vhost_path: Augeas virtual host path - - :returns: filename of vhost - :rtype: str - - """ - if not vhost_path or not vhost_path.startswith("/files/"): - return None - - return _split_aug_path(vhost_path)[0] - - -def get_internal_aug_path(vhost_path): - """Get the Augeas path for a vhost with the file path removed. - - :param str vhost_path: Augeas virtual host path - - :returns: Augeas path to vhost relative to the containing file - :rtype: str - - """ - return _split_aug_path(vhost_path)[1] - - -def _split_aug_path(vhost_path): - """Splits an Augeas path into a file path and an internal path. - - After removing "/files", this function splits vhost_path into the - file path and the remaining Augeas path. - - :param str vhost_path: Augeas virtual host path - - :returns: file path and internal Augeas path - :rtype: `tuple` of `str` - - """ - # Strip off /files - file_path = vhost_path[6:] - internal_path = [] - - # Remove components from the end of file_path until it becomes valid - while not os.path.exists(file_path): - file_path, _, internal_path_part = file_path.rpartition("/") - internal_path.append(internal_path_part) - - return file_path, "/".join(reversed(internal_path)) - - -def install_ssl_options_conf(options_ssl, options_ssl_digest): - """Copy Certbot's SSL options file into the system's config dir if required.""" - - # XXX if we ever try to enforce a local privilege boundary (eg, running - # certbot for unprivileged users via setuid), this function will need - # to be modified. - return common.install_version_controlled_file(options_ssl, options_ssl_digest, - constants.os_constant("MOD_SSL_CONF_SRC"), constants.ALL_SSL_OPTIONS_HASHES) diff --git a/certbot-apache/certbot_apache/constants.py b/certbot-apache/certbot_apache/constants.py index 0c5f7263a..a13ca04a6 100644 --- a/certbot-apache/certbot_apache/constants.py +++ b/certbot-apache/certbot_apache/constants.py @@ -1,151 +1,6 @@ """Apache plugin constants.""" import pkg_resources -from certbot import util -CLI_DEFAULTS_DEFAULT = dict( - server_root="/etc/apache2", - vhost_root="/etc/apache2/sites-available", - vhost_files="*", - logs_root="/var/log/apache2", - version_cmd=['apache2ctl', '-v'], - define_cmd=['apache2ctl', '-t', '-D', 'DUMP_RUN_CFG'], - restart_cmd=['apache2ctl', 'graceful'], - conftest_cmd=['apache2ctl', 'configtest'], - enmod=None, - dismod=None, - le_vhost_ext="-le-ssl.conf", - handle_mods=False, - handle_sites=False, - challenge_location="/etc/apache2", - MOD_SSL_CONF_SRC=pkg_resources.resource_filename( - "certbot_apache", "options-ssl-apache.conf") -) -CLI_DEFAULTS_DEBIAN = dict( - server_root="/etc/apache2", - vhost_root="/etc/apache2/sites-available", - vhost_files="*", - logs_root="/var/log/apache2", - version_cmd=['apache2ctl', '-v'], - define_cmd=['apache2ctl', '-t', '-D', 'DUMP_RUN_CFG'], - restart_cmd=['apache2ctl', 'graceful'], - conftest_cmd=['apache2ctl', 'configtest'], - enmod="a2enmod", - dismod="a2dismod", - le_vhost_ext="-le-ssl.conf", - handle_mods=True, - handle_sites=True, - challenge_location="/etc/apache2", - MOD_SSL_CONF_SRC=pkg_resources.resource_filename( - "certbot_apache", "options-ssl-apache.conf") -) -CLI_DEFAULTS_CENTOS = dict( - server_root="/etc/httpd", - vhost_root="/etc/httpd/conf.d", - vhost_files="*.conf", - logs_root="/var/log/httpd", - version_cmd=['apachectl', '-v'], - define_cmd=['apachectl', '-t', '-D', 'DUMP_RUN_CFG'], - restart_cmd=['apachectl', 'graceful'], - conftest_cmd=['apachectl', 'configtest'], - enmod=None, - dismod=None, - le_vhost_ext="-le-ssl.conf", - handle_mods=False, - handle_sites=False, - challenge_location="/etc/httpd/conf.d", - MOD_SSL_CONF_SRC=pkg_resources.resource_filename( - "certbot_apache", "centos-options-ssl-apache.conf") -) -CLI_DEFAULTS_GENTOO = dict( - server_root="/etc/apache2", - vhost_root="/etc/apache2/vhosts.d", - vhost_files="*.conf", - logs_root="/var/log/apache2", - version_cmd=['/usr/sbin/apache2', '-v'], - define_cmd=['apache2ctl', 'virtualhosts'], - restart_cmd=['apache2ctl', 'graceful'], - conftest_cmd=['apache2ctl', 'configtest'], - enmod=None, - dismod=None, - le_vhost_ext="-le-ssl.conf", - handle_mods=False, - handle_sites=False, - challenge_location="/etc/apache2/vhosts.d", - MOD_SSL_CONF_SRC=pkg_resources.resource_filename( - "certbot_apache", "options-ssl-apache.conf") -) -CLI_DEFAULTS_DARWIN = dict( - server_root="/etc/apache2", - vhost_root="/etc/apache2/other", - vhost_files="*.conf", - logs_root="/var/log/apache2", - version_cmd=['/usr/sbin/httpd', '-v'], - define_cmd=['/usr/sbin/httpd', '-t', '-D', 'DUMP_RUN_CFG'], - restart_cmd=['apachectl', 'graceful'], - conftest_cmd=['apachectl', 'configtest'], - enmod=None, - dismod=None, - le_vhost_ext="-le-ssl.conf", - handle_mods=False, - handle_sites=False, - challenge_location="/etc/apache2/other", - MOD_SSL_CONF_SRC=pkg_resources.resource_filename( - "certbot_apache", "options-ssl-apache.conf") -) -CLI_DEFAULTS_SUSE = dict( - server_root="/etc/apache2", - vhost_root="/etc/apache2/vhosts.d", - vhost_files="*.conf", - logs_root="/var/log/apache2", - version_cmd=['apache2ctl', '-v'], - define_cmd=['apache2ctl', '-t', '-D', 'DUMP_RUN_CFG'], - restart_cmd=['apache2ctl', 'graceful'], - conftest_cmd=['apache2ctl', 'configtest'], - enmod="a2enmod", - dismod="a2dismod", - le_vhost_ext="-le-ssl.conf", - handle_mods=False, - handle_sites=False, - challenge_location="/etc/apache2/vhosts.d", - MOD_SSL_CONF_SRC=pkg_resources.resource_filename( - "certbot_apache", "options-ssl-apache.conf") -) -CLI_DEFAULTS_ARCH = dict( - server_root="/etc/httpd", - vhost_root="/etc/httpd/conf", - vhost_files="*.conf", - logs_root="/var/log/httpd", - version_cmd=['apachectl', '-v'], - define_cmd=['apachectl', '-t', '-D', 'DUMP_RUN_CFG'], - restart_cmd=['apachectl', 'graceful'], - conftest_cmd=['apachectl', 'configtest'], - enmod=None, - dismod=None, - le_vhost_ext="-le-ssl.conf", - handle_mods=False, - handle_sites=False, - challenge_location="/etc/httpd/conf", - MOD_SSL_CONF_SRC=pkg_resources.resource_filename( - "certbot_apache", "options-ssl-apache.conf") -) -CLI_DEFAULTS = { - "default": CLI_DEFAULTS_DEFAULT, - "debian": CLI_DEFAULTS_DEBIAN, - "ubuntu": CLI_DEFAULTS_DEBIAN, - "centos": CLI_DEFAULTS_CENTOS, - "centos linux": CLI_DEFAULTS_CENTOS, - "fedora": CLI_DEFAULTS_CENTOS, - "red hat enterprise linux server": CLI_DEFAULTS_CENTOS, - "rhel": CLI_DEFAULTS_CENTOS, - "amazon": CLI_DEFAULTS_CENTOS, - "gentoo": CLI_DEFAULTS_GENTOO, - "gentoo base system": CLI_DEFAULTS_GENTOO, - "darwin": CLI_DEFAULTS_DARWIN, - "opensuse": CLI_DEFAULTS_SUSE, - "suse": CLI_DEFAULTS_SUSE, - "arch": CLI_DEFAULTS_ARCH, -} -"""CLI defaults.""" MOD_SSL_CONF_DEST = "options-ssl-apache.conf" """Name of the mod_ssl config file as saved in `IConfig.config_dir`.""" @@ -191,39 +46,3 @@ UIR_ARGS = ["always", "set", "Content-Security-Policy", HEADER_ARGS = {"Strict-Transport-Security": HSTS_ARGS, "Upgrade-Insecure-Requests": UIR_ARGS} - - -def os_constant(key): - """ - Get a constant value for operating system - - :param key: name of cli constant - :return: value of constant for active os - """ - - os_info = util.get_os_info() - try: - constants = CLI_DEFAULTS[os_info[0].lower()] - except KeyError: - constants = os_like_constants() - if not constants: - constants = CLI_DEFAULTS["default"] - return constants[key] - - -def os_like_constants(): - """ - Try to get constants for distribution with - similar layout and configuration, indicated by - /etc/os-release variable "LIKE" - - :returns: Constants dictionary - :rtype: `dict` - """ - - os_like = util.get_systemd_os_like() - if os_like: - for os_name in os_like: - if os_name in CLI_DEFAULTS.keys(): - return CLI_DEFAULTS[os_name] - return {} diff --git a/certbot-apache/certbot_apache/entrypoint.py b/certbot-apache/certbot_apache/entrypoint.py new file mode 100644 index 000000000..4267398d5 --- /dev/null +++ b/certbot-apache/certbot_apache/entrypoint.py @@ -0,0 +1,47 @@ +""" Entry point for Apache Plugin """ +from certbot import util + +from certbot_apache import configurator +from certbot_apache import override_arch +from certbot_apache import override_darwin +from certbot_apache import override_debian +from certbot_apache import override_centos +from certbot_apache import override_gentoo +from certbot_apache import override_suse + +OVERRIDE_CLASSES = { + "arch": override_arch.ArchConfigurator, + "darwin": override_darwin.DarwinConfigurator, + "debian": override_debian.DebianConfigurator, + "ubuntu": override_debian.DebianConfigurator, + "centos": override_centos.CentOSConfigurator, + "centos linux": override_centos.CentOSConfigurator, + "fedora": override_centos.CentOSConfigurator, + "red hat enterprise linux server": override_centos.CentOSConfigurator, + "rhel": override_centos.CentOSConfigurator, + "amazon": override_centos.CentOSConfigurator, + "gentoo": override_gentoo.GentooConfigurator, + "gentoo base system": override_gentoo.GentooConfigurator, + "opensuse": override_suse.OpenSUSEConfigurator, + "suse": override_suse.OpenSUSEConfigurator, +} + +def get_configurator(): + """ Get correct configurator class based on the OS fingerprint """ + os_info = util.get_os_info() + override_class = None + try: + override_class = OVERRIDE_CLASSES[os_info[0].lower()] + except KeyError: + # OS not found in the list + os_like = util.get_systemd_os_like() + if os_like: + for os_name in os_like: + if os_name in OVERRIDE_CLASSES.keys(): + override_class = OVERRIDE_CLASSES[os_name] + if not override_class: + # No override class found, return the generic configurator + override_class = configurator.ApacheConfigurator + return override_class + +ENTRYPOINT = get_configurator() diff --git a/certbot-apache/certbot_apache/override_arch.py b/certbot-apache/certbot_apache/override_arch.py new file mode 100644 index 000000000..ea5155a3c --- /dev/null +++ b/certbot-apache/certbot_apache/override_arch.py @@ -0,0 +1,31 @@ +""" Distribution specific override class for Arch Linux """ +import pkg_resources + +import zope.interface + +from certbot import interfaces + +from certbot_apache import configurator + +@zope.interface.provider(interfaces.IPluginFactory) +class ArchConfigurator(configurator.ApacheConfigurator): + """Arch Linux specific ApacheConfigurator override class""" + + OS_DEFAULTS = dict( + server_root="/etc/httpd", + vhost_root="/etc/httpd/conf", + vhost_files="*.conf", + logs_root="/var/log/httpd", + version_cmd=['apachectl', '-v'], + apache_cmd="apachectl", + restart_cmd=['apachectl', 'graceful'], + conftest_cmd=['apachectl', 'configtest'], + enmod=None, + dismod=None, + le_vhost_ext="-le-ssl.conf", + handle_mods=False, + handle_sites=False, + challenge_location="/etc/httpd/conf", + MOD_SSL_CONF_SRC=pkg_resources.resource_filename( + "certbot_apache", "options-ssl-apache.conf") + ) diff --git a/certbot-apache/certbot_apache/override_centos.py b/certbot-apache/certbot_apache/override_centos.py new file mode 100644 index 000000000..db6cd6fba --- /dev/null +++ b/certbot-apache/certbot_apache/override_centos.py @@ -0,0 +1,59 @@ +""" Distribution specific override class for CentOS family (RHEL, Fedora) """ +import pkg_resources + +import zope.interface + +from certbot import interfaces + +from certbot_apache import apache_util +from certbot_apache import configurator +from certbot_apache import parser + +@zope.interface.provider(interfaces.IPluginFactory) +class CentOSConfigurator(configurator.ApacheConfigurator): + """CentOS specific ApacheConfigurator override class""" + + OS_DEFAULTS = dict( + server_root="/etc/httpd", + vhost_root="/etc/httpd/conf.d", + vhost_files="*.conf", + logs_root="/var/log/httpd", + version_cmd=['apachectl', '-v'], + apache_cmd="apachectl", + restart_cmd=['apachectl', 'graceful'], + conftest_cmd=['apachectl', 'configtest'], + enmod=None, + dismod=None, + le_vhost_ext="-le-ssl.conf", + handle_mods=False, + handle_sites=False, + challenge_location="/etc/httpd/conf.d", + MOD_SSL_CONF_SRC=pkg_resources.resource_filename( + "certbot_apache", "centos-options-ssl-apache.conf") + ) + + def get_parser(self): + """Initializes the ApacheParser""" + return CentOSParser( + self.aug, self.conf("server-root"), self.conf("vhost-root"), + self.version, configurator=self) + + +class CentOSParser(parser.ApacheParser): + """CentOS specific ApacheParser override class""" + def __init__(self, *args, **kwargs): + # CentOS specific configuration file for Apache + self.sysconfig_filep = "/etc/sysconfig/httpd" + super(CentOSParser, self).__init__(*args, **kwargs) + + def update_runtime_variables(self, *args, **kwargs): + """ Override for update_runtime_variables for custom parsing """ + # Opportunistic, works if SELinux not enforced + super(CentOSParser, self).update_runtime_variables(*args, **kwargs) + self.parse_sysconfig_var() + + def parse_sysconfig_var(self): + """ Parses Apache CLI options from CentOS configuration file """ + defines = apache_util.parse_define_file(self.sysconfig_filep, "OPTIONS") + for k in defines.keys(): + self.variables[k] = defines[k] diff --git a/certbot-apache/certbot_apache/override_darwin.py b/certbot-apache/certbot_apache/override_darwin.py new file mode 100644 index 000000000..53741d504 --- /dev/null +++ b/certbot-apache/certbot_apache/override_darwin.py @@ -0,0 +1,31 @@ +""" Distribution specific override class for macOS """ +import pkg_resources + +import zope.interface + +from certbot import interfaces + +from certbot_apache import configurator + +@zope.interface.provider(interfaces.IPluginFactory) +class DarwinConfigurator(configurator.ApacheConfigurator): + """macOS specific ApacheConfigurator override class""" + + OS_DEFAULTS = dict( + server_root="/etc/apache2", + vhost_root="/etc/apache2/other", + vhost_files="*.conf", + logs_root="/var/log/apache2", + version_cmd=['/usr/sbin/httpd', '-v'], + apache_cmd="/usr/sbin/httpd", + restart_cmd=['apachectl', 'graceful'], + conftest_cmd=['apachectl', 'configtest'], + enmod=None, + dismod=None, + le_vhost_ext="-le-ssl.conf", + handle_mods=False, + handle_sites=False, + challenge_location="/etc/apache2/other", + MOD_SSL_CONF_SRC=pkg_resources.resource_filename( + "certbot_apache", "options-ssl-apache.conf") + ) diff --git a/certbot-apache/certbot_apache/override_debian.py b/certbot-apache/certbot_apache/override_debian.py new file mode 100644 index 000000000..6e2e34ba9 --- /dev/null +++ b/certbot-apache/certbot_apache/override_debian.py @@ -0,0 +1,144 @@ +""" Distribution specific override class for Debian family (Ubuntu/Debian) """ +import logging +import os +import pkg_resources + +import zope.interface + +from certbot import errors +from certbot import interfaces +from certbot import util + +from certbot_apache import apache_util +from certbot_apache import configurator + +logger = logging.getLogger(__name__) + +@zope.interface.provider(interfaces.IPluginFactory) +class DebianConfigurator(configurator.ApacheConfigurator): + """Debian specific ApacheConfigurator override class""" + + OS_DEFAULTS = dict( + server_root="/etc/apache2", + vhost_root="/etc/apache2/sites-available", + vhost_files="*", + logs_root="/var/log/apache2", + version_cmd=['apache2ctl', '-v'], + apache_cmd="apache2ctl", + restart_cmd=['apache2ctl', 'graceful'], + conftest_cmd=['apache2ctl', 'configtest'], + enmod="a2enmod", + dismod="a2dismod", + le_vhost_ext="-le-ssl.conf", + handle_mods=True, + handle_sites=True, + challenge_location="/etc/apache2", + MOD_SSL_CONF_SRC=pkg_resources.resource_filename( + "certbot_apache", "options-ssl-apache.conf") + ) + + def enable_site(self, vhost): + """Enables an available site, Apache reload required. + + .. note:: Does not make sure that the site correctly works or that all + modules are enabled appropriately. + + :param vhost: vhost to enable + :type vhost: :class:`~certbot_apache.obj.VirtualHost` + + :raises .errors.NotSupportedError: If filesystem layout is not + supported. + + """ + if vhost.enabled: + return + + enabled_path = ("%s/sites-enabled/%s" % + (self.parser.root, + os.path.basename(vhost.filep))) + if not os.path.isdir(os.path.dirname(enabled_path)): + # For some reason, sites-enabled / sites-available do not exist + # Call the parent method + return super(DebianConfigurator, self).enable_site(vhost) + self.reverter.register_file_creation(False, enabled_path) + try: + os.symlink(vhost.filep, enabled_path) + except OSError as err: + if os.path.islink(enabled_path) and os.path.realpath( + enabled_path) == vhost.filep: + # Already in shape + vhost.enabled = True + return + else: + logger.warning( + "Could not symlink %s to %s, got error: %s", enabled_path, + vhost.filep, err.strerror) + errstring = ("Encountered error while trying to enable a " + + "newly created VirtualHost located at {0} by " + + "linking to it from {1}") + raise errors.NotSupportedError(errstring.format(vhost.filep, + enabled_path)) + vhost.enabled = True + logger.info("Enabling available site: %s", vhost.filep) + self.save_notes += "Enabled site %s\n" % vhost.filep + + def enable_mod(self, mod_name, temp=False): + # pylint: disable=unused-argument + """Enables module in Apache. + + Both enables and reloads Apache so module is active. + + :param str mod_name: Name of the module to enable. (e.g. 'ssl') + :param bool temp: Whether or not this is a temporary action. + + :raises .errors.NotSupportedError: If the filesystem layout is not + supported. + :raises .errors.MisconfigurationError: If a2enmod or a2dismod cannot be + run. + + """ + avail_path = os.path.join(self.parser.root, "mods-available") + enabled_path = os.path.join(self.parser.root, "mods-enabled") + if not os.path.isdir(avail_path) or not os.path.isdir(enabled_path): + raise errors.NotSupportedError( + "Unsupported directory layout. You may try to enable mod %s " + "and try again." % mod_name) + + deps = apache_util.get_mod_deps(mod_name) + + # Enable all dependencies + for dep in deps: + if (dep + "_module") not in self.parser.modules: + self._enable_mod_debian(dep, temp) + self.parser.add_mod(dep) + note = "Enabled dependency of %s module - %s" % (mod_name, dep) + if not temp: + self.save_notes += note + os.linesep + logger.debug(note) + + # Enable actual module + self._enable_mod_debian(mod_name, temp) + self.parser.add_mod(mod_name) + + if not temp: + self.save_notes += "Enabled %s module in Apache\n" % mod_name + logger.info("Enabled Apache %s module", mod_name) + + # Modules can enable additional config files. Variables may be defined + # within these new configuration sections. + # Reload is not necessary as DUMP_RUN_CFG uses latest config. + self.parser.update_runtime_variables() + + def _enable_mod_debian(self, mod_name, temp): + """Assumes mods-available, mods-enabled layout.""" + # Generate reversal command. + # Try to be safe here... check that we can probably reverse before + # applying enmod command + if not util.exe_exists(self.conf("dismod")): + raise errors.MisconfigurationError( + "Unable to find a2dismod, please make sure a2enmod and " + "a2dismod are configured correctly for certbot.") + + self.reverter.register_undo_command( + temp, [self.conf("dismod"), mod_name]) + util.run_script([self.conf("enmod"), mod_name]) diff --git a/certbot-apache/certbot_apache/override_gentoo.py b/certbot-apache/certbot_apache/override_gentoo.py new file mode 100644 index 000000000..d4d4e96b9 --- /dev/null +++ b/certbot-apache/certbot_apache/override_gentoo.py @@ -0,0 +1,58 @@ +""" Distribution specific override class for Gentoo Linux """ +import pkg_resources + +import zope.interface + +from certbot import interfaces + +from certbot_apache import apache_util +from certbot_apache import configurator +from certbot_apache import parser + +@zope.interface.provider(interfaces.IPluginFactory) +class GentooConfigurator(configurator.ApacheConfigurator): + """Gentoo specific ApacheConfigurator override class""" + + OS_DEFAULTS = dict( + server_root="/etc/apache2", + vhost_root="/etc/apache2/vhosts.d", + vhost_files="*.conf", + logs_root="/var/log/apache2", + version_cmd=['/usr/sbin/apache2', '-v'], + apache_cmd="apache2ctl", + restart_cmd=['apache2ctl', 'graceful'], + conftest_cmd=['apache2ctl', 'configtest'], + enmod=None, + dismod=None, + le_vhost_ext="-le-ssl.conf", + handle_mods=False, + handle_sites=False, + challenge_location="/etc/apache2/vhosts.d", + MOD_SSL_CONF_SRC=pkg_resources.resource_filename( + "certbot_apache", "options-ssl-apache.conf") + ) + + def get_parser(self): + """Initializes the ApacheParser""" + return GentooParser( + self.aug, self.conf("server-root"), self.conf("vhost-root"), + self.version, configurator=self) + + +class GentooParser(parser.ApacheParser): + """Gentoo specific ApacheParser override class""" + def __init__(self, *args, **kwargs): + # Gentoo specific configuration file for Apache2 + self.apacheconfig_filep = "/etc/conf.d/apache2" + super(GentooParser, self).__init__(*args, **kwargs) + + def update_runtime_variables(self): + """ Override for update_runtime_variables for custom parsing """ + self.parse_sysconfig_var() + + def parse_sysconfig_var(self): + """ Parses Apache CLI options from Gentoo configuration file """ + defines = apache_util.parse_define_file(self.apacheconfig_filep, + "APACHE2_OPTS") + for k in defines.keys(): + self.variables[k] = defines[k] diff --git a/certbot-apache/certbot_apache/override_suse.py b/certbot-apache/certbot_apache/override_suse.py new file mode 100644 index 000000000..a67054b5b --- /dev/null +++ b/certbot-apache/certbot_apache/override_suse.py @@ -0,0 +1,31 @@ +""" Distribution specific override class for OpenSUSE """ +import pkg_resources + +import zope.interface + +from certbot import interfaces + +from certbot_apache import configurator + +@zope.interface.provider(interfaces.IPluginFactory) +class OpenSUSEConfigurator(configurator.ApacheConfigurator): + """OpenSUSE specific ApacheConfigurator override class""" + + OS_DEFAULTS = dict( + server_root="/etc/apache2", + vhost_root="/etc/apache2/vhosts.d", + vhost_files="*.conf", + logs_root="/var/log/apache2", + version_cmd=['apache2ctl', '-v'], + apache_cmd="apache2ctl", + restart_cmd=['apache2ctl', 'graceful'], + conftest_cmd=['apache2ctl', 'configtest'], + enmod="a2enmod", + dismod="a2dismod", + le_vhost_ext="-le-ssl.conf", + handle_mods=False, + handle_sites=False, + challenge_location="/etc/apache2/vhosts.d", + MOD_SSL_CONF_SRC=pkg_resources.resource_filename( + "certbot_apache", "options-ssl-apache.conf") + ) diff --git a/certbot-apache/certbot_apache/parser.py b/certbot-apache/certbot_apache/parser.py index b15608d61..7715d2c35 100644 --- a/certbot-apache/certbot_apache/parser.py +++ b/certbot-apache/certbot_apache/parser.py @@ -11,8 +11,6 @@ import six from certbot import errors -from certbot_apache import constants - logger = logging.getLogger(__name__) @@ -40,14 +38,9 @@ class ApacheParser(object): # issues with aug.load() after adding new files / defines to parse tree self.configurator = configurator - # This uses the binary, so it can be done first. - # https://httpd.apache.org/docs/2.4/mod/core.html#define - # https://httpd.apache.org/docs/2.4/mod/core.html#ifdefine - # This only handles invocation parameters and Define directives! + self.modules = set() self.parser_paths = {} self.variables = {} - if version >= (2, 4): - self.update_runtime_variables() self.aug = aug # Find configuration root and make sure augeas can parse it. @@ -55,24 +48,26 @@ class ApacheParser(object): self.loc = {"root": self._find_config_root()} self.parse_file(self.loc["root"]) + if version >= (2, 4): + # Look up variables from httpd and add to DOM if not already parsed + self.update_runtime_variables() + # This problem has been fixed in Augeas 1.0 self.standardize_excl() - # Temporarily set modules to be empty, so that find_dirs can work - # https://httpd.apache.org/docs/2.4/mod/core.html#ifmodule - # This needs to come before locations are set. - self.modules = set() - self.init_modules() + # Parse LoadModule directives from configuration files + self.parse_modules() # Set up rest of locations self.loc.update(self._set_locations()) + # list of the active include paths, before modifications self.existing_paths = copy.deepcopy(self.parser_paths) # Must also attempt to parse additional virtual host root if vhostroot: self.parse_file(os.path.abspath(vhostroot) + "/" + - constants.os_constant("vhost_files")) + self.configurator.constant("vhost_files")) # check to see if there were unparsed define statements if version < (2, 4): @@ -103,50 +98,61 @@ class ApacheParser(object): # Create a new path self.existing_paths[new_dir] = [new_file] - def init_modules(self): + def add_mod(self, mod_name): + """Shortcut for updating parser modules.""" + if mod_name + "_module" not in self.modules: + self.modules.add(mod_name + "_module") + if "mod_" + mod_name + ".c" not in self.modules: + self.modules.add("mod_" + mod_name + ".c") + + def reset_modules(self): + """Reset the loaded modules list. This is called from cleanup to clear + temporarily loaded modules.""" + self.modules = set() + self.update_modules() + self.parse_modules() + + def parse_modules(self): """Iterates on the configuration until no new modules are loaded. ..todo:: This should be attempted to be done with a binary to avoid the iteration issue. Else... parse and enable mods at same time. """ - # Since modules are being initiated... clear existing set. - self.modules = set() + mods = set() matches = self.find_dir("LoadModule") - iterator = iter(matches) # Make sure prev_size != cur_size for do: while: iteration prev_size = -1 - while len(self.modules) != prev_size: - prev_size = len(self.modules) + while len(mods) != prev_size: + prev_size = len(mods) for match_name, match_filename in six.moves.zip( iterator, iterator): mod_name = self.get_arg(match_name) mod_filename = self.get_arg(match_filename) if mod_name and mod_filename: - self.modules.add(mod_name) - self.modules.add(os.path.basename(mod_filename)[:-2] + "c") + mods.add(mod_name) + mods.add(os.path.basename(mod_filename)[:-2] + "c") else: logger.debug("Could not read LoadModule directive from " + "Augeas path: {0}".format(match_name[6:])) + self.modules.update(mods) def update_runtime_variables(self): - """" + """Update Includes, Defines and Includes from httpd config dump data""" + self.update_defines() + self.update_includes() + self.update_modules() - .. note:: Compile time variables (apache2ctl -V) are not used within - the dynamic configuration files. These should not be parsed or - interpreted. - - .. todo:: Create separate compile time variables... - simply for arg_get() - - """ - stdout = self._get_runtime_cfg() + def update_defines(self): + """Get Defines from httpd process""" variables = dict() - matches = re.compile(r"Define: ([^ \n]*)").findall(stdout) + define_cmd = [self.configurator.constant("apache_cmd"), "-t", "-D", + "DUMP_RUN_CFG"] + matches = self.parse_from_subprocess(define_cmd, r"Define: ([^ \n]*)") try: matches.remove("DUMP_RUN_CFG") except ValueError: @@ -163,15 +169,54 @@ class ApacheParser(object): self.variables = variables - def _get_runtime_cfg(self): # pylint: disable=no-self-use - """Get runtime configuration info. + def update_includes(self): + """Get includes from httpd process, and add them to DOM if needed""" - :returns: stdout from DUMP_RUN_CFG + # Find_dir iterates over configuration for Include and IncludeOptional + # directives to make sure we see the full include tree present in the + # configuration files + _ = self.find_dir("Include") + + inc_cmd = [self.configurator.constant("apache_cmd"), "-t", "-D", + "DUMP_INCLUDES"] + matches = self.parse_from_subprocess(inc_cmd, r"\(.*\) (.*)") + if matches: + for i in matches: + if not self.parsed_in_current(i): + self.parse_file(i) + + def update_modules(self): + """Get loaded modules from httpd process, and add them to DOM""" + + mod_cmd = [self.configurator.constant("apache_cmd"), "-t", "-D", + "DUMP_MODULES"] + matches = self.parse_from_subprocess(mod_cmd, r"(.*)_module") + for mod in matches: + self.add_mod(mod.strip()) + + def parse_from_subprocess(self, command, regexp): + """Get values from stdout of subprocess command + + :param list command: Command to run + :param str regexp: Regexp for parsing + + :returns: list parsed from command output + :rtype: list + + """ + stdout = self._get_runtime_cfg(command) + return re.compile(regexp).findall(stdout) + + def _get_runtime_cfg(self, command): # pylint: disable=no-self-use + """Get runtime configuration info. + :param command: Command to run + + :returns: stdout from command """ try: proc = subprocess.Popen( - constants.os_constant("define_cmd"), + command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) @@ -180,10 +225,10 @@ class ApacheParser(object): except (OSError, ValueError): logger.error( "Error running command %s for runtime parameters!%s", - constants.os_constant("define_cmd"), os.linesep) + command, os.linesep) raise errors.MisconfigurationError( "Error accessing loaded Apache parameters: %s", - constants.os_constant("define_cmd")) + command) # Small errors that do not impede if proc.returncode != 0: logger.warning("Error in checking parameter list: %s", stderr) diff --git a/certbot-apache/certbot_apache/tests/centos_test.py b/certbot-apache/certbot_apache/tests/centos_test.py new file mode 100644 index 000000000..7ca47a4d5 --- /dev/null +++ b/certbot-apache/certbot_apache/tests/centos_test.py @@ -0,0 +1,123 @@ +"""Test for certbot_apache.configurator for Centos overrides""" +import os +import unittest + +import mock + +from certbot_apache import obj +from certbot_apache import override_centos +from certbot_apache.tests import util + +def get_vh_truth(temp_dir, config_name): + """Return the ground truth for the specified directory.""" + prefix = os.path.join( + temp_dir, config_name, "httpd/conf.d") + + aug_pre = "/files" + prefix + vh_truth = [ + obj.VirtualHost( + os.path.join(prefix, "centos.example.com.conf"), + os.path.join(aug_pre, "centos.example.com.conf/VirtualHost"), + set([obj.Addr.fromstring("*:80")]), + False, True, "centos.example.com"), + obj.VirtualHost( + os.path.join(prefix, "ssl.conf"), + os.path.join(aug_pre, "ssl.conf/VirtualHost"), + set([obj.Addr.fromstring("_default_:443")]), + True, True, None) + ] + return vh_truth + +class MultipleVhostsTestCentOS(util.ApacheTest): + """Multiple vhost tests for CentOS / RHEL family of distros""" + + _multiprocess_can_split_ = True + + def setUp(self): # pylint: disable=arguments-differ + test_dir = "centos7_apache/apache" + config_root = "centos7_apache/apache/httpd" + vhost_root = "centos7_apache/apache/httpd/conf.d" + super(MultipleVhostsTestCentOS, self).setUp(test_dir=test_dir, + config_root=config_root, + vhost_root=vhost_root) + + self.config = util.get_apache_configurator( + self.config_path, self.vhost_path, self.config_dir, self.work_dir, + os_info="centos") + self.vh_truth = get_vh_truth( + self.temp_dir, "centos7_apache/apache") + + def test_get_parser(self): + self.assertTrue(isinstance(self.config.parser, + override_centos.CentOSParser)) + + @mock.patch("certbot_apache.parser.ApacheParser._get_runtime_cfg") + def test_opportunistic_httpd_runtime_parsing(self, mock_get): + define_val = ( + 'Define: TEST1\n' + 'Define: TEST2\n' + 'Define: DUMP_RUN_CFG\n' + ) + mod_val = ( + 'Loaded Modules:\n' + ' mock_module (static)\n' + ' another_module (static)\n' + ) + def mock_get_cfg(command): + """Mock httpd process stdout""" + if command == ['apachectl', '-t', '-D', 'DUMP_RUN_CFG']: + return define_val + elif command == ['apachectl', '-t', '-D', 'DUMP_MODULES']: + return mod_val + return "" + mock_get.side_effect = mock_get_cfg + self.config.parser.modules = set() + self.config.parser.variables = {} + + with mock.patch("certbot.util.get_os_info") as mock_osi: + # Make sure we have the have the CentOS httpd constants + mock_osi.return_value = ("centos", "7") + self.config.parser.update_runtime_variables() + + self.assertEquals(mock_get.call_count, 3) + self.assertEquals(len(self.config.parser.modules), 4) + self.assertEquals(len(self.config.parser.variables), 2) + self.assertTrue("TEST2" in self.config.parser.variables.keys()) + self.assertTrue("mod_another.c" in self.config.parser.modules) + + def test_get_virtual_hosts(self): + """Make sure all vhosts are being properly found.""" + vhs = self.config.get_virtual_hosts() + self.assertEqual(len(vhs), 2) + found = 0 + + for vhost in vhs: + for centos_truth in self.vh_truth: + if vhost == centos_truth: + found += 1 + break + else: + raise Exception("Missed: %s" % vhost) # pragma: no cover + self.assertEqual(found, 2) + + @mock.patch("certbot_apache.parser.ApacheParser._get_runtime_cfg") + def test_get_sysconfig_vars(self, mock_cfg): + """Make sure we read the sysconfig OPTIONS variable correctly""" + # Return nothing for the process calls + mock_cfg.return_value = "" + self.config.parser.sysconfig_filep = os.path.realpath( + os.path.join(self.config.parser.root, "../sysconfig/httpd")) + self.config.parser.variables = {} + + with mock.patch("certbot.util.get_os_info") as mock_osi: + # Make sure we have the have the CentOS httpd constants + mock_osi.return_value = ("centos", "7") + self.config.parser.update_runtime_variables() + + self.assertTrue("mock_define" in self.config.parser.variables.keys()) + self.assertTrue("mock_define_too" in self.config.parser.variables.keys()) + self.assertTrue("mock_value" in self.config.parser.variables.keys()) + self.assertEqual("TRUE", self.config.parser.variables["mock_value"]) + +if __name__ == "__main__": + unittest.main() # pragma: no cover diff --git a/certbot-apache/certbot_apache/tests/complex_parsing_test.py b/certbot-apache/certbot_apache/tests/complex_parsing_test.py index 079d7e95f..a296fb0eb 100644 --- a/certbot-apache/certbot_apache/tests/complex_parsing_test.py +++ b/certbot-apache/certbot_apache/tests/complex_parsing_test.py @@ -18,7 +18,7 @@ class ComplexParserTest(util.ParserTest): self.setup_variables() # This needs to happen after due to setup_variables not being run # until after - self.parser.init_modules() # pylint: disable=protected-access + self.parser.parse_modules() # pylint: disable=protected-access def tearDown(self): shutil.rmtree(self.temp_dir) diff --git a/certbot-apache/certbot_apache/tests/configurator_test.py b/certbot-apache/certbot_apache/tests/configurator_test.py index 90561d6ad..4f85e1e3f 100644 --- a/certbot-apache/certbot_apache/tests/configurator_test.py +++ b/certbot-apache/certbot_apache/tests/configurator_test.py @@ -3,12 +3,12 @@ import os import shutil import socket +import tempfile import unittest import mock # six is used in mock.patch() import six # pylint: disable=unused-import -import tempfile from acme import challenges @@ -19,7 +19,7 @@ from certbot import errors from certbot.tests import acme_util from certbot.tests import util as certbot_util -from certbot_apache import configurator +from certbot_apache import apache_util from certbot_apache import constants from certbot_apache import parser from certbot_apache import obj @@ -34,39 +34,24 @@ class MultipleVhostsTest(util.ApacheTest): def setUp(self): # pylint: disable=arguments-differ super(MultipleVhostsTest, self).setUp() - from certbot_apache.constants import os_constant - orig_os_constant = os_constant - def mock_os_constant(key, vhost_path=self.vhost_path): - """Mock default vhost path""" - if key == "vhost_root": - return vhost_path - else: - return orig_os_constant(key) - - with mock.patch("certbot_apache.constants.os_constant") as mock_c: - mock_c.side_effect = mock_os_constant - self.config = util.get_apache_configurator( - self.config_path, None, self.config_dir, self.work_dir) - self.config = self.mock_deploy_cert(self.config) + self.config = util.get_apache_configurator( + self.config_path, self.vhost_path, self.config_dir, self.work_dir) + self.config = self.mock_deploy_cert(self.config) self.vh_truth = util.get_vh_truth( self.temp_dir, "debian_apache_2_4/multiple_vhosts") def mock_deploy_cert(self, config): """A test for a mock deploy cert""" - self.config.real_deploy_cert = self.config.deploy_cert + config.real_deploy_cert = self.config.deploy_cert def mocked_deploy_cert(*args, **kwargs): """a helper to mock a deployed cert""" - with mock.patch("certbot_apache.configurator.ApacheConfigurator.enable_mod"): + g_mod = "certbot_apache.configurator.ApacheConfigurator.enable_mod" + with mock.patch(g_mod): config.real_deploy_cert(*args, **kwargs) self.config.deploy_cert = mocked_deploy_cert return self.config - def tearDown(self): - shutil.rmtree(self.temp_dir) - shutil.rmtree(self.config_dir) - shutil.rmtree(self.work_dir) - @mock.patch("certbot_apache.configurator.ApacheConfigurator.init_augeas") @mock.patch("certbot_apache.configurator.path_surgery") def test_prepare_no_install(self, mock_surgery, _init_augeas): @@ -130,6 +115,10 @@ class MultipleVhostsTest(util.ApacheTest): # Weak test.. ApacheConfigurator.add_parser_arguments(mock.MagicMock()) + def test_constant(self): + self.assertEqual(self.config.constant("server_root"), "/etc/apache2") + self.assertEqual(self.config.constant("nonexistent"), None) + @certbot_util.patch_get_utility() def test_get_all_names(self, mock_getutility): mock_utility = mock_getutility() @@ -163,13 +152,12 @@ class MultipleVhostsTest(util.ApacheTest): self.assertTrue("certbot.demo" in names) def test_get_bad_path(self): - from certbot_apache.configurator import get_file_path - self.assertEqual(get_file_path(None), None) - self.assertEqual(get_file_path("nonexistent"), None) + self.assertEqual(apache_util.get_file_path(None), None) + self.assertEqual(apache_util.get_file_path("nonexistent"), None) self.assertEqual(self.config._create_vhost("nonexistent"), None) # pylint: disable=protected-access def test_get_aug_internal_path(self): - from certbot_apache.configurator import get_internal_aug_path + from certbot_apache.apache_util import get_internal_aug_path internal_paths = [ "Virtualhost", "IfModule/VirtualHost", "VirtualHost", "VirtualHost", "Macro/VirtualHost", "IfModule/VirtualHost", "VirtualHost", @@ -319,190 +307,23 @@ class MultipleVhostsTest(util.ApacheTest): # pylint: disable=protected-access self.assertEqual(len(self.config._non_default_vhosts()), 8) - @mock.patch("certbot.util.run_script") - @mock.patch("certbot.util.exe_exists") - @mock.patch("certbot_apache.parser.subprocess.Popen") - def test_enable_mod(self, mock_popen, mock_exe_exists, mock_run_script): - mock_popen().communicate.return_value = ("Define: DUMP_RUN_CFG", "") - mock_popen().returncode = 0 - mock_exe_exists.return_value = True - - self.config.enable_mod("ssl") - self.assertTrue("ssl_module" in self.config.parser.modules) - self.assertTrue("mod_ssl.c" in self.config.parser.modules) - - self.assertTrue(mock_run_script.called) - - def test_enable_mod_unsupported_dirs(self): - shutil.rmtree(os.path.join(self.config.parser.root, "mods-enabled")) - self.assertRaises( - errors.NotSupportedError, self.config.enable_mod, "ssl") - - @mock.patch("certbot.util.exe_exists") - def test_enable_mod_no_disable(self, mock_exe_exists): - mock_exe_exists.return_value = False - self.assertRaises( - errors.MisconfigurationError, self.config.enable_mod, "ssl") - - def test_enable_site_already_enabled(self): - self.assertTrue(self.vh_truth[1].enabled) - self.config.enable_site(self.vh_truth[1]) - - def test_enable_site_failure(self): - self.config.parser.root = "/tmp/nonexistent" - self.assertRaises( - errors.NotSupportedError, - self.config.enable_site, - obj.VirtualHost("asdf", "afsaf", set(), False, False)) - - def test_enable_site_nondebian(self): - mock_c = "certbot_apache.configurator.ApacheConfigurator.conf" - def conf_side_effect(arg): - """ Mock function for ApacheConfigurator.conf """ - confvars = {"handle-sites": False} - if arg in confvars: - return confvars[arg] - inc_path = "/path/to/wherever" - vhost = self.vh_truth[0] - with mock.patch(mock_c) as mock_conf: - mock_conf.side_effect = conf_side_effect - vhost.enabled = False - vhost.filep = inc_path - self.assertFalse(self.config.parser.find_dir("Include", inc_path)) - self.assertFalse( - os.path.dirname(inc_path) in self.config.parser.existing_paths) - self.config.enable_site(vhost) - self.assertTrue(self.config.parser.find_dir("Include", inc_path)) - self.assertTrue( - os.path.dirname(inc_path) in self.config.parser.existing_paths) - self.assertTrue( - os.path.basename(inc_path) in self.config.parser.existing_paths[ - os.path.dirname(inc_path)]) - def test_deploy_cert_enable_new_vhost(self): # Create ssl_vhost = self.config.make_vhost_ssl(self.vh_truth[0]) self.config.parser.modules.add("ssl_module") self.config.parser.modules.add("mod_ssl.c") + self.config.parser.modules.add("socache_shmcb_module") + self.assertFalse(ssl_vhost.enabled) self.config.deploy_cert( "encryption-example.demo", "example/cert.pem", "example/key.pem", "example/cert_chain.pem", "example/fullchain.pem") self.assertTrue(ssl_vhost.enabled) - # Make sure that we don't error out if symlink already exists - ssl_vhost.enabled = False - self.assertFalse(ssl_vhost.enabled) - self.config.deploy_cert( - "encryption-example.demo", "example/cert.pem", "example/key.pem", - "example/cert_chain.pem", "example/fullchain.pem") - self.assertTrue(ssl_vhost.enabled) - - def test_deploy_cert_newssl(self): - self.config = util.get_apache_configurator( - self.config_path, self.vhost_path, self.config_dir, - self.work_dir, version=(2, 4, 16)) - - self.config.parser.modules.add("ssl_module") - self.config.parser.modules.add("mod_ssl.c") - - # Get the default 443 vhost - self.config.assoc["random.demo"] = self.vh_truth[1] - self.config = self.mock_deploy_cert(self.config) - self.config.deploy_cert( - "random.demo", "example/cert.pem", "example/key.pem", - "example/cert_chain.pem", "example/fullchain.pem") - self.config.save() - - # Verify ssl_module was enabled. - self.assertTrue(self.vh_truth[1].enabled) - self.assertTrue("ssl_module" in self.config.parser.modules) - - loc_cert = self.config.parser.find_dir( - "sslcertificatefile", "example/fullchain.pem", - self.vh_truth[1].path) - loc_key = self.config.parser.find_dir( - "sslcertificateKeyfile", "example/key.pem", self.vh_truth[1].path) - - # Verify one directive was found in the correct file - self.assertEqual(len(loc_cert), 1) - self.assertEqual( - configurator.get_file_path(loc_cert[0]), - self.vh_truth[1].filep) - - self.assertEqual(len(loc_key), 1) - self.assertEqual( - configurator.get_file_path(loc_key[0]), - self.vh_truth[1].filep) - - def test_deploy_cert_newssl_no_fullchain(self): - self.config = util.get_apache_configurator( - self.config_path, self.vhost_path, self.config_dir, - self.work_dir, version=(2, 4, 16)) - self.config = self.mock_deploy_cert(self.config) - - self.config.parser.modules.add("ssl_module") - self.config.parser.modules.add("mod_ssl.c") - - # Get the default 443 vhost - self.config.assoc["random.demo"] = self.vh_truth[1] - self.assertRaises(errors.PluginError, - lambda: self.config.deploy_cert( - "random.demo", "example/cert.pem", - "example/key.pem")) - - def test_deploy_cert_old_apache_no_chain(self): - self.config = util.get_apache_configurator( - self.config_path, self.vhost_path, self.config_dir, - self.work_dir, version=(2, 4, 7)) - self.config = self.mock_deploy_cert(self.config) - - self.config.parser.modules.add("ssl_module") - self.config.parser.modules.add("mod_ssl.c") - - # Get the default 443 vhost - self.config.assoc["random.demo"] = self.vh_truth[1] - self.assertRaises(errors.PluginError, - lambda: self.config.deploy_cert( - "random.demo", "example/cert.pem", - "example/key.pem")) - - def test_deploy_cert_not_parsed_path(self): - # Make sure that we add include to root config for vhosts when - # handle-sites is false - self.config.parser.modules.add("ssl_module") - self.config.parser.modules.add("mod_ssl.c") - tmp_path = os.path.realpath(tempfile.mkdtemp("vhostroot")) - os.chmod(tmp_path, 0o755) - mock_p = "certbot_apache.configurator.ApacheConfigurator._get_ssl_vhost_path" - mock_a = "certbot_apache.parser.ApacheParser.add_include" - mock_c = "certbot_apache.configurator.ApacheConfigurator.conf" - orig_conf = self.config.conf - def conf_side_effect(arg): - """ Mock function for ApacheConfigurator.conf """ - confvars = {"handle-sites": False} - if arg in confvars: - return confvars[arg] - else: - return orig_conf("arg") - - with mock.patch(mock_c) as mock_conf: - mock_conf.side_effect = conf_side_effect - with mock.patch(mock_p) as mock_path: - mock_path.return_value = os.path.join(tmp_path, "whatever.conf") - with mock.patch(mock_a) as mock_add: - self.config.deploy_cert( - "encryption-example.demo", - "example/cert.pem", "example/key.pem", - "example/cert_chain.pem") - # Test that we actually called add_include - self.assertTrue(mock_add.called) - shutil.rmtree(tmp_path) - def test_deploy_cert(self): self.config.parser.modules.add("ssl_module") self.config.parser.modules.add("mod_ssl.c") - + self.config.parser.modules.add("socache_shmcb_module") # Patch _add_dummy_ssl_directives to make sure we write them correctly # pylint: disable=protected-access orig_add_dummy = self.config._add_dummy_ssl_directives @@ -531,7 +352,6 @@ class MultipleVhostsTest(util.ApacheTest): self.assertTrue( "insert_key_file_path" in find_args(vhostpath, "SSLCertificateKeyFile")) - # pylint: disable=protected-access self.config._add_dummy_ssl_directives = mock_add_dummy_ssl @@ -557,17 +377,17 @@ class MultipleVhostsTest(util.ApacheTest): # Verify one directive was found in the correct file self.assertEqual(len(loc_cert), 1) self.assertEqual( - configurator.get_file_path(loc_cert[0]), + apache_util.get_file_path(loc_cert[0]), self.vh_truth[1].filep) self.assertEqual(len(loc_key), 1) self.assertEqual( - configurator.get_file_path(loc_key[0]), + apache_util.get_file_path(loc_key[0]), self.vh_truth[1].filep) self.assertEqual(len(loc_chain), 1) self.assertEqual( - configurator.get_file_path(loc_chain[0]), + apache_util.get_file_path(loc_chain[0]), self.vh_truth[1].filep) # One more time for chain directive setting @@ -877,7 +697,9 @@ class MultipleVhostsTest(util.ApacheTest): self.assertEqual(mock_restart.call_count, 1) @mock.patch("certbot_apache.configurator.ApacheConfigurator.restart") - def test_cleanup(self, mock_restart): + @mock.patch("certbot_apache.parser.ApacheParser._get_runtime_cfg") + def test_cleanup(self, mock_cfg, mock_restart): + mock_cfg.return_value = "" _, achall1, achall2 = self.get_achalls() self.config._chall_out.add(achall1) # pylint: disable=protected-access @@ -890,7 +712,9 @@ class MultipleVhostsTest(util.ApacheTest): self.assertTrue(mock_restart.called) @mock.patch("certbot_apache.configurator.ApacheConfigurator.restart") - def test_cleanup_no_errors(self, mock_restart): + @mock.patch("certbot_apache.parser.ApacheParser._get_runtime_cfg") + def test_cleanup_no_errors(self, mock_cfg, mock_restart): + mock_cfg.return_value = "" _, achall1, achall2 = self.get_achalls() self.config._chall_out.add(achall1) # pylint: disable=protected-access @@ -951,10 +775,9 @@ class MultipleVhostsTest(util.ApacheTest): self.assertTrue(isinstance(self.config.get_chall_pref(""), list)) def test_install_ssl_options_conf(self): - from certbot_apache.configurator import install_ssl_options_conf path = os.path.join(self.work_dir, "test_it") other_path = os.path.join(self.work_dir, "other_test_it") - install_ssl_options_conf(path, other_path) + self.config.install_ssl_options_conf(path, other_path) self.assertTrue(os.path.isfile(path)) self.assertTrue(os.path.isfile(other_path)) @@ -994,20 +817,17 @@ class MultipleVhostsTest(util.ApacheTest): errors.PluginError, self.config.enhance, "certbot.demo", "unknown_enhancement") - @mock.patch("certbot.util.run_script") @mock.patch("certbot.util.exe_exists") - def test_ocsp_stapling(self, mock_exe, mock_run_script): + def test_ocsp_stapling(self, mock_exe): self.config.parser.update_runtime_variables = mock.Mock() self.config.parser.modules.add("mod_ssl.c") + self.config.parser.modules.add("socache_shmcb_module") self.config.get_version = mock.Mock(return_value=(2, 4, 7)) mock_exe.return_value = True # This will create an ssl vhost for certbot.demo self.config.enhance("certbot.demo", "staple-ocsp") - self.assertTrue("socache_shmcb_module" in self.config.parser.modules) - self.assertTrue(mock_run_script.called) - # Get the ssl vhost for certbot.demo ssl_vhost = self.config.assoc["certbot.demo"] @@ -1077,14 +897,13 @@ class MultipleVhostsTest(util.ApacheTest): def test_http_header_hsts(self, mock_exe, _): self.config.parser.update_runtime_variables = mock.Mock() self.config.parser.modules.add("mod_ssl.c") + self.config.parser.modules.add("headers_module") mock_exe.return_value = True # This will create an ssl vhost for certbot.demo self.config.enhance("certbot.demo", "ensure-http-header", "Strict-Transport-Security") - self.assertTrue("headers_module" in self.config.parser.modules) - # Get the ssl vhost for certbot.demo ssl_vhost = self.config.assoc["certbot.demo"] @@ -1115,6 +934,8 @@ class MultipleVhostsTest(util.ApacheTest): def test_http_header_uir(self, mock_exe, _): self.config.parser.update_runtime_variables = mock.Mock() self.config.parser.modules.add("mod_ssl.c") + self.config.parser.modules.add("headers_module") + mock_exe.return_value = True # This will create an ssl vhost for certbot.demo @@ -1151,6 +972,7 @@ class MultipleVhostsTest(util.ApacheTest): @mock.patch("certbot.util.run_script") @mock.patch("certbot.util.exe_exists") def test_redirect_well_formed_http(self, mock_exe, _): + self.config.parser.modules.add("rewrite_module") self.config.parser.update_runtime_variables = mock.Mock() mock_exe.return_value = True self.config.get_version = mock.Mock(return_value=(2, 2)) @@ -1173,8 +995,6 @@ class MultipleVhostsTest(util.ApacheTest): self.assertTrue(rw_engine[0].startswith(self.vh_truth[3].path[:-3])) self.assertTrue(rw_rule[0].startswith(self.vh_truth[3].path[:-3])) - self.assertTrue("rewrite_module" in self.config.parser.modules) - def test_rewrite_rule_exists(self): # Skip the enable mod self.config.parser.modules.add("rewrite_module") @@ -1196,6 +1016,7 @@ class MultipleVhostsTest(util.ApacheTest): @mock.patch("certbot.util.run_script") @mock.patch("certbot.util.exe_exists") def test_redirect_with_existing_rewrite(self, mock_exe, _): + self.config.parser.modules.add("rewrite_module") self.config.parser.update_runtime_variables = mock.Mock() mock_exe.return_value = True self.config.get_version = mock.Mock(return_value=(2, 2, 0)) @@ -1228,6 +1049,7 @@ class MultipleVhostsTest(util.ApacheTest): @mock.patch("certbot.util.run_script") @mock.patch("certbot.util.exe_exists") def test_redirect_with_old_https_redirection(self, mock_exe, _): + self.config.parser.modules.add("rewrite_module") self.config.parser.update_runtime_variables = mock.Mock() mock_exe.return_value = True self.config.get_version = mock.Mock(return_value=(2, 2, 0)) @@ -1365,6 +1187,57 @@ class MultipleVhostsTest(util.ApacheTest): self.config.aug.match.side_effect = RuntimeError self.assertFalse(self.config._check_aug_version()) + def test_enable_site_nondebian(self): + inc_path = "/path/to/wherever" + vhost = self.vh_truth[0] + vhost.enabled = False + vhost.filep = inc_path + self.assertFalse(self.config.parser.find_dir("Include", inc_path)) + self.assertFalse( + os.path.dirname(inc_path) in self.config.parser.existing_paths) + self.config.enable_site(vhost) + self.assertTrue(self.config.parser.find_dir("Include", inc_path)) + self.assertTrue( + os.path.dirname(inc_path) in self.config.parser.existing_paths) + self.assertTrue( + os.path.basename(inc_path) in self.config.parser.existing_paths[ + os.path.dirname(inc_path)]) + + def test_deploy_cert_not_parsed_path(self): + # Make sure that we add include to root config for vhosts when + # handle-sites is false + self.config.parser.modules.add("ssl_module") + self.config.parser.modules.add("mod_ssl.c") + self.config.parser.modules.add("socache_shmcb_module") + tmp_path = os.path.realpath(tempfile.mkdtemp("vhostroot")) + os.chmod(tmp_path, 0o755) + mock_p = "certbot_apache.configurator.ApacheConfigurator._get_ssl_vhost_path" + mock_a = "certbot_apache.parser.ApacheParser.add_include" + + with mock.patch(mock_p) as mock_path: + mock_path.return_value = os.path.join(tmp_path, "whatever.conf") + with mock.patch(mock_a) as mock_add: + self.config.deploy_cert( + "encryption-example.demo", + "example/cert.pem", "example/key.pem", + "example/cert_chain.pem") + # Test that we actually called add_include + self.assertTrue(mock_add.called) + shutil.rmtree(tmp_path) + + @mock.patch("certbot_apache.parser.ApacheParser.parsed_in_original") + def test_choose_vhost_and_servername_addition_parsed(self, mock_parsed): + ret_vh = self.vh_truth[8] + ret_vh.enabled = True + self.config.enable_site(ret_vh) + # Make sure that we return early + self.assertFalse(mock_parsed.called) + + def test_enable_mod_unsupported(self): + self.assertRaises(errors.MisconfigurationError, + self.config.enable_mod, + "whatever") + class AugeasVhostsTest(util.ApacheTest): """Test vhosts with illegal names dependent on augeas version.""" # pylint: disable=protected-access @@ -1378,12 +1251,8 @@ class AugeasVhostsTest(util.ApacheTest): vhost_root=vr) self.config = util.get_apache_configurator( - self.config_path, self.vhost_path, self.config_dir, self.work_dir) - - def tearDown(self): - shutil.rmtree(self.temp_dir) - shutil.rmtree(self.config_dir) - shutil.rmtree(self.work_dir) + self.config_path, self.vhost_path, self.config_dir, + self.work_dir) def test_choosevhost_with_illegal_name(self): self.config.aug = mock.MagicMock() @@ -1461,15 +1330,11 @@ class MultiVhostsTest(util.ApacheTest): vhost_root=vr) self.config = util.get_apache_configurator( - self.config_path, self.vhost_path, self.config_dir, self.work_dir) + self.config_path, self.vhost_path, + self.config_dir, self.work_dir, conf_vhost_path=self.vhost_path) 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]) @@ -1569,11 +1434,11 @@ class InstallSslOptionsConfTest(util.ApacheTest): self.config_path, self.vhost_path, self.config_dir, self.work_dir) def _call(self): - from certbot_apache.configurator import install_ssl_options_conf - install_ssl_options_conf(self.config.mod_ssl_conf, self.config.updated_mod_ssl_conf_digest) + self.config.install_ssl_options_conf(self.config.mod_ssl_conf, + self.config.updated_mod_ssl_conf_digest) def _current_ssl_options_hash(self): - return crypto_util.sha256sum(constants.os_constant("MOD_SSL_CONF_SRC")) + return crypto_util.sha256sum(self.config.constant("MOD_SSL_CONF_SRC")) def _assert_current_file(self): self.assertTrue(os.path.isfile(self.config.mod_ssl_conf)) @@ -1608,7 +1473,8 @@ class InstallSslOptionsConfTest(util.ApacheTest): self._call() self.assertFalse(mock_logger.warning.called) self.assertTrue(os.path.isfile(self.config.mod_ssl_conf)) - self.assertEqual(crypto_util.sha256sum(constants.os_constant("MOD_SSL_CONF_SRC")), + self.assertEqual(crypto_util.sha256sum( + self.config.constant("MOD_SSL_CONF_SRC")), self._current_ssl_options_hash()) self.assertNotEqual(crypto_util.sha256sum(self.config.mod_ssl_conf), self._current_ssl_options_hash()) @@ -1623,7 +1489,8 @@ class InstallSslOptionsConfTest(util.ApacheTest): self.assertEqual(mock_logger.warning.call_args[0][0], "%s has been manually modified; updated file " "saved to %s. We recommend updating %s for security purposes.") - self.assertEqual(crypto_util.sha256sum(constants.os_constant("MOD_SSL_CONF_SRC")), + self.assertEqual(crypto_util.sha256sum( + self.config.constant("MOD_SSL_CONF_SRC")), self._current_ssl_options_hash()) # only print warning once with mock.patch("certbot.plugins.common.logger") as mock_logger: diff --git a/certbot-apache/certbot_apache/tests/constants_test.py b/certbot-apache/certbot_apache/tests/constants_test.py deleted file mode 100644 index 5ab324101..000000000 --- a/certbot-apache/certbot_apache/tests/constants_test.py +++ /dev/null @@ -1,44 +0,0 @@ -"""Test for certbot_apache.configurator.""" - -import mock -import unittest - -from certbot_apache import constants - - -class ConstantsTest(unittest.TestCase): - - @mock.patch("certbot.util.get_os_info") - def test_get_debian_value(self, os_info): - os_info.return_value = ('Debian', '', '') - self.assertEqual(constants.os_constant("vhost_root"), - "/etc/apache2/sites-available") - - @mock.patch("certbot.util.get_os_info") - def test_get_centos_value(self, os_info): - os_info.return_value = ('CentOS Linux', '', '') - self.assertEqual(constants.os_constant("vhost_root"), - "/etc/httpd/conf.d") - - @mock.patch("certbot.util.get_systemd_os_like") - @mock.patch("certbot.util.get_os_info") - def test_get_default_values(self, os_info, os_like): - os_info.return_value = ('Nonexistent Linux', '', '') - os_like.return_value = {} - self.assertFalse(constants.os_constant("handle_mods")) - self.assertEqual(constants.os_constant("server_root"), "/etc/apache2") - self.assertEqual(constants.os_constant("vhost_root"), - "/etc/apache2/sites-available") - - @mock.patch("certbot.util.get_systemd_os_like") - @mock.patch("certbot.util.get_os_info") - def test_get_darwin_like_values(self, os_info, os_like): - os_info.return_value = ('Nonexistent Linux', '', '') - os_like.return_value = ["something", "nonexistent", "darwin"] - self.assertFalse(constants.os_constant("enmod")) - self.assertEqual(constants.os_constant("vhost_root"), - "/etc/apache2/other") - - -if __name__ == "__main__": - unittest.main() # pragma: no cover diff --git a/certbot-apache/certbot_apache/tests/debian_test.py b/certbot-apache/certbot_apache/tests/debian_test.py new file mode 100644 index 000000000..a648101e9 --- /dev/null +++ b/certbot-apache/certbot_apache/tests/debian_test.py @@ -0,0 +1,209 @@ +"""Test for certbot_apache.configurator for Debian overrides""" +import os +import shutil +import unittest + +import mock + +from certbot import errors + +from certbot_apache import apache_util +from certbot_apache import obj +from certbot_apache.tests import util + + +class MultipleVhostsTestDebian(util.ApacheTest): + """Multiple vhost tests for Debian family of distros""" + + _multiprocess_can_split_ = True + + def setUp(self): # pylint: disable=arguments-differ + super(MultipleVhostsTestDebian, self).setUp() + self.config = util.get_apache_configurator( + self.config_path, None, self.config_dir, self.work_dir, + os_info="debian") + self.config = self.mock_deploy_cert(self.config) + self.vh_truth = util.get_vh_truth(self.temp_dir, + "debian_apache_2_4/multiple_vhosts") + + def mock_deploy_cert(self, config): + """A test for a mock deploy cert""" + config.real_deploy_cert = self.config.deploy_cert + + def mocked_deploy_cert(*args, **kwargs): + """a helper to mock a deployed cert""" + g_mod = "certbot_apache.configurator.ApacheConfigurator.enable_mod" + d_mod = "certbot_apache.override_debian.DebianConfigurator.enable_mod" + with mock.patch(g_mod): + with mock.patch(d_mod): + config.real_deploy_cert(*args, **kwargs) + self.config.deploy_cert = mocked_deploy_cert + return self.config + + def test_enable_mod_unsupported_dirs(self): + shutil.rmtree(os.path.join(self.config.parser.root, "mods-enabled")) + self.assertRaises( + errors.NotSupportedError, self.config.enable_mod, "ssl") + + @mock.patch("certbot.util.run_script") + @mock.patch("certbot.util.exe_exists") + @mock.patch("certbot_apache.parser.subprocess.Popen") + def test_enable_mod(self, mock_popen, mock_exe_exists, mock_run_script): + mock_popen().communicate.return_value = ("Define: DUMP_RUN_CFG", "") + mock_popen().returncode = 0 + mock_exe_exists.return_value = True + + self.config.enable_mod("ssl") + self.assertTrue("ssl_module" in self.config.parser.modules) + self.assertTrue("mod_ssl.c" in self.config.parser.modules) + + self.assertTrue(mock_run_script.called) + + def test_deploy_cert_enable_new_vhost(self): + # Create + ssl_vhost = self.config.make_vhost_ssl(self.vh_truth[0]) + self.config.parser.modules.add("ssl_module") + self.config.parser.modules.add("mod_ssl.c") + self.assertFalse(ssl_vhost.enabled) + self.config.deploy_cert( + "encryption-example.demo", "example/cert.pem", "example/key.pem", + "example/cert_chain.pem", "example/fullchain.pem") + self.assertTrue(ssl_vhost.enabled) + # Make sure that we don't error out if symlink already exists + ssl_vhost.enabled = False + self.assertFalse(ssl_vhost.enabled) + self.config.deploy_cert( + "encryption-example.demo", "example/cert.pem", "example/key.pem", + "example/cert_chain.pem", "example/fullchain.pem") + self.assertTrue(ssl_vhost.enabled) + + def test_enable_site_failure(self): + self.config.parser.root = "/tmp/nonexistent" + with mock.patch("os.path.isdir") as mock_dir: + mock_dir.return_value = True + with mock.patch("os.path.islink") as mock_link: + mock_link.return_value = False + self.assertRaises( + errors.NotSupportedError, + self.config.enable_site, + obj.VirtualHost("asdf", "afsaf", set(), False, False)) + + def test_deploy_cert_newssl(self): + self.config = util.get_apache_configurator( + self.config_path, self.vhost_path, self.config_dir, + self.work_dir, version=(2, 4, 16)) + self.config = self.mock_deploy_cert(self.config) + self.config.parser.modules.add("ssl_module") + self.config.parser.modules.add("mod_ssl.c") + + # Get the default 443 vhost + self.config.assoc["random.demo"] = self.vh_truth[1] + self.config.deploy_cert( + "random.demo", "example/cert.pem", "example/key.pem", + "example/cert_chain.pem", "example/fullchain.pem") + self.config.save() + + # Verify ssl_module was enabled. + self.assertTrue(self.vh_truth[1].enabled) + self.assertTrue("ssl_module" in self.config.parser.modules) + + loc_cert = self.config.parser.find_dir( + "sslcertificatefile", "example/fullchain.pem", + self.vh_truth[1].path) + loc_key = self.config.parser.find_dir( + "sslcertificateKeyfile", "example/key.pem", self.vh_truth[1].path) + + # Verify one directive was found in the correct file + self.assertEqual(len(loc_cert), 1) + self.assertEqual( + apache_util.get_file_path(loc_cert[0]), + self.vh_truth[1].filep) + + self.assertEqual(len(loc_key), 1) + self.assertEqual( + apache_util.get_file_path(loc_key[0]), + self.vh_truth[1].filep) + + def test_deploy_cert_newssl_no_fullchain(self): + self.config = util.get_apache_configurator( + self.config_path, self.vhost_path, self.config_dir, + self.work_dir, version=(2, 4, 16)) + self.config = self.mock_deploy_cert(self.config) + self.config.parser.modules.add("ssl_module") + self.config.parser.modules.add("mod_ssl.c") + + # Get the default 443 vhost + self.config.assoc["random.demo"] = self.vh_truth[1] + self.assertRaises(errors.PluginError, + lambda: self.config.deploy_cert( + "random.demo", "example/cert.pem", + "example/key.pem")) + + def test_deploy_cert_old_apache_no_chain(self): + self.config = util.get_apache_configurator( + self.config_path, self.vhost_path, self.config_dir, + self.work_dir, version=(2, 4, 7)) + self.config = self.mock_deploy_cert(self.config) + self.config.parser.modules.add("ssl_module") + self.config.parser.modules.add("mod_ssl.c") + + # Get the default 443 vhost + self.config.assoc["random.demo"] = self.vh_truth[1] + self.assertRaises(errors.PluginError, + lambda: self.config.deploy_cert( + "random.demo", "example/cert.pem", + "example/key.pem")) + + @mock.patch("certbot.util.run_script") + @mock.patch("certbot.util.exe_exists") + def test_ocsp_stapling_enable_mod(self, mock_exe, _): + self.config.parser.update_runtime_variables = mock.Mock() + self.config.parser.modules.add("mod_ssl.c") + self.config.get_version = mock.Mock(return_value=(2, 4, 7)) + mock_exe.return_value = True + self.config.enhance("certbot.demo", "staple-ocsp") + self.assertTrue("socache_shmcb_module" in self.config.parser.modules) + + @mock.patch("certbot.util.run_script") + @mock.patch("certbot.util.exe_exists") + def test_ensure_http_header_enable_mod(self, mock_exe, _): + self.config.parser.update_runtime_variables = mock.Mock() + self.config.parser.modules.add("mod_ssl.c") + mock_exe.return_value = True + + # This will create an ssl vhost for certbot.demo + self.config.enhance("certbot.demo", "ensure-http-header", + "Strict-Transport-Security") + self.assertTrue("headers_module" in self.config.parser.modules) + + @mock.patch("certbot.util.run_script") + @mock.patch("certbot.util.exe_exists") + def test_redirect_enable_mod(self, mock_exe, _): + self.config.parser.update_runtime_variables = mock.Mock() + mock_exe.return_value = True + self.config.get_version = mock.Mock(return_value=(2, 2)) + # This will create an ssl vhost for certbot.demo + self.config.enhance("certbot.demo", "redirect") + self.assertTrue("rewrite_module" in self.config.parser.modules) + + def test_enable_site_already_enabled(self): + self.assertTrue(self.vh_truth[1].enabled) + self.config.enable_site(self.vh_truth[1]) + + def test_enable_site_call_parent(self): + with mock.patch( + "certbot_apache.configurator.ApacheConfigurator.enable_site") as e_s: + self.config.parser.root = "/tmp/nonexistent" + vh = self.vh_truth[0] + vh.enabled = False + self.config.enable_site(vh) + self.assertTrue(e_s.called) + + @mock.patch("certbot.util.exe_exists") + def test_enable_mod_no_disable(self, mock_exe_exists): + mock_exe_exists.return_value = False + self.assertRaises( + errors.MisconfigurationError, self.config.enable_mod, "ssl") + +if __name__ == "__main__": + unittest.main() # pragma: no cover diff --git a/certbot-apache/certbot_apache/tests/entrypoint_test.py b/certbot-apache/certbot_apache/tests/entrypoint_test.py new file mode 100644 index 000000000..c04611465 --- /dev/null +++ b/certbot-apache/certbot_apache/tests/entrypoint_test.py @@ -0,0 +1,41 @@ +"""Test for certbot_apache.entrypoint for override class resolution""" +import unittest + +import mock + +from certbot_apache import configurator +from certbot_apache import entrypoint + +class EntryPointTest(unittest.TestCase): + """Entrypoint tests""" + + _multiprocess_can_split_ = True + + def test_get_configurator(self): + + with mock.patch("certbot.util.get_os_info") as mock_info: + for distro in entrypoint.OVERRIDE_CLASSES.keys(): + mock_info.return_value = (distro, "whatever") + self.assertEqual(entrypoint.get_configurator(), + entrypoint.OVERRIDE_CLASSES[distro]) + + def test_nonexistent_like(self): + with mock.patch("certbot.util.get_os_info") as mock_info: + mock_info.return_value = ("nonexistent", "irrelevant") + with mock.patch("certbot.util.get_systemd_os_like") as mock_like: + for like in entrypoint.OVERRIDE_CLASSES.keys(): + mock_like.return_value = [like] + self.assertEqual(entrypoint.get_configurator(), + entrypoint.OVERRIDE_CLASSES[like]) + + def test_nonexistent_generic(self): + with mock.patch("certbot.util.get_os_info") as mock_info: + mock_info.return_value = ("nonexistent", "irrelevant") + with mock.patch("certbot.util.get_systemd_os_like") as mock_like: + mock_like.return_value = ["unknonwn"] + self.assertEqual(entrypoint.get_configurator(), + configurator.ApacheConfigurator) + + +if __name__ == "__main__": + unittest.main() # pragma: no cover diff --git a/certbot-apache/certbot_apache/tests/gentoo_test.py b/certbot-apache/certbot_apache/tests/gentoo_test.py new file mode 100644 index 000000000..0f2b96818 --- /dev/null +++ b/certbot-apache/certbot_apache/tests/gentoo_test.py @@ -0,0 +1,86 @@ +"""Test for certbot_apache.configurator for Gentoo overrides""" +import os +import unittest + +from certbot_apache import override_gentoo +from certbot_apache import obj +from certbot_apache.tests import util + +def get_vh_truth(temp_dir, config_name): + """Return the ground truth for the specified directory.""" + prefix = os.path.join( + temp_dir, config_name, "apache2/vhosts.d") + + aug_pre = "/files" + prefix + vh_truth = [ + obj.VirtualHost( + os.path.join(prefix, "gentoo.example.com.conf"), + os.path.join(aug_pre, "gentoo.example.com.conf/VirtualHost"), + set([obj.Addr.fromstring("*:80")]), + False, True, "gentoo.example.com"), + obj.VirtualHost( + os.path.join(prefix, "00_default_vhost.conf"), + os.path.join(aug_pre, "00_default_vhost.conf/IfDefine/VirtualHost"), + set([obj.Addr.fromstring("*:80")]), + False, True, "localhost"), + obj.VirtualHost( + os.path.join(prefix, "00_default_ssl_vhost.conf"), + os.path.join(aug_pre, + "00_default_ssl_vhost.conf" + + "/IfDefine/IfDefine/IfModule/VirtualHost"), + set([obj.Addr.fromstring("_default_:443")]), + True, True, "localhost") + ] + return vh_truth + +class MultipleVhostsTestGentoo(util.ApacheTest): + """Multiple vhost tests for non-debian distro""" + + _multiprocess_can_split_ = True + + def setUp(self): # pylint: disable=arguments-differ + test_dir = "gentoo_apache/apache" + config_root = "gentoo_apache/apache/apache2" + vhost_root = "gentoo_apache/apache/apache2/vhosts.d" + super(MultipleVhostsTestGentoo, self).setUp(test_dir=test_dir, + config_root=config_root, + vhost_root=vhost_root) + + self.config = util.get_apache_configurator( + self.config_path, self.vhost_path, self.config_dir, self.work_dir, + os_info="gentoo") + self.vh_truth = get_vh_truth( + self.temp_dir, "gentoo_apache/apache") + + def test_get_parser(self): + self.assertTrue(isinstance(self.config.parser, + override_gentoo.GentooParser)) + + def test_get_virtual_hosts(self): + """Make sure all vhosts are being properly found.""" + vhs = self.config.get_virtual_hosts() + self.assertEqual(len(vhs), 3) + found = 0 + + for vhost in vhs: + for gentoo_truth in self.vh_truth: + if vhost == gentoo_truth: + found += 1 + break + else: + raise Exception("Missed: %s" % vhost) # pragma: no cover + self.assertEqual(found, 3) + + def test_get_sysconfig_vars(self): + """Make sure we read the Gentoo APACHE2_OPTS variable correctly""" + defines = ['DEFAULT_VHOST', 'INFO', + 'SSL', 'SSL_DEFAULT_VHOST', 'LANGUAGE'] + self.config.parser.apacheconfig_filep = os.path.realpath( + os.path.join(self.config.parser.root, "../conf.d/apache2")) + self.config.parser.variables = {} + self.config.parser.update_runtime_variables() + for define in defines: + self.assertTrue(define in self.config.parser.variables.keys()) + +if __name__ == "__main__": + unittest.main() # pragma: no cover diff --git a/certbot-apache/certbot_apache/tests/parser_test.py b/certbot-apache/certbot_apache/tests/parser_test.py index 38c5b29c7..a9eb129c2 100644 --- a/certbot-apache/certbot_apache/tests/parser_test.py +++ b/certbot-apache/certbot_apache/tests/parser_test.py @@ -120,17 +120,18 @@ class BasicParserTest(util.ParserTest): @mock.patch("certbot_apache.parser.ApacheParser.find_dir") @mock.patch("certbot_apache.parser.ApacheParser.get_arg") - def test_init_modules_bad_syntax(self, mock_arg, mock_find): + def test_parse_modules_bad_syntax(self, mock_arg, mock_find): mock_find.return_value = ["1", "2", "3", "4", "5", "6", "7", "8"] mock_arg.return_value = None with mock.patch("certbot_apache.parser.logger") as mock_logger: - self.parser.init_modules() + self.parser.parse_modules() # Make sure that we got None return value and logged the file self.assertTrue(mock_logger.debug.called) + @mock.patch("certbot_apache.parser.ApacheParser.find_dir") @mock.patch("certbot_apache.parser.ApacheParser._get_runtime_cfg") - def test_update_runtime_variables(self, mock_cfg): - mock_cfg.return_value = ( + def test_update_runtime_variables(self, mock_cfg, _): + define_val = ( 'ServerRoot: "/etc/apache2"\n' 'Main DocumentRoot: "/var/www"\n' 'Main ErrorLog: "/var/log/apache2/error.log"\n' @@ -147,11 +148,113 @@ class BasicParserTest(util.ParserTest): 'User: name="www-data" id=33 not_used\n' 'Group: name="www-data" id=33 not_used\n' ) + inc_val = ( + 'Included configuration files:\n' + ' (*) /etc/apache2/apache2.conf\n' + ' (146) /etc/apache2/mods-enabled/access_compat.load\n' + ' (146) /etc/apache2/mods-enabled/alias.load\n' + ' (146) /etc/apache2/mods-enabled/auth_basic.load\n' + ' (146) /etc/apache2/mods-enabled/authn_core.load\n' + ' (146) /etc/apache2/mods-enabled/authn_file.load\n' + ' (146) /etc/apache2/mods-enabled/authz_core.load\n' + ' (146) /etc/apache2/mods-enabled/authz_host.load\n' + ' (146) /etc/apache2/mods-enabled/authz_user.load\n' + ' (146) /etc/apache2/mods-enabled/autoindex.load\n' + ' (146) /etc/apache2/mods-enabled/deflate.load\n' + ' (146) /etc/apache2/mods-enabled/dir.load\n' + ' (146) /etc/apache2/mods-enabled/env.load\n' + ' (146) /etc/apache2/mods-enabled/filter.load\n' + ' (146) /etc/apache2/mods-enabled/mime.load\n' + ' (146) /etc/apache2/mods-enabled/mpm_event.load\n' + ' (146) /etc/apache2/mods-enabled/negotiation.load\n' + ' (146) /etc/apache2/mods-enabled/reqtimeout.load\n' + ' (146) /etc/apache2/mods-enabled/setenvif.load\n' + ' (146) /etc/apache2/mods-enabled/socache_shmcb.load\n' + ' (146) /etc/apache2/mods-enabled/ssl.load\n' + ' (146) /etc/apache2/mods-enabled/status.load\n' + ' (147) /etc/apache2/mods-enabled/alias.conf\n' + ' (147) /etc/apache2/mods-enabled/autoindex.conf\n' + ' (147) /etc/apache2/mods-enabled/deflate.conf\n' + ) + mod_val = ( + 'Loaded Modules:\n' + ' core_module (static)\n' + ' so_module (static)\n' + ' watchdog_module (static)\n' + ' http_module (static)\n' + ' log_config_module (static)\n' + ' logio_module (static)\n' + ' version_module (static)\n' + ' unixd_module (static)\n' + ' access_compat_module (shared)\n' + ' alias_module (shared)\n' + ' auth_basic_module (shared)\n' + ' authn_core_module (shared)\n' + ' authn_file_module (shared)\n' + ' authz_core_module (shared)\n' + ' authz_host_module (shared)\n' + ' authz_user_module (shared)\n' + ' autoindex_module (shared)\n' + ' deflate_module (shared)\n' + ' dir_module (shared)\n' + ' env_module (shared)\n' + ' filter_module (shared)\n' + ' mime_module (shared)\n' + ' mpm_event_module (shared)\n' + ' negotiation_module (shared)\n' + ' reqtimeout_module (shared)\n' + ' setenvif_module (shared)\n' + ' socache_shmcb_module (shared)\n' + ' ssl_module (shared)\n' + ' status_module (shared)\n' + ) + + def mock_get_vars(cmd): + """Mock command output""" + if cmd[-1] == "DUMP_RUN_CFG": + return define_val + elif cmd[-1] == "DUMP_INCLUDES": + return inc_val + elif cmd[-1] == "DUMP_MODULES": + return mod_val + + mock_cfg.side_effect = mock_get_vars + expected_vars = {"TEST": "", "U_MICH": "", "TLS": "443", "example_path": "Documents/path"} - self.parser.update_runtime_variables() - self.assertEqual(self.parser.variables, expected_vars) + self.parser.modules = set() + with mock.patch( + "certbot_apache.parser.ApacheParser.parse_file") as mock_parse: + self.parser.update_runtime_variables() + self.assertEqual(self.parser.variables, expected_vars) + self.assertEqual(len(self.parser.modules), 58) + # None of the includes in inc_val should be in parsed paths. + # Make sure we tried to include them all. + self.assertEqual(mock_parse.call_count, 25) + + @mock.patch("certbot_apache.parser.ApacheParser.find_dir") + @mock.patch("certbot_apache.parser.ApacheParser._get_runtime_cfg") + def test_update_runtime_variables_alt_values(self, mock_cfg, _): + inc_val = ( + 'Included configuration files:\n' + ' (*) {0}\n' + ' (146) /etc/apache2/mods-enabled/access_compat.load\n' + ' (146) {1}/mods-enabled/alias.load\n' + ).format(self.parser.loc["root"], + os.path.dirname(self.parser.loc["root"])) + + mock_cfg.return_value = inc_val + self.parser.modules = set() + + with mock.patch( + "certbot_apache.parser.ApacheParser.parse_file") as mock_parse: + self.parser.update_runtime_variables() + # No matching modules should have been found + self.assertEqual(len(self.parser.modules), 0) + # Only one of the three includes do not exist in already parsed + # path derived from root configuration Include statements + self.assertEqual(mock_parse.call_count, 1) @mock.patch("certbot_apache.parser.ApacheParser._get_runtime_cfg") def test_update_runtime_vars_bad_output(self, mock_cfg): @@ -162,7 +265,7 @@ class BasicParserTest(util.ParserTest): self.assertRaises( errors.PluginError, self.parser.update_runtime_variables) - @mock.patch("certbot_apache.constants.os_constant") + @mock.patch("certbot_apache.configurator.ApacheConfigurator.constant") @mock.patch("certbot_apache.parser.subprocess.Popen") def test_update_runtime_vars_bad_ctl(self, mock_popen, mock_const): mock_popen.side_effect = OSError @@ -198,7 +301,7 @@ class ParserInitTest(util.ApacheTest): self.assertRaises( errors.PluginError, ApacheParser, self.aug, os.path.relpath(self.config_path), - "/dummy/vhostpath", version=(2, 2, 22)) + "/dummy/vhostpath", version=(2, 2, 22), configurator=self.config) def test_root_normalized(self): from certbot_apache.parser import ApacheParser @@ -210,7 +313,7 @@ class ParserInitTest(util.ApacheTest): "debian_apache_2_4/////multiple_vhosts/../multiple_vhosts/apache2") parser = ApacheParser(self.aug, path, - "/dummy/vhostpath") + "/dummy/vhostpath", configurator=self.config) self.assertEqual(parser.root, self.config_path) @@ -220,7 +323,7 @@ class ParserInitTest(util.ApacheTest): "update_runtime_variables"): parser = ApacheParser( self.aug, os.path.relpath(self.config_path), - "/dummy/vhostpath") + "/dummy/vhostpath", configurator=self.config) self.assertEqual(parser.root, self.config_path) @@ -230,7 +333,7 @@ class ParserInitTest(util.ApacheTest): "update_runtime_variables"): parser = ApacheParser( self.aug, self.config_path + os.path.sep, - "/dummy/vhostpath") + "/dummy/vhostpath", configurator=self.config) self.assertEqual(parser.root, self.config_path) diff --git a/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.d/README b/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.d/README new file mode 100644 index 000000000..f5e96615a --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.d/README @@ -0,0 +1,9 @@ + +This directory holds configuration files for the Apache HTTP Server; +any files in this directory which have the ".conf" extension will be +processed as httpd configuration files. The directory is used in +addition to the directory /etc/httpd/conf.modules.d/, which contains +configuration files necessary to load modules. + +Files are processed in alphabetical order. + diff --git a/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.d/autoindex.conf b/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.d/autoindex.conf new file mode 100644 index 000000000..a85cf5dca --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.d/autoindex.conf @@ -0,0 +1,94 @@ +# +# Directives controlling the display of server-generated directory listings. +# +# Required modules: mod_authz_core, mod_authz_host, +# mod_autoindex, mod_alias +# +# To see the listing of a directory, the Options directive for the +# directory must include "Indexes", and the directory must not contain +# a file matching those listed in the DirectoryIndex directive. +# + +# +# IndexOptions: Controls the appearance of server-generated directory +# listings. +# +IndexOptions FancyIndexing HTMLTable VersionSort + +# We include the /icons/ alias for FancyIndexed directory listings. If +# you do not use FancyIndexing, you may comment this out. +# +Alias /icons/ "/usr/share/httpd/icons/" + + + Options Indexes MultiViews FollowSymlinks + AllowOverride None + Require all granted + + +# +# AddIcon* directives tell the server which icon to show for different +# files or filename extensions. These are only displayed for +# FancyIndexed directories. +# +AddIconByEncoding (CMP,/icons/compressed.gif) x-compress x-gzip + +AddIconByType (TXT,/icons/text.gif) text/* +AddIconByType (IMG,/icons/image2.gif) image/* +AddIconByType (SND,/icons/sound2.gif) audio/* +AddIconByType (VID,/icons/movie.gif) video/* + +AddIcon /icons/binary.gif .bin .exe +AddIcon /icons/binhex.gif .hqx +AddIcon /icons/tar.gif .tar +AddIcon /icons/world2.gif .wrl .wrl.gz .vrml .vrm .iv +AddIcon /icons/compressed.gif .Z .z .tgz .gz .zip +AddIcon /icons/a.gif .ps .ai .eps +AddIcon /icons/layout.gif .html .shtml .htm .pdf +AddIcon /icons/text.gif .txt +AddIcon /icons/c.gif .c +AddIcon /icons/p.gif .pl .py +AddIcon /icons/f.gif .for +AddIcon /icons/dvi.gif .dvi +AddIcon /icons/uuencoded.gif .uu +AddIcon /icons/script.gif .conf .sh .shar .csh .ksh .tcl +AddIcon /icons/tex.gif .tex +AddIcon /icons/bomb.gif /core +AddIcon /icons/bomb.gif */core.* + +AddIcon /icons/back.gif .. +AddIcon /icons/hand.right.gif README +AddIcon /icons/folder.gif ^^DIRECTORY^^ +AddIcon /icons/blank.gif ^^BLANKICON^^ + +# +# DefaultIcon is which icon to show for files which do not have an icon +# explicitly set. +# +DefaultIcon /icons/unknown.gif + +# +# AddDescription allows you to place a short description after a file in +# server-generated indexes. These are only displayed for FancyIndexed +# directories. +# Format: AddDescription "description" filename +# +#AddDescription "GZIP compressed document" .gz +#AddDescription "tar archive" .tar +#AddDescription "GZIP compressed tar archive" .tgz + +# +# ReadmeName is the name of the README file the server will look for by +# default, and append to directory listings. +# +# HeaderName is the name of a file which should be prepended to +# directory indexes. +ReadmeName README.html +HeaderName HEADER.html + +# +# IndexIgnore is a set of filenames which directory indexing should ignore +# and not include in the listing. Shell-style wildcarding is permitted. +# +IndexIgnore .??* *~ *# HEADER* README* RCS CVS *,v *,t + diff --git a/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.d/centos.example.com.conf b/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.d/centos.example.com.conf new file mode 100644 index 000000000..de7ac2777 --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.d/centos.example.com.conf @@ -0,0 +1,7 @@ + + ServerName centos.example.com + ServerAdmin webmaster@localhost + DocumentRoot /var/www/html + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined + diff --git a/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.d/ssl.conf b/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.d/ssl.conf new file mode 100644 index 000000000..6e2502e9a --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.d/ssl.conf @@ -0,0 +1,211 @@ +# +# When we also provide SSL we have to listen to the +# the HTTPS port in addition. +# +Listen 443 https + +## +## SSL Global Context +## +## All SSL configuration in this context applies both to +## the main server and all SSL-enabled virtual hosts. +## + +# Pass Phrase Dialog: +# Configure the pass phrase gathering process. +# The filtering dialog program (`builtin' is a internal +# terminal dialog) has to provide the pass phrase on stdout. +SSLPassPhraseDialog exec:/usr/libexec/httpd-ssl-pass-dialog + +# Inter-Process Session Cache: +# Configure the SSL Session Cache: First the mechanism +# to use and second the expiring timeout (in seconds). +SSLSessionCache shmcb:/run/httpd/sslcache(512000) +SSLSessionCacheTimeout 300 + +# Pseudo Random Number Generator (PRNG): +# Configure one or more sources to seed the PRNG of the +# SSL library. The seed data should be of good random quality. +# WARNING! On some platforms /dev/random blocks if not enough entropy +# is available. This means you then cannot use the /dev/random device +# because it would lead to very long connection times (as long as +# it requires to make more entropy available). But usually those +# platforms additionally provide a /dev/urandom device which doesn't +# block. So, if available, use this one instead. Read the mod_ssl User +# Manual for more details. +SSLRandomSeed startup file:/dev/urandom 256 +SSLRandomSeed connect builtin +#SSLRandomSeed startup file:/dev/random 512 +#SSLRandomSeed connect file:/dev/random 512 +#SSLRandomSeed connect file:/dev/urandom 512 + +# +# Use "SSLCryptoDevice" to enable any supported hardware +# accelerators. Use "openssl engine -v" to list supported +# engine names. NOTE: If you enable an accelerator and the +# server does not start, consult the error logs and ensure +# your accelerator is functioning properly. +# +SSLCryptoDevice builtin +#SSLCryptoDevice ubsec + +## +## SSL Virtual Host Context +## + + + +# General setup for the virtual host, inherited from global configuration +#DocumentRoot "/var/www/html" +#ServerName www.example.com:443 + +# Use separate log files for the SSL virtual host; note that LogLevel +# is not inherited from httpd.conf. +ErrorLog logs/ssl_error_log +TransferLog logs/ssl_access_log +LogLevel warn + +# SSL Engine Switch: +# Enable/Disable SSL for this virtual host. +SSLEngine on + +# SSL Protocol support: +# List the enable protocol levels with which clients will be able to +# connect. Disable SSLv2 access by default: +SSLProtocol all -SSLv2 + +# SSL Cipher Suite: +# List the ciphers that the client is permitted to negotiate. +# See the mod_ssl documentation for a complete list. +SSLCipherSuite HIGH:MEDIUM:!aNULL:!MD5:!SEED:!IDEA + +# Speed-optimized SSL Cipher configuration: +# If speed is your main concern (on busy HTTPS servers e.g.), +# you might want to force clients to specific, performance +# optimized ciphers. In this case, prepend those ciphers +# to the SSLCipherSuite list, and enable SSLHonorCipherOrder. +# Caveat: by giving precedence to RC4-SHA and AES128-SHA +# (as in the example below), most connections will no longer +# have perfect forward secrecy - if the server's key is +# compromised, captures of past or future traffic must be +# considered compromised, too. +#SSLCipherSuite RC4-SHA:AES128-SHA:HIGH:MEDIUM:!aNULL:!MD5 +#SSLHonorCipherOrder on + +# Server Certificate: +# Point SSLCertificateFile at a PEM encoded certificate. If +# the certificate is encrypted, then you will be prompted for a +# pass phrase. Note that a kill -HUP will prompt again. A new +# certificate can be generated using the genkey(1) command. + +# Server Private Key: +# If the key is not combined with the certificate, use this +# directive to point at the key file. Keep in mind that if +# you've both a RSA and a DSA private key you can configure +# both in parallel (to also allow the use of DSA ciphers, etc.) + +# Server Certificate Chain: +# Point SSLCertificateChainFile at a file containing the +# concatenation of PEM encoded CA certificates which form the +# certificate chain for the server certificate. Alternatively +# the referenced file can be the same as SSLCertificateFile +# when the CA certificates are directly appended to the server +# certificate for convinience. +#SSLCertificateChainFile /etc/pki/tls/certs/server-chain.crt + +# Certificate Authority (CA): +# Set the CA certificate verification path where to find CA +# certificates for client authentication or alternatively one +# huge file containing all of them (file must be PEM encoded) +#SSLCACertificateFile /etc/pki/tls/certs/ca-bundle.crt + +# Client Authentication (Type): +# Client certificate verification type and depth. Types are +# none, optional, require and optional_no_ca. Depth is a +# number which specifies how deeply to verify the certificate +# issuer chain before deciding the certificate is not valid. +#SSLVerifyClient require +#SSLVerifyDepth 10 + +# Access Control: +# With SSLRequire you can do per-directory access control based +# on arbitrary complex boolean expressions containing server +# variable checks and other lookup directives. The syntax is a +# mixture between C and Perl. See the mod_ssl documentation +# for more details. +# +#SSLRequire ( %{SSL_CIPHER} !~ m/^(EXP|NULL)/ \ +# and %{SSL_CLIENT_S_DN_O} eq "Snake Oil, Ltd." \ +# and %{SSL_CLIENT_S_DN_OU} in {"Staff", "CA", "Dev"} \ +# and %{TIME_WDAY} >= 1 and %{TIME_WDAY} <= 5 \ +# and %{TIME_HOUR} >= 8 and %{TIME_HOUR} <= 20 ) \ +# or %{REMOTE_ADDR} =~ m/^192\.76\.162\.[0-9]+$/ +# + +# SSL Engine Options: +# Set various options for the SSL engine. +# o FakeBasicAuth: +# Translate the client X.509 into a Basic Authorisation. This means that +# the standard Auth/DBMAuth methods can be used for access control. The +# user name is the `one line' version of the client's X.509 certificate. +# Note that no password is obtained from the user. Every entry in the user +# file needs this password: `xxj31ZMTZzkVA'. +# o ExportCertData: +# This exports two additional environment variables: SSL_CLIENT_CERT and +# SSL_SERVER_CERT. These contain the PEM-encoded certificates of the +# server (always existing) and the client (only existing when client +# authentication is used). This can be used to import the certificates +# into CGI scripts. +# o StdEnvVars: +# This exports the standard SSL/TLS related `SSL_*' environment variables. +# Per default this exportation is switched off for performance reasons, +# because the extraction step is an expensive operation and is usually +# useless for serving static content. So one usually enables the +# exportation for CGI and SSI requests only. +# o StrictRequire: +# This denies access when "SSLRequireSSL" or "SSLRequire" applied even +# under a "Satisfy any" situation, i.e. when it applies access is denied +# and no other module can change it. +# o OptRenegotiate: +# This enables optimized SSL connection renegotiation handling when SSL +# directives are used in per-directory context. +#SSLOptions +FakeBasicAuth +ExportCertData +StrictRequire + + SSLOptions +StdEnvVars + + + SSLOptions +StdEnvVars + + +# SSL Protocol Adjustments: +# The safe and default but still SSL/TLS standard compliant shutdown +# approach is that mod_ssl sends the close notify alert but doesn't wait for +# the close notify alert from client. When you need a different shutdown +# approach you can use one of the following variables: +# o ssl-unclean-shutdown: +# This forces an unclean shutdown when the connection is closed, i.e. no +# SSL close notify alert is send or allowed to received. This violates +# the SSL/TLS standard but is needed for some brain-dead browsers. Use +# this when you receive I/O errors because of the standard approach where +# mod_ssl sends the close notify alert. +# o ssl-accurate-shutdown: +# This forces an accurate shutdown when the connection is closed, i.e. a +# SSL close notify alert is send and mod_ssl waits for the close notify +# alert of the client. This is 100% SSL/TLS standard compliant, but in +# practice often causes hanging connections with brain-dead browsers. Use +# this only for browsers where you know that their SSL implementation +# works correctly. +# Notice: Most problems of broken clients are also related to the HTTP +# keep-alive facility, so you usually additionally want to disable +# keep-alive for those clients, too. Use variable "nokeepalive" for this. +# Similarly, one has to force some clients to use HTTP/1.0 to workaround +# their broken HTTP/1.1 implementation. Use variables "downgrade-1.0" and +# "force-response-1.0" for this. +BrowserMatch "MSIE [2-5]" nokeepalive ssl-unclean-shutdown downgrade-1.0 force-response-1.0 + +# Per-Server Logging: +# The home of a custom SSL log file. Use this when you want a +# compact non-error SSL logfile on a virtual host basis. +CustomLog logs/ssl_request_log "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b" + + diff --git a/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.d/userdir.conf b/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.d/userdir.conf new file mode 100644 index 000000000..b5d7a49ef --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.d/userdir.conf @@ -0,0 +1,36 @@ +# +# UserDir: The name of the directory that is appended onto a user's home +# directory if a ~user request is received. +# +# The path to the end user account 'public_html' directory must be +# accessible to the webserver userid. This usually means that ~userid +# must have permissions of 711, ~userid/public_html must have permissions +# of 755, and documents contained therein must be world-readable. +# Otherwise, the client will only receive a "403 Forbidden" message. +# + + # + # UserDir is disabled by default since it can confirm the presence + # of a username on the system (depending on home directory + # permissions). + # + UserDir disabled + + # + # To enable requests to /~user/ to serve the user's public_html + # directory, remove the "UserDir disabled" line above, and uncomment + # the following line instead: + # + #UserDir public_html + + +# +# Control access to UserDir directories. The following is an example +# for a site where these directories are restricted to read-only. +# + + AllowOverride FileInfo AuthConfig Limit Indexes + Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec + Require method GET POST OPTIONS + + diff --git a/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.d/welcome.conf b/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.d/welcome.conf new file mode 100644 index 000000000..c1b6c11d9 --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.d/welcome.conf @@ -0,0 +1,22 @@ +# +# This configuration file enables the default "Welcome" page if there +# is no default index page present for the root URL. To disable the +# Welcome page, comment out all the lines below. +# +# NOTE: if this file is removed, it will be restored on upgrades. +# + + Options -Indexes + ErrorDocument 403 /.noindex.html + + + + AllowOverride None + Require all granted + + +Alias /.noindex.html /usr/share/httpd/noindex/index.html +Alias /noindex/css/bootstrap.min.css /usr/share/httpd/noindex/css/bootstrap.min.css +Alias /noindex/css/open-sans.css /usr/share/httpd/noindex/css/open-sans.css +Alias /images/apache_pb.gif /usr/share/httpd/noindex/images/apache_pb.gif +Alias /images/poweredby.png /usr/share/httpd/noindex/images/poweredby.png diff --git a/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.modules.d/00-base.conf b/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.modules.d/00-base.conf new file mode 100644 index 000000000..31d979f20 --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.modules.d/00-base.conf @@ -0,0 +1,77 @@ +# +# This file loads most of the modules included with the Apache HTTP +# Server itself. +# + +LoadModule access_compat_module modules/mod_access_compat.so +LoadModule actions_module modules/mod_actions.so +LoadModule alias_module modules/mod_alias.so +LoadModule allowmethods_module modules/mod_allowmethods.so +LoadModule auth_basic_module modules/mod_auth_basic.so +LoadModule auth_digest_module modules/mod_auth_digest.so +LoadModule authn_anon_module modules/mod_authn_anon.so +LoadModule authn_core_module modules/mod_authn_core.so +LoadModule authn_dbd_module modules/mod_authn_dbd.so +LoadModule authn_dbm_module modules/mod_authn_dbm.so +LoadModule authn_file_module modules/mod_authn_file.so +LoadModule authn_socache_module modules/mod_authn_socache.so +LoadModule authz_core_module modules/mod_authz_core.so +LoadModule authz_dbd_module modules/mod_authz_dbd.so +LoadModule authz_dbm_module modules/mod_authz_dbm.so +LoadModule authz_groupfile_module modules/mod_authz_groupfile.so +LoadModule authz_host_module modules/mod_authz_host.so +LoadModule authz_owner_module modules/mod_authz_owner.so +LoadModule authz_user_module modules/mod_authz_user.so +LoadModule autoindex_module modules/mod_autoindex.so +LoadModule cache_module modules/mod_cache.so +LoadModule cache_disk_module modules/mod_cache_disk.so +LoadModule data_module modules/mod_data.so +LoadModule dbd_module modules/mod_dbd.so +LoadModule deflate_module modules/mod_deflate.so +LoadModule dir_module modules/mod_dir.so +LoadModule dumpio_module modules/mod_dumpio.so +LoadModule echo_module modules/mod_echo.so +LoadModule env_module modules/mod_env.so +LoadModule expires_module modules/mod_expires.so +LoadModule ext_filter_module modules/mod_ext_filter.so +LoadModule filter_module modules/mod_filter.so +LoadModule headers_module modules/mod_headers.so +LoadModule include_module modules/mod_include.so +LoadModule info_module modules/mod_info.so +LoadModule log_config_module modules/mod_log_config.so +LoadModule logio_module modules/mod_logio.so +LoadModule mime_magic_module modules/mod_mime_magic.so +LoadModule mime_module modules/mod_mime.so +LoadModule negotiation_module modules/mod_negotiation.so +LoadModule remoteip_module modules/mod_remoteip.so +LoadModule reqtimeout_module modules/mod_reqtimeout.so +LoadModule rewrite_module modules/mod_rewrite.so +LoadModule setenvif_module modules/mod_setenvif.so +LoadModule slotmem_plain_module modules/mod_slotmem_plain.so +LoadModule slotmem_shm_module modules/mod_slotmem_shm.so +LoadModule socache_dbm_module modules/mod_socache_dbm.so +LoadModule socache_memcache_module modules/mod_socache_memcache.so +LoadModule socache_shmcb_module modules/mod_socache_shmcb.so +LoadModule status_module modules/mod_status.so +LoadModule substitute_module modules/mod_substitute.so +LoadModule suexec_module modules/mod_suexec.so +LoadModule unique_id_module modules/mod_unique_id.so +LoadModule unixd_module modules/mod_unixd.so +LoadModule userdir_module modules/mod_userdir.so +LoadModule version_module modules/mod_version.so +LoadModule vhost_alias_module modules/mod_vhost_alias.so + +#LoadModule buffer_module modules/mod_buffer.so +#LoadModule watchdog_module modules/mod_watchdog.so +#LoadModule heartbeat_module modules/mod_heartbeat.so +#LoadModule heartmonitor_module modules/mod_heartmonitor.so +#LoadModule usertrack_module modules/mod_usertrack.so +#LoadModule dialup_module modules/mod_dialup.so +#LoadModule charset_lite_module modules/mod_charset_lite.so +#LoadModule log_debug_module modules/mod_log_debug.so +#LoadModule ratelimit_module modules/mod_ratelimit.so +#LoadModule reflector_module modules/mod_reflector.so +#LoadModule request_module modules/mod_request.so +#LoadModule sed_module modules/mod_sed.so +#LoadModule speling_module modules/mod_speling.so + diff --git a/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.modules.d/00-dav.conf b/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.modules.d/00-dav.conf new file mode 100644 index 000000000..e6af8decd --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.modules.d/00-dav.conf @@ -0,0 +1,3 @@ +LoadModule dav_module modules/mod_dav.so +LoadModule dav_fs_module modules/mod_dav_fs.so +LoadModule dav_lock_module modules/mod_dav_lock.so diff --git a/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.modules.d/00-lua.conf b/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.modules.d/00-lua.conf new file mode 100644 index 000000000..9e0d0db6e --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.modules.d/00-lua.conf @@ -0,0 +1 @@ +LoadModule lua_module modules/mod_lua.so diff --git a/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.modules.d/00-mpm.conf b/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.modules.d/00-mpm.conf new file mode 100644 index 000000000..7bfd1d413 --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.modules.d/00-mpm.conf @@ -0,0 +1,19 @@ +# Select the MPM module which should be used by uncommenting exactly +# one of the following LoadModule lines: + +# prefork MPM: Implements a non-threaded, pre-forking web server +# See: http://httpd.apache.org/docs/2.4/mod/prefork.html +LoadModule mpm_prefork_module modules/mod_mpm_prefork.so + +# worker MPM: Multi-Processing Module implementing a hybrid +# multi-threaded multi-process web server +# See: http://httpd.apache.org/docs/2.4/mod/worker.html +# +#LoadModule mpm_worker_module modules/mod_mpm_worker.so + +# event MPM: A variant of the worker MPM with the goal of consuming +# threads only for connections with active processing +# See: http://httpd.apache.org/docs/2.4/mod/event.html +# +#LoadModule mpm_event_module modules/mod_mpm_event.so + diff --git a/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.modules.d/00-proxy.conf b/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.modules.d/00-proxy.conf new file mode 100644 index 000000000..cc0bca077 --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.modules.d/00-proxy.conf @@ -0,0 +1,16 @@ +# This file configures all the proxy modules: +LoadModule proxy_module modules/mod_proxy.so +LoadModule lbmethod_bybusyness_module modules/mod_lbmethod_bybusyness.so +LoadModule lbmethod_byrequests_module modules/mod_lbmethod_byrequests.so +LoadModule lbmethod_bytraffic_module modules/mod_lbmethod_bytraffic.so +LoadModule lbmethod_heartbeat_module modules/mod_lbmethod_heartbeat.so +LoadModule proxy_ajp_module modules/mod_proxy_ajp.so +LoadModule proxy_balancer_module modules/mod_proxy_balancer.so +LoadModule proxy_connect_module modules/mod_proxy_connect.so +LoadModule proxy_express_module modules/mod_proxy_express.so +LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so +LoadModule proxy_fdpass_module modules/mod_proxy_fdpass.so +LoadModule proxy_ftp_module modules/mod_proxy_ftp.so +LoadModule proxy_http_module modules/mod_proxy_http.so +LoadModule proxy_scgi_module modules/mod_proxy_scgi.so +LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so diff --git a/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.modules.d/00-ssl.conf b/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.modules.d/00-ssl.conf new file mode 100644 index 000000000..53235cd76 --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.modules.d/00-ssl.conf @@ -0,0 +1 @@ +LoadModule ssl_module modules/mod_ssl.so diff --git a/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.modules.d/00-systemd.conf b/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.modules.d/00-systemd.conf new file mode 100644 index 000000000..b208c972d --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.modules.d/00-systemd.conf @@ -0,0 +1,2 @@ +# This file configures systemd module: +LoadModule systemd_module modules/mod_systemd.so diff --git a/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.modules.d/01-cgi.conf b/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.modules.d/01-cgi.conf new file mode 100644 index 000000000..5b8b9362e --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf.modules.d/01-cgi.conf @@ -0,0 +1,14 @@ +# This configuration file loads a CGI module appropriate to the MPM +# which has been configured in 00-mpm.conf. mod_cgid should be used +# with a threaded MPM; mod_cgi with the prefork MPM. + + + LoadModule cgid_module modules/mod_cgid.so + + + LoadModule cgid_module modules/mod_cgid.so + + + LoadModule cgi_module modules/mod_cgi.so + + diff --git a/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf/httpd.conf b/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf/httpd.conf new file mode 100644 index 000000000..a7af0dc1e --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf/httpd.conf @@ -0,0 +1,353 @@ +# +# This is the main Apache HTTP server configuration file. It contains the +# configuration directives that give the server its instructions. +# See for detailed information. +# In particular, see +# +# for a discussion of each configuration directive. +# +# Do NOT simply read the instructions in here without understanding +# what they do. They're here only as hints or reminders. If you are unsure +# consult the online docs. You have been warned. +# +# Configuration and logfile names: If the filenames you specify for many +# of the server's control files begin with "/" (or "drive:/" for Win32), the +# server will use that explicit path. If the filenames do *not* begin +# with "/", the value of ServerRoot is prepended -- so 'log/access_log' +# with ServerRoot set to '/www' will be interpreted by the +# server as '/www/log/access_log', where as '/log/access_log' will be +# interpreted as '/log/access_log'. + +# +# ServerRoot: The top of the directory tree under which the server's +# configuration, error, and log files are kept. +# +# Do not add a slash at the end of the directory path. If you point +# ServerRoot at a non-local disk, be sure to specify a local disk on the +# Mutex directive, if file-based mutexes are used. If you wish to share the +# same ServerRoot for multiple httpd daemons, you will need to change at +# least PidFile. +# +ServerRoot "/etc/httpd" + +# +# Listen: Allows you to bind Apache to specific IP addresses and/or +# ports, instead of the default. See also the +# directive. +# +# Change this to Listen on specific IP addresses as shown below to +# prevent Apache from glomming onto all bound IP addresses. +# +#Listen 12.34.56.78:80 +Listen 80 + +# +# Dynamic Shared Object (DSO) Support +# +# To be able to use the functionality of a module which was built as a DSO you +# have to place corresponding `LoadModule' lines at this location so the +# directives contained in it are actually available _before_ they are used. +# Statically compiled modules (those listed by `httpd -l') do not need +# to be loaded here. +# +# Example: +# LoadModule foo_module modules/mod_foo.so +# +Include conf.modules.d/*.conf + +# +# If you wish httpd to run as a different user or group, you must run +# httpd as root initially and it will switch. +# +# User/Group: The name (or #number) of the user/group to run httpd as. +# It is usually good practice to create a dedicated user and group for +# running httpd, as with most system services. +# +User apache +Group apache + +# 'Main' server configuration +# +# The directives in this section set up the values used by the 'main' +# server, which responds to any requests that aren't handled by a +# definition. These values also provide defaults for +# any containers you may define later in the file. +# +# All of these directives may appear inside containers, +# in which case these default settings will be overridden for the +# virtual host being defined. +# + +# +# ServerAdmin: Your address, where problems with the server should be +# e-mailed. This address appears on some server-generated pages, such +# as error documents. e.g. admin@your-domain.com +# +ServerAdmin root@localhost + +# +# ServerName gives the name and port that the server uses to identify itself. +# This can often be determined automatically, but we recommend you specify +# it explicitly to prevent problems during startup. +# +# If your host doesn't have a registered DNS name, enter its IP address here. +# +#ServerName www.example.com:80 + +# +# Deny access to the entirety of your server's filesystem. You must +# explicitly permit access to web content directories in other +# blocks below. +# + + AllowOverride none + Require all denied + + +# +# Note that from this point forward you must specifically allow +# particular features to be enabled - so if something's not working as +# you might expect, make sure that you have specifically enabled it +# below. +# + +# +# DocumentRoot: The directory out of which you will serve your +# documents. By default, all requests are taken from this directory, but +# symbolic links and aliases may be used to point to other locations. +# +DocumentRoot "/var/www/html" + +# +# Relax access to content within /var/www. +# + + AllowOverride None + # Allow open access: + Require all granted + + +# Further relax access to the default document root: + + # + # Possible values for the Options directive are "None", "All", + # or any combination of: + # Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews + # + # Note that "MultiViews" must be named *explicitly* --- "Options All" + # doesn't give it to you. + # + # The Options directive is both complicated and important. Please see + # http://httpd.apache.org/docs/2.4/mod/core.html#options + # for more information. + # + Options Indexes FollowSymLinks + + # + # AllowOverride controls what directives may be placed in .htaccess files. + # It can be "All", "None", or any combination of the keywords: + # Options FileInfo AuthConfig Limit + # + AllowOverride None + + # + # Controls who can get stuff from this server. + # + Require all granted + + +# +# DirectoryIndex: sets the file that Apache will serve if a directory +# is requested. +# + + DirectoryIndex index.html + + +# +# The following lines prevent .htaccess and .htpasswd files from being +# viewed by Web clients. +# + + Require all denied + + +# +# 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 "logs/error_log" + +# +# LogLevel: Control the number of messages logged to the error_log. +# Possible values include: debug, info, notice, warn, error, crit, +# alert, emerg. +# +LogLevel warn + + + # + # The following directives define some format nicknames for use with + # a CustomLog directive (see below). + # + LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined + LogFormat "%h %l %u %t \"%r\" %>s %b" common + + + # You need to enable mod_logio.c to use %I and %O + LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio + + + # + # The location and format of the access logfile (Common Logfile Format). + # If you do not define any access logfiles within a + # container, they will be logged here. Contrariwise, if you *do* + # define per- access logfiles, transactions will be + # logged therein and *not* in this file. + # + #CustomLog "logs/access_log" common + + # + # If you prefer a logfile with access, agent, and referer information + # (Combined Logfile Format) you can use the following directive. + # + CustomLog "logs/access_log" combined + + + + # + # Redirect: Allows you to tell clients about documents that used to + # exist in your server's namespace, but do not anymore. The client + # will make a new request for the document at its new location. + # Example: + # Redirect permanent /foo http://www.example.com/bar + + # + # Alias: Maps web paths into filesystem paths and is used to + # access content that does not live under the DocumentRoot. + # Example: + # Alias /webpath /full/filesystem/path + # + # If you include a trailing / on /webpath then the server will + # require it to be present in the URL. You will also likely + # need to provide a section to allow access to + # the filesystem path. + + # + # ScriptAlias: This controls which directories contain server scripts. + # ScriptAliases are essentially the same as Aliases, except that + # documents in the target directory are treated as applications and + # run by the server when requested rather than as documents sent to the + # client. The same rules about trailing "/" apply to ScriptAlias + # directives as to Alias. + # + ScriptAlias /cgi-bin/ "/var/www/cgi-bin/" + + + +# +# "/var/www/cgi-bin" should be changed to whatever your ScriptAliased +# CGI directory exists, if you have that configured. +# + + AllowOverride None + Options None + Require all granted + + + + # + # TypesConfig points to the file containing the list of mappings from + # filename extension to MIME-type. + # + TypesConfig /etc/mime.types + + # + # AddType allows you to add to or override the MIME configuration + # file specified in TypesConfig for specific file types. + # + #AddType application/x-gzip .tgz + # + # AddEncoding allows you to have certain browsers uncompress + # information on the fly. Note: Not all browsers support this. + # + #AddEncoding x-compress .Z + #AddEncoding x-gzip .gz .tgz + # + # If the AddEncoding directives above are commented-out, then you + # probably should define those extensions to indicate media types: + # + AddType application/x-compress .Z + AddType application/x-gzip .gz .tgz + + # + # AddHandler allows you to map certain file extensions to "handlers": + # actions unrelated to filetype. These can be either built into the server + # or added with the Action directive (see below) + # + # To use CGI scripts outside of ScriptAliased directories: + # (You will also need to add "ExecCGI" to the "Options" directive.) + # + #AddHandler cgi-script .cgi + + # For type maps (negotiated resources): + #AddHandler type-map var + + # + # Filters allow you to process content before it is sent to the client. + # + # To parse .shtml files for server-side includes (SSI): + # (You will also need to add "Includes" to the "Options" directive.) + # + AddType text/html .shtml + AddOutputFilter INCLUDES .shtml + + +# +# Specify a default charset for all content served; this enables +# interpretation of all content as UTF-8 by default. To use the +# default browser choice (ISO-8859-1), or to allow the META tags +# in HTML content to override this choice, comment out this +# directive: +# +AddDefaultCharset UTF-8 + + + # + # The mod_mime_magic module allows the server to use various hints from the + # contents of the file itself to determine its type. The MIMEMagicFile + # directive tells the module where the hint definitions are located. + # + MIMEMagicFile conf/magic + + +# +# Customizable error responses come in three flavors: +# 1) plain text 2) local redirects 3) external redirects +# +# Some examples: +#ErrorDocument 500 "The server made a boo boo." +#ErrorDocument 404 /missing.html +#ErrorDocument 404 "/cgi-bin/missing_handler.pl" +#ErrorDocument 402 http://www.example.com/subscription_info.html +# + +# +# EnableMMAP and EnableSendfile: On systems that support it, +# memory-mapping or the sendfile syscall may be used to deliver +# files. This usually improves server performance, but must +# be turned off when serving from networked-mounted +# filesystems or if support for these functions is otherwise +# broken on your system. +# Defaults if commented: EnableMMAP On, EnableSendfile Off +# +#EnableMMAP off +EnableSendfile on + +# Supplemental configuration +# +# Load config files in the "/etc/httpd/conf.d" directory, if any. +IncludeOptional conf.d/*.conf diff --git a/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf/magic b/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf/magic new file mode 100644 index 000000000..7c56119e9 --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/httpd/conf/magic @@ -0,0 +1,385 @@ +# Magic data for mod_mime_magic Apache module (originally for file(1) command) +# The module is described in /manual/mod/mod_mime_magic.html +# +# The format is 4-5 columns: +# Column #1: byte number to begin checking from, ">" indicates continuation +# Column #2: type of data to match +# Column #3: contents of data to match +# Column #4: MIME type of result +# Column #5: MIME encoding of result (optional) + +#------------------------------------------------------------------------------ +# Localstuff: file(1) magic for locally observed files +# Add any locally observed files here. + +#------------------------------------------------------------------------------ +# end local stuff +#------------------------------------------------------------------------------ + +#------------------------------------------------------------------------------ +# Java + +0 short 0xcafe +>2 short 0xbabe application/java + +#------------------------------------------------------------------------------ +# audio: file(1) magic for sound formats +# +# from Jan Nicolai Langfeldt , +# + +# Sun/NeXT audio data +0 string .snd +>12 belong 1 audio/basic +>12 belong 2 audio/basic +>12 belong 3 audio/basic +>12 belong 4 audio/basic +>12 belong 5 audio/basic +>12 belong 6 audio/basic +>12 belong 7 audio/basic + +>12 belong 23 audio/x-adpcm + +# DEC systems (e.g. DECstation 5000) use a variant of the Sun/NeXT format +# that uses little-endian encoding and has a different magic number +# (0x0064732E in little-endian encoding). +0 lelong 0x0064732E +>12 lelong 1 audio/x-dec-basic +>12 lelong 2 audio/x-dec-basic +>12 lelong 3 audio/x-dec-basic +>12 lelong 4 audio/x-dec-basic +>12 lelong 5 audio/x-dec-basic +>12 lelong 6 audio/x-dec-basic +>12 lelong 7 audio/x-dec-basic +# compressed (G.721 ADPCM) +>12 lelong 23 audio/x-dec-adpcm + +# Bytes 0-3 of AIFF, AIFF-C, & 8SVX audio files are "FORM" +# AIFF audio data +8 string AIFF audio/x-aiff +# AIFF-C audio data +8 string AIFC audio/x-aiff +# IFF/8SVX audio data +8 string 8SVX audio/x-aiff + +# Creative Labs AUDIO stuff +# Standard MIDI data +0 string MThd audio/unknown +#>9 byte >0 (format %d) +#>11 byte >1 using %d channels +# Creative Music (CMF) data +0 string CTMF audio/unknown +# SoundBlaster instrument data +0 string SBI audio/unknown +# Creative Labs voice data +0 string Creative\ Voice\ File audio/unknown +## is this next line right? it came this way... +#>19 byte 0x1A +#>23 byte >0 - version %d +#>22 byte >0 \b.%d + +# [GRR 950115: is this also Creative Labs? Guessing that first line +# should be string instead of unknown-endian long...] +#0 long 0x4e54524b MultiTrack sound data +#0 string NTRK MultiTrack sound data +#>4 long x - version %ld + +# Microsoft WAVE format (*.wav) +# [GRR 950115: probably all of the shorts and longs should be leshort/lelong] +# Microsoft RIFF +0 string RIFF audio/unknown +# - WAVE format +>8 string WAVE audio/x-wav +# MPEG audio. +0 beshort&0xfff0 0xfff0 audio/mpeg +# C64 SID Music files, from Linus Walleij +0 string PSID audio/prs.sid + +#------------------------------------------------------------------------------ +# c-lang: file(1) magic for C programs or various scripts +# + +# XPM icons (Greg Roelofs, newt@uchicago.edu) +# ideally should go into "images", but entries below would tag XPM as C source +0 string /*\ XPM image/x-xbm 7bit + +# this first will upset you if you're a PL/1 shop... (are there any left?) +# in which case rm it; ascmagic will catch real C programs +# C or REXX program text +0 string /* text/plain +# C++ program text +0 string // text/plain + +#------------------------------------------------------------------------------ +# compress: file(1) magic for pure-compression formats (no archives) +# +# compress, gzip, pack, compact, huf, squeeze, crunch, freeze, yabba, whap, etc. +# +# Formats for various forms of compressed data +# Formats for "compress" proper have been moved into "compress.c", +# because it tries to uncompress it to figure out what's inside. + +# standard unix compress +0 string \037\235 application/octet-stream x-compress + +# gzip (GNU zip, not to be confused with [Info-ZIP/PKWARE] zip archiver) +0 string \037\213 application/octet-stream x-gzip + +# According to gzip.h, this is the correct byte order for packed data. +0 string \037\036 application/octet-stream +# +# This magic number is byte-order-independent. +# +0 short 017437 application/octet-stream + +# XXX - why *two* entries for "compacted data", one of which is +# byte-order independent, and one of which is byte-order dependent? +# +# compacted data +0 short 0x1fff application/octet-stream +0 string \377\037 application/octet-stream +# huf output +0 short 0145405 application/octet-stream + +# Squeeze and Crunch... +# These numbers were gleaned from the Unix versions of the programs to +# handle these formats. Note that I can only uncrunch, not crunch, and +# I didn't have a crunched file handy, so the crunch number is untested. +# Keith Waclena +#0 leshort 0x76FF squeezed data (CP/M, DOS) +#0 leshort 0x76FE crunched data (CP/M, DOS) + +# Freeze +#0 string \037\237 Frozen file 2.1 +#0 string \037\236 Frozen file 1.0 (or gzip 0.5) + +# lzh? +#0 string \037\240 LZH compressed data + +#------------------------------------------------------------------------------ +# frame: file(1) magic for FrameMaker files +# +# This stuff came on a FrameMaker demo tape, most of which is +# copyright, but this file is "published" as witness the following: +# +0 string \ +# and Anna Shergold +# +0 string \ +0 string \14 byte 12 (OS/2 1.x format) +#>14 byte 64 (OS/2 2.x format) +#>14 byte 40 (Windows 3.x format) +#0 string IC icon +#0 string PI pointer +#0 string CI color icon +#0 string CP color pointer +#0 string BA bitmap array + +0 string \x89PNG image/png +0 string FWS application/x-shockwave-flash +0 string CWS application/x-shockwave-flash + +#------------------------------------------------------------------------------ +# lisp: file(1) magic for lisp programs +# +# various lisp types, from Daniel Quinlan (quinlan@yggdrasil.com) +0 string ;; text/plain 8bit +# Emacs 18 - this is always correct, but not very magical. +0 string \012( application/x-elc +# Emacs 19 +0 string ;ELC\023\000\000\000 application/x-elc + +#------------------------------------------------------------------------------ +# mail.news: file(1) magic for mail and news +# +# There are tests to ascmagic.c to cope with mail and news. +0 string Relay-Version: message/rfc822 7bit +0 string #!\ rnews message/rfc822 7bit +0 string N#!\ rnews message/rfc822 7bit +0 string Forward\ to message/rfc822 7bit +0 string Pipe\ to message/rfc822 7bit +0 string Return-Path: message/rfc822 7bit +0 string Path: message/news 8bit +0 string Xref: message/news 8bit +0 string From: message/rfc822 7bit +0 string Article message/news 8bit +#------------------------------------------------------------------------------ +# msword: file(1) magic for MS Word files +# +# Contributor claims: +# Reversed-engineered MS Word magic numbers +# + +0 string \376\067\0\043 application/msword +0 string \333\245-\0\0\0 application/msword + +# disable this one because it applies also to other +# Office/OLE documents for which msword is not correct. See PR#2608. +#0 string \320\317\021\340\241\261 application/msword + + + +#------------------------------------------------------------------------------ +# printer: file(1) magic for printer-formatted files +# + +# PostScript +0 string %! application/postscript +0 string \004%! application/postscript + +# Acrobat +# (due to clamen@cs.cmu.edu) +0 string %PDF- application/pdf + +#------------------------------------------------------------------------------ +# sc: file(1) magic for "sc" spreadsheet +# +38 string Spreadsheet application/x-sc + +#------------------------------------------------------------------------------ +# tex: file(1) magic for TeX files +# +# XXX - needs byte-endian stuff (big-endian and little-endian DVI?) +# +# From + +# Although we may know the offset of certain text fields in TeX DVI +# and font files, we can't use them reliably because they are not +# zero terminated. [but we do anyway, christos] +0 string \367\002 application/x-dvi +#0 string \367\203 TeX generic font data +#0 string \367\131 TeX packed font data +#0 string \367\312 TeX virtual font data +#0 string This\ is\ TeX, TeX transcript text +#0 string This\ is\ METAFONT, METAFONT transcript text + +# There is no way to detect TeX Font Metric (*.tfm) files without +# breaking them apart and reading the data. The following patterns +# match most *.tfm files generated by METAFONT or afm2tfm. +#2 string \000\021 TeX font metric data +#2 string \000\022 TeX font metric data +#>34 string >\0 (%s) + +# Texinfo and GNU Info, from Daniel Quinlan (quinlan@yggdrasil.com) +#0 string \\input\ texinfo Texinfo source text +#0 string This\ is\ Info\ file GNU Info text + +# correct TeX magic for Linux (and maybe more) +# from Peter Tobias (tobias@server.et-inf.fho-emden.de) +# +0 leshort 0x02f7 application/x-dvi + +# RTF - Rich Text Format +0 string {\\rtf application/rtf + +#------------------------------------------------------------------------------ +# animation: file(1) magic for animation/movie formats +# +# animation formats, originally from vax@ccwf.cc.utexas.edu (VaX#n8) +# MPEG file +0 string \000\000\001\263 video/mpeg +# +# The contributor claims: +# I couldn't find a real magic number for these, however, this +# -appears- to work. Note that it might catch other files, too, +# so BE CAREFUL! +# +# Note that title and author appear in the two 20-byte chunks +# at decimal offsets 2 and 22, respectively, but they are XOR'ed with +# 255 (hex FF)! DL format SUCKS BIG ROCKS. +# +# DL file version 1 , medium format (160x100, 4 images/screen) +0 byte 1 video/unknown +0 byte 2 video/unknown +# Quicktime video, from Linus Walleij +# from Apple quicktime file format documentation. +4 string moov video/quicktime +4 string mdat video/quicktime + diff --git a/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/sites b/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/sites new file mode 100644 index 000000000..6af1f63fa --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/sites @@ -0,0 +1 @@ +conf.d/centos.example.com.conf, centos.example.com diff --git a/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/sysconfig/httpd b/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/sysconfig/httpd new file mode 100644 index 000000000..0bf6b176c --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/centos7_apache/apache/sysconfig/httpd @@ -0,0 +1,25 @@ +# +# This file can be used to set additional environment variables for +# the httpd process, or pass additional options to the httpd +# executable. +# +# Note: With previous versions of httpd, the MPM could be changed by +# editing an "HTTPD" variable here. With the current version, that +# variable is now ignored. The MPM is a loadable module, and the +# choice of MPM can be changed by editing the configuration file +# /etc/httpd/conf.modules.d/00-mpm.conf. +# + +# +# To pass additional options (for instance, -D definitions) to the +# httpd binary at startup, set OPTIONS here. +# +OPTIONS="-D mock_define -D mock_define_too -D mock_value=TRUE" + +# +# This setting ensures the httpd process is started in the "C" locale +# by default. (Some modules will not behave correctly if +# case-sensitive string comparisons are performed in a different +# locale.) +# +LANG=C diff --git a/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/httpd.conf b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/httpd.conf new file mode 100644 index 000000000..e5693ffff --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/httpd.conf @@ -0,0 +1,157 @@ +# This is a modification of the default Apache 2.4 configuration file +# for Gentoo Linux. +# +# Support: +# http://www.gentoo.org/main/en/lists.xml [mailing lists] +# http://forums.gentoo.org/ [web forums] +# irc://irc.freenode.net#gentoo-apache [irc chat] +# +# Bug Reports: +# http://bugs.gentoo.org [gentoo related bugs] +# http://httpd.apache.org/bug_report.html [apache httpd related bugs] +# +# +# This is the main Apache HTTP server configuration file. It contains the +# configuration directives that give the server its instructions. +# See for detailed information. +# In particular, see +# +# for a discussion of each configuration directive. +# +# Do NOT simply read the instructions in here without understanding +# what they do. They're here only as hints or reminders. If you are unsure +# consult the online docs. You have been warned. +# +# Configuration and logfile names: If the filenames you specify for many +# of the server's control files begin with "/" (or "drive:/" for Win32), the +# server will use that explicit path. If the filenames do *not* begin +# with "/", the value of ServerRoot is prepended -- so "var/log/apache2/foo_log" +# with ServerRoot set to "/usr" will be interpreted by the +# server as "/usr/var/log/apache2/foo.log". + +# ServerRoot: The top of the directory tree under which the server's +# configuration, error, and log files are kept. +# +# Do not add a slash at the end of the directory path. If you point +# ServerRoot at a non-local disk, be sure to point the LockFile directive +# at a local disk. If you wish to share the same ServerRoot for multiple +# httpd daemons, you will need to change at least LockFile and PidFile. +# Comment: The LockFile directive has been replaced by the Mutex directive +ServerRoot "/usr/lib64/apache2" + +# Dynamic Shared Object (DSO) Support +# +# To be able to use the functionality of a module which was built as a DSO you +# have to place corresponding `LoadModule' lines at this location so the +# directives contained in it are actually available _before_ they are used. +# Statically compiled modules (those listed by `httpd -l') do not need +# to be loaded here. +# +# Example: +# LoadModule foo_module modules/mod_foo.so +# +# GENTOO: Automatically defined based on APACHE2_MODULES USE_EXPAND variable. +# Do not change manually, it will be overwritten on upgrade. +# +# The following modules are considered as the default configuration. +# If you wish to disable one of them, you may have to alter other +# configuration directives. +# +# Change these at your own risk! + +LoadModule actions_module modules/mod_actions.so +LoadModule alias_module modules/mod_alias.so +LoadModule auth_basic_module modules/mod_auth_basic.so +LoadModule authn_anon_module modules/mod_authn_anon.so +LoadModule authn_core_module modules/mod_authn_core.so +LoadModule authn_dbm_module modules/mod_authn_dbm.so +LoadModule authn_file_module modules/mod_authn_file.so +LoadModule authz_core_module modules/mod_authz_core.so +LoadModule authz_dbm_module modules/mod_authz_dbm.so +LoadModule authz_groupfile_module modules/mod_authz_groupfile.so +LoadModule authz_host_module modules/mod_authz_host.so +LoadModule authz_owner_module modules/mod_authz_owner.so +LoadModule authz_user_module modules/mod_authz_user.so +LoadModule autoindex_module modules/mod_autoindex.so + +LoadModule cache_module modules/mod_cache.so + +LoadModule cgi_module modules/mod_cgi.so +LoadModule cgid_module modules/mod_cgid.so + +LoadModule dav_module modules/mod_dav.so + + +LoadModule dav_fs_module modules/mod_dav_fs.so + + +LoadModule dav_lock_module modules/mod_dav_lock.so + +LoadModule deflate_module modules/mod_deflate.so +LoadModule dir_module modules/mod_dir.so +LoadModule env_module modules/mod_env.so +LoadModule expires_module modules/mod_expires.so +LoadModule ext_filter_module modules/mod_ext_filter.so + +LoadModule file_cache_module modules/mod_file_cache.so + +LoadModule filter_module modules/mod_filter.so +LoadModule headers_module modules/mod_headers.so +LoadModule include_module modules/mod_include.so + +LoadModule info_module modules/mod_info.so + +LoadModule log_config_module modules/mod_log_config.so +LoadModule logio_module modules/mod_logio.so +LoadModule mime_module modules/mod_mime.so +LoadModule mime_magic_module modules/mod_mime_magic.so +LoadModule negotiation_module modules/mod_negotiation.so +LoadModule rewrite_module modules/mod_rewrite.so +LoadModule setenvif_module modules/mod_setenvif.so + +LoadModule socache_shmcb_module modules/mod_socache_shmcb.so + +LoadModule speling_module modules/mod_speling.so + +LoadModule ssl_module modules/mod_ssl.so + + +LoadModule status_module modules/mod_status.so + +LoadModule unique_id_module modules/mod_unique_id.so +LoadModule unixd_module modules/mod_unixd.so + +LoadModule userdir_module modules/mod_userdir.so + +LoadModule usertrack_module modules/mod_usertrack.so +LoadModule vhost_alias_module modules/mod_vhost_alias.so + +# If you wish httpd to run as a different user or group, you must run +# httpd as root initially and it will switch. +# +# User/Group: The name (or #number) of the user/group to run httpd as. +# It is usually good practice to create a dedicated user and group for +# running httpd, as with most system services. +User apache +Group apache + +# Supplemental configuration +# +# Most of the configuration files in the /etc/apache2/modules.d/ directory can +# be turned on using APACHE2_OPTS in /etc/conf.d/apache2 to add extra features +# or to modify the default configuration of the server. +# +# To know which flag to add to APACHE2_OPTS, look at the first line of the +# the file, which will usually be an where OPTION is the +# flag to use. + +Include modules.d/*.conf + +# Virtual-host support +# +# Gentoo has made using virtual-hosts easy. In /etc/apache2/vhosts.d/ we +# include a default vhost (enabled by adding -D DEFAULT_VHOST to +# APACHE2_OPTS in /etc/conf.d/apache2). +Include vhosts.d/*.conf + +# vim: ts=4 filetype=apache diff --git a/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/magic b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/magic new file mode 100644 index 000000000..7c56119e9 --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/magic @@ -0,0 +1,385 @@ +# Magic data for mod_mime_magic Apache module (originally for file(1) command) +# The module is described in /manual/mod/mod_mime_magic.html +# +# The format is 4-5 columns: +# Column #1: byte number to begin checking from, ">" indicates continuation +# Column #2: type of data to match +# Column #3: contents of data to match +# Column #4: MIME type of result +# Column #5: MIME encoding of result (optional) + +#------------------------------------------------------------------------------ +# Localstuff: file(1) magic for locally observed files +# Add any locally observed files here. + +#------------------------------------------------------------------------------ +# end local stuff +#------------------------------------------------------------------------------ + +#------------------------------------------------------------------------------ +# Java + +0 short 0xcafe +>2 short 0xbabe application/java + +#------------------------------------------------------------------------------ +# audio: file(1) magic for sound formats +# +# from Jan Nicolai Langfeldt , +# + +# Sun/NeXT audio data +0 string .snd +>12 belong 1 audio/basic +>12 belong 2 audio/basic +>12 belong 3 audio/basic +>12 belong 4 audio/basic +>12 belong 5 audio/basic +>12 belong 6 audio/basic +>12 belong 7 audio/basic + +>12 belong 23 audio/x-adpcm + +# DEC systems (e.g. DECstation 5000) use a variant of the Sun/NeXT format +# that uses little-endian encoding and has a different magic number +# (0x0064732E in little-endian encoding). +0 lelong 0x0064732E +>12 lelong 1 audio/x-dec-basic +>12 lelong 2 audio/x-dec-basic +>12 lelong 3 audio/x-dec-basic +>12 lelong 4 audio/x-dec-basic +>12 lelong 5 audio/x-dec-basic +>12 lelong 6 audio/x-dec-basic +>12 lelong 7 audio/x-dec-basic +# compressed (G.721 ADPCM) +>12 lelong 23 audio/x-dec-adpcm + +# Bytes 0-3 of AIFF, AIFF-C, & 8SVX audio files are "FORM" +# AIFF audio data +8 string AIFF audio/x-aiff +# AIFF-C audio data +8 string AIFC audio/x-aiff +# IFF/8SVX audio data +8 string 8SVX audio/x-aiff + +# Creative Labs AUDIO stuff +# Standard MIDI data +0 string MThd audio/unknown +#>9 byte >0 (format %d) +#>11 byte >1 using %d channels +# Creative Music (CMF) data +0 string CTMF audio/unknown +# SoundBlaster instrument data +0 string SBI audio/unknown +# Creative Labs voice data +0 string Creative\ Voice\ File audio/unknown +## is this next line right? it came this way... +#>19 byte 0x1A +#>23 byte >0 - version %d +#>22 byte >0 \b.%d + +# [GRR 950115: is this also Creative Labs? Guessing that first line +# should be string instead of unknown-endian long...] +#0 long 0x4e54524b MultiTrack sound data +#0 string NTRK MultiTrack sound data +#>4 long x - version %ld + +# Microsoft WAVE format (*.wav) +# [GRR 950115: probably all of the shorts and longs should be leshort/lelong] +# Microsoft RIFF +0 string RIFF audio/unknown +# - WAVE format +>8 string WAVE audio/x-wav +# MPEG audio. +0 beshort&0xfff0 0xfff0 audio/mpeg +# C64 SID Music files, from Linus Walleij +0 string PSID audio/prs.sid + +#------------------------------------------------------------------------------ +# c-lang: file(1) magic for C programs or various scripts +# + +# XPM icons (Greg Roelofs, newt@uchicago.edu) +# ideally should go into "images", but entries below would tag XPM as C source +0 string /*\ XPM image/x-xbm 7bit + +# this first will upset you if you're a PL/1 shop... (are there any left?) +# in which case rm it; ascmagic will catch real C programs +# C or REXX program text +0 string /* text/plain +# C++ program text +0 string // text/plain + +#------------------------------------------------------------------------------ +# compress: file(1) magic for pure-compression formats (no archives) +# +# compress, gzip, pack, compact, huf, squeeze, crunch, freeze, yabba, whap, etc. +# +# Formats for various forms of compressed data +# Formats for "compress" proper have been moved into "compress.c", +# because it tries to uncompress it to figure out what's inside. + +# standard unix compress +0 string \037\235 application/octet-stream x-compress + +# gzip (GNU zip, not to be confused with [Info-ZIP/PKWARE] zip archiver) +0 string \037\213 application/octet-stream x-gzip + +# According to gzip.h, this is the correct byte order for packed data. +0 string \037\036 application/octet-stream +# +# This magic number is byte-order-independent. +# +0 short 017437 application/octet-stream + +# XXX - why *two* entries for "compacted data", one of which is +# byte-order independent, and one of which is byte-order dependent? +# +# compacted data +0 short 0x1fff application/octet-stream +0 string \377\037 application/octet-stream +# huf output +0 short 0145405 application/octet-stream + +# Squeeze and Crunch... +# These numbers were gleaned from the Unix versions of the programs to +# handle these formats. Note that I can only uncrunch, not crunch, and +# I didn't have a crunched file handy, so the crunch number is untested. +# Keith Waclena +#0 leshort 0x76FF squeezed data (CP/M, DOS) +#0 leshort 0x76FE crunched data (CP/M, DOS) + +# Freeze +#0 string \037\237 Frozen file 2.1 +#0 string \037\236 Frozen file 1.0 (or gzip 0.5) + +# lzh? +#0 string \037\240 LZH compressed data + +#------------------------------------------------------------------------------ +# frame: file(1) magic for FrameMaker files +# +# This stuff came on a FrameMaker demo tape, most of which is +# copyright, but this file is "published" as witness the following: +# +0 string \ +# and Anna Shergold +# +0 string \ +0 string \14 byte 12 (OS/2 1.x format) +#>14 byte 64 (OS/2 2.x format) +#>14 byte 40 (Windows 3.x format) +#0 string IC icon +#0 string PI pointer +#0 string CI color icon +#0 string CP color pointer +#0 string BA bitmap array + +0 string \x89PNG image/png +0 string FWS application/x-shockwave-flash +0 string CWS application/x-shockwave-flash + +#------------------------------------------------------------------------------ +# lisp: file(1) magic for lisp programs +# +# various lisp types, from Daniel Quinlan (quinlan@yggdrasil.com) +0 string ;; text/plain 8bit +# Emacs 18 - this is always correct, but not very magical. +0 string \012( application/x-elc +# Emacs 19 +0 string ;ELC\023\000\000\000 application/x-elc + +#------------------------------------------------------------------------------ +# mail.news: file(1) magic for mail and news +# +# There are tests to ascmagic.c to cope with mail and news. +0 string Relay-Version: message/rfc822 7bit +0 string #!\ rnews message/rfc822 7bit +0 string N#!\ rnews message/rfc822 7bit +0 string Forward\ to message/rfc822 7bit +0 string Pipe\ to message/rfc822 7bit +0 string Return-Path: message/rfc822 7bit +0 string Path: message/news 8bit +0 string Xref: message/news 8bit +0 string From: message/rfc822 7bit +0 string Article message/news 8bit +#------------------------------------------------------------------------------ +# msword: file(1) magic for MS Word files +# +# Contributor claims: +# Reversed-engineered MS Word magic numbers +# + +0 string \376\067\0\043 application/msword +0 string \333\245-\0\0\0 application/msword + +# disable this one because it applies also to other +# Office/OLE documents for which msword is not correct. See PR#2608. +#0 string \320\317\021\340\241\261 application/msword + + + +#------------------------------------------------------------------------------ +# printer: file(1) magic for printer-formatted files +# + +# PostScript +0 string %! application/postscript +0 string \004%! application/postscript + +# Acrobat +# (due to clamen@cs.cmu.edu) +0 string %PDF- application/pdf + +#------------------------------------------------------------------------------ +# sc: file(1) magic for "sc" spreadsheet +# +38 string Spreadsheet application/x-sc + +#------------------------------------------------------------------------------ +# tex: file(1) magic for TeX files +# +# XXX - needs byte-endian stuff (big-endian and little-endian DVI?) +# +# From + +# Although we may know the offset of certain text fields in TeX DVI +# and font files, we can't use them reliably because they are not +# zero terminated. [but we do anyway, christos] +0 string \367\002 application/x-dvi +#0 string \367\203 TeX generic font data +#0 string \367\131 TeX packed font data +#0 string \367\312 TeX virtual font data +#0 string This\ is\ TeX, TeX transcript text +#0 string This\ is\ METAFONT, METAFONT transcript text + +# There is no way to detect TeX Font Metric (*.tfm) files without +# breaking them apart and reading the data. The following patterns +# match most *.tfm files generated by METAFONT or afm2tfm. +#2 string \000\021 TeX font metric data +#2 string \000\022 TeX font metric data +#>34 string >\0 (%s) + +# Texinfo and GNU Info, from Daniel Quinlan (quinlan@yggdrasil.com) +#0 string \\input\ texinfo Texinfo source text +#0 string This\ is\ Info\ file GNU Info text + +# correct TeX magic for Linux (and maybe more) +# from Peter Tobias (tobias@server.et-inf.fho-emden.de) +# +0 leshort 0x02f7 application/x-dvi + +# RTF - Rich Text Format +0 string {\\rtf application/rtf + +#------------------------------------------------------------------------------ +# animation: file(1) magic for animation/movie formats +# +# animation formats, originally from vax@ccwf.cc.utexas.edu (VaX#n8) +# MPEG file +0 string \000\000\001\263 video/mpeg +# +# The contributor claims: +# I couldn't find a real magic number for these, however, this +# -appears- to work. Note that it might catch other files, too, +# so BE CAREFUL! +# +# Note that title and author appear in the two 20-byte chunks +# at decimal offsets 2 and 22, respectively, but they are XOR'ed with +# 255 (hex FF)! DL format SUCKS BIG ROCKS. +# +# DL file version 1 , medium format (160x100, 4 images/screen) +0 byte 1 video/unknown +0 byte 2 video/unknown +# Quicktime video, from Linus Walleij +# from Apple quicktime file format documentation. +4 string moov video/quicktime +4 string mdat video/quicktime + diff --git a/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/.keep_www-servers_apache-2 b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/.keep_www-servers_apache-2 new file mode 100644 index 000000000..e69de29bb diff --git a/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/00_default_settings.conf b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/00_default_settings.conf new file mode 100644 index 000000000..38635aa9d --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/00_default_settings.conf @@ -0,0 +1,131 @@ +# This configuration file reflects default settings for Apache HTTP Server. +# You may change these, but chances are that you may not need to. + +# 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 15 + +# UseCanonicalName: Determines how Apache constructs self-referencing +# URLs and the SERVER_NAME and SERVER_PORT variables. +# When set "Off", Apache will use the Hostname and Port supplied +# by the client. When set "On", Apache will use the value of the +# ServerName directive. +UseCanonicalName Off + +# AccessFileName: The name of the file to look for in each directory +# for additional configuration directives. See also the AllowOverride +# directive. +AccessFileName .htaccess + +# ServerTokens +# This directive configures what you return as the Server HTTP response +# Header. The default is 'Full' which sends information about the OS-Type +# and compiled in modules. +# Set to one of: Full | OS | Minor | Minimal | Major | Prod +# where Full conveys the most information, and Prod the least. +ServerTokens Prod + +# TraceEnable +# This directive overrides the behavior of TRACE for both the core server and +# mod_proxy. The default TraceEnable on permits TRACE requests per RFC 2616, +# which disallows any request body to accompany the request. TraceEnable off +# causes the core server and mod_proxy to return a 405 (Method not allowed) +# error to the client. +# For security reasons this is turned off by default. (bug #240680) +TraceEnable off + +# Optionally add a line containing the server version and virtual host +# name to server-generated pages (internal error documents, FTP directory +# listings, mod_status and mod_info output etc., but not CGI generated +# documents or custom error documents). +# Set to "EMail" to also include a mailto: link to the ServerAdmin. +# Set to one of: On | Off | EMail +ServerSignature On + +# 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 + +# EnableMMAP and EnableSendfile: On systems that support it, +# memory-mapping or the sendfile syscall is used to deliver +# files. This usually improves server performance, but must +# be turned off when serving from networked-mounted +# filesystems or if support for these functions is otherwise +# broken on your system. +EnableMMAP On +EnableSendfile Off + +# FileETag: Configures the file attributes that are used to create +# the ETag (entity tag) response header field when the document is +# based on a static file. (The ETag value is used in cache management +# to save network bandwidth.) +FileETag MTime Size + +# ContentDigest: This directive enables the generation of Content-MD5 +# headers as defined in RFC1864 respectively RFC2616. +# The Content-MD5 header provides an end-to-end message integrity +# check (MIC) of the entity-body. A proxy or client may check this +# header for detecting accidental modification of the entity-body +# in transit. +# Note that this can cause performance problems on your server since +# the message digest is computed on every request (the values are +# not cached). +# Content-MD5 is only sent for documents served by the core, and not +# by any module. For example, SSI documents, output from CGI scripts, +# and byte range responses do not have this header. +ContentDigest 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 /var/log/apache2/error_log + +# LogLevel: Control the number of messages logged to the error_log. +# Possible values include: debug, info, notice, warn, error, crit, +# alert, emerg. +LogLevel warn + +# We configure the "default" to be a very restrictive set of features. + + Options FollowSymLinks + AllowOverride None + Require all denied + + +# DirectoryIndex: sets the file that Apache will serve if a directory +# is requested. +# +# The index.html.var file (a type-map) is used to deliver content- +# negotiated documents. The MultiViews Options can be used for the +# same purpose, but it is much slower. +# +# Do not change this entry unless you know what you are doing. + + DirectoryIndex index.html index.html.var + + +# The following lines prevent .htaccess and .htpasswd files from being +# viewed by Web clients. + + Require all denied + + +# vim: ts=4 filetype=apache diff --git a/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/00_error_documents.conf b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/00_error_documents.conf new file mode 100644 index 000000000..61479fa53 --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/00_error_documents.conf @@ -0,0 +1,57 @@ +# The configuration below implements multi-language error documents through +# content-negotiation. + +# Customizable error responses come in three flavors: +# 1) plain text 2) local redirects 3) external redirects +# Some examples: +#ErrorDocument 500 "The server made a boo boo." +#ErrorDocument 404 /missing.html +#ErrorDocument 404 "/cgi-bin/missing_handler.pl" +#ErrorDocument 402 http://www.example.com/subscription_info.html + +# Required modules: mod_alias, mod_include, mod_negotiation +# We use Alias to redirect any /error/HTTP_.html.var response to +# our collection of by-error message multi-language collections. We use +# includes to substitute the appropriate text. +# You can modify the messages' appearance without changing any of the +# default HTTP_.html.var files by adding the line: +# Alias /error/include/ "/your/include/path/" +# which allows you to create your own set of files by starting with the +# /var/www/localhost/error/include/ files and copying them to /your/include/path/, +# even on a per-VirtualHost basis. The default include files will display +# your Apache version number and your ServerAdmin email address regardless +# of the setting of ServerSignature. + + +Alias /error/ "/usr/share/apache2/error/" + + + AllowOverride None + Options IncludesNoExec + AddOutputFilter Includes html + AddHandler type-map var + Require all granted + LanguagePriority en cs de es fr it ja ko nl pl pt-br ro sv tr + ForceLanguagePriority Prefer Fallback + + +ErrorDocument 400 /error/HTTP_BAD_REQUEST.html.var +ErrorDocument 401 /error/HTTP_UNAUTHORIZED.html.var +ErrorDocument 403 /error/HTTP_FORBIDDEN.html.var +ErrorDocument 404 /error/HTTP_NOT_FOUND.html.var +ErrorDocument 405 /error/HTTP_METHOD_NOT_ALLOWED.html.var +ErrorDocument 408 /error/HTTP_REQUEST_TIME_OUT.html.var +ErrorDocument 410 /error/HTTP_GONE.html.var +ErrorDocument 411 /error/HTTP_LENGTH_REQUIRED.html.var +ErrorDocument 412 /error/HTTP_PRECONDITION_FAILED.html.var +ErrorDocument 413 /error/HTTP_REQUEST_ENTITY_TOO_LARGE.html.var +ErrorDocument 414 /error/HTTP_REQUEST_URI_TOO_LARGE.html.var +ErrorDocument 415 /error/HTTP_UNSUPPORTED_MEDIA_TYPE.html.var +ErrorDocument 500 /error/HTTP_INTERNAL_SERVER_ERROR.html.var +ErrorDocument 501 /error/HTTP_NOT_IMPLEMENTED.html.var +ErrorDocument 502 /error/HTTP_BAD_GATEWAY.html.var +ErrorDocument 503 /error/HTTP_SERVICE_UNAVAILABLE.html.var +ErrorDocument 506 /error/HTTP_VARIANT_ALSO_VARIES.html.var + + +# vim: ts=4 filetype=apache diff --git a/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/00_languages.conf b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/00_languages.conf new file mode 100644 index 000000000..c429bf94c --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/00_languages.conf @@ -0,0 +1,133 @@ +# Settings for hosting different languages. + +# DefaultLanguage and AddLanguage allows you to specify the language of +# a document. You can then use content negotiation to give a browser a +# file in a language the user can understand. +# +# Specify a default language. This means that all data +# going out without a specific language tag (see below) will +# be marked with this one. You probably do NOT want to set +# this unless you are sure it is correct for all cases. +# +# It is generally better to not mark a page as +# being a certain language than marking it with the wrong +# language! +# +# DefaultLanguage nl +# +# Note 1: The suffix does not have to be the same as the language +# keyword --- those with documents in Polish (whose net-standard +# language code is pl) may wish to use "AddLanguage pl .po" to +# avoid the ambiguity with the common suffix for perl scripts. +# +# Note 2: The example entries below illustrate that in some cases +# the two character 'Language' abbreviation is not identical to +# the two character 'Country' code for its country, +# E.g. 'Danmark/dk' versus 'Danish/da'. +# +# Note 3: In the case of 'ltz' we violate the RFC by using a three char +# specifier. There is 'work in progress' to fix this and get +# the reference data for rfc1766 cleaned up. +# +# Catalan (ca) - Croatian (hr) - Czech (cs) - Danish (da) - Dutch (nl) +# English (en) - Esperanto (eo) - Estonian (et) - French (fr) - German (de) +# Greek-Modern (el) - Hebrew (he) - Italian (it) - Japanese (ja) +# Korean (ko) - Luxembourgeois* (ltz) - Norwegian Nynorsk (nn) +# Norwegian (no) - Polish (pl) - Portugese (pt) +# Brazilian Portuguese (pt-BR) - Russian (ru) - Swedish (sv) +# Simplified Chinese (zh-CN) - Spanish (es) - Traditional Chinese (zh-TW) +AddLanguage ca .ca +AddLanguage cs .cz .cs +AddLanguage da .dk +AddLanguage de .de +AddLanguage el .el +AddLanguage en .en +AddLanguage eo .eo +AddLanguage es .es +AddLanguage et .et +AddLanguage fr .fr +AddLanguage he .he +AddLanguage hr .hr +AddLanguage it .it +AddLanguage ja .ja +AddLanguage ko .ko +AddLanguage ltz .ltz +AddLanguage nl .nl +AddLanguage nn .nn +AddLanguage no .no +AddLanguage pl .po +AddLanguage pt .pt +AddLanguage pt-BR .pt-br +AddLanguage ru .ru +AddLanguage sv .sv +AddLanguage zh-CN .zh-cn +AddLanguage zh-TW .zh-tw + +# LanguagePriority allows you to give precedence to some languages +# in case of a tie during content negotiation. +# +# Just list the languages in decreasing order of preference. We have +# more or less alphabetized them here. You probably want to change this. +LanguagePriority en ca cs da de el eo es et fr he hr it ja ko ltz nl nn no pl pt pt-BR ru sv zh-CN zh-TW + +# ForceLanguagePriority allows you to serve a result page rather than +# MULTIPLE CHOICES (Prefer) [in case of a tie] or NOT ACCEPTABLE (Fallback) +# [in case no accepted languages matched the available variants] +ForceLanguagePriority Prefer Fallback + +# Commonly used filename extensions to character sets. You probably +# want to avoid clashes with the language extensions, unless you +# are good at carefully testing your setup after each change. +# See http://www.iana.org/assignments/character-sets for the +# official list of charset names and their respective RFCs. +AddCharset us-ascii.ascii .us-ascii +AddCharset ISO-8859-1 .iso8859-1 .latin1 +AddCharset ISO-8859-2 .iso8859-2 .latin2 .cen +AddCharset ISO-8859-3 .iso8859-3 .latin3 +AddCharset ISO-8859-4 .iso8859-4 .latin4 +AddCharset ISO-8859-5 .iso8859-5 .cyr .iso-ru +AddCharset ISO-8859-6 .iso8859-6 .arb .arabic +AddCharset ISO-8859-7 .iso8859-7 .grk .greek +AddCharset ISO-8859-8 .iso8859-8 .heb .hebrew +AddCharset ISO-8859-9 .iso8859-9 .latin5 .trk +AddCharset ISO-8859-10 .iso8859-10 .latin6 +AddCharset ISO-8859-13 .iso8859-13 +AddCharset ISO-8859-14 .iso8859-14 .latin8 +AddCharset ISO-8859-15 .iso8859-15 .latin9 +AddCharset ISO-8859-16 .iso8859-16 .latin10 +AddCharset ISO-2022-JP .iso2022-jp .jis +AddCharset ISO-2022-KR .iso2022-kr .kis +AddCharset ISO-2022-CN .iso2022-cn .cis +AddCharset Big5.Big5 .big5 .b5 +AddCharset cn-Big5 .cn-big5 +# For russian, more than one charset is used (depends on client, mostly): +AddCharset WINDOWS-1251 .cp-1251 .win-1251 +AddCharset CP866 .cp866 +AddCharset KOI8 .koi8 +AddCharset KOI8-E .koi8-e +AddCharset KOI8-r .koi8-r .koi8-ru +AddCharset KOI8-U .koi8-u +AddCharset KOI8-ru .koi8-uk .ua +AddCharset ISO-10646-UCS-2 .ucs2 +AddCharset ISO-10646-UCS-4 .ucs4 +AddCharset UTF-7 .utf7 +AddCharset UTF-8 .utf8 +AddCharset UTF-16 .utf16 +AddCharset UTF-16BE .utf16be +AddCharset UTF-16LE .utf16le +AddCharset UTF-32 .utf32 +AddCharset UTF-32BE .utf32be +AddCharset UTF-32LE .utf32le +AddCharset euc-cn .euc-cn +AddCharset euc-gb .euc-gb +AddCharset euc-jp .euc-jp +AddCharset euc-kr .euc-kr +# Not sure how euc-tw got in - IANA doesn't list it??? +AddCharset EUC-TW .euc-tw +AddCharset gb2312 .gb2312 .gb +AddCharset iso-10646-ucs-2 .ucs-2 .iso-10646-ucs-2 +AddCharset iso-10646-ucs-4 .ucs-4 .iso-10646-ucs-4 +AddCharset shift_jis .shift_jis .sjis + + +# vim: ts=4 filetype=apache diff --git a/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/00_mod_autoindex.conf b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/00_mod_autoindex.conf new file mode 100644 index 000000000..10bf48317 --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/00_mod_autoindex.conf @@ -0,0 +1,85 @@ + + + + +# We include the /icons/ alias for FancyIndexed directory listings. If +# you do not use FancyIndexing, you may comment this out. +Alias /icons/ "/usr/share/apache2/icons/" + + + Options Indexes MultiViews + AllowOverride None + Require all granted + + + +# Directives controlling the display of server-generated directory listings. +# +# To see the listing of a directory, the Options directive for the +# directory must include "Indexes", and the directory must not contain +# a file matching those listed in the DirectoryIndex directive. + +# IndexOptions: Controls the appearance of server-generated directory +# listings. +IndexOptions FancyIndexing VersionSort + +# AddIcon* directives tell the server which icon to show for different +# files or filename extensions. These are only displayed for +# FancyIndexed directories. +AddIconByEncoding (CMP,/icons/compressed.gif) x-compress x-gzip + +AddIconByType (TXT,/icons/text.gif) text/* +AddIconByType (IMG,/icons/image2.gif) image/* +AddIconByType (SND,/icons/sound2.gif) audio/* +AddIconByType (VID,/icons/movie.gif) video/* + +AddIcon /icons/binary.gif .bin .exe +AddIcon /icons/binhex.gif .hqx +AddIcon /icons/tar.gif .tar +AddIcon /icons/world2.gif .wrl .wrl.gz .vrml .vrm .iv +AddIcon /icons/compressed.gif .Z .z .tgz .gz .zip +AddIcon /icons/a.gif .ps .ai .eps +AddIcon /icons/layout.gif .html .shtml .htm .pdf +AddIcon /icons/text.gif .txt +AddIcon /icons/c.gif .c +AddIcon /icons/p.gif .pl .py +AddIcon /icons/f.gif .for +AddIcon /icons/dvi.gif .dvi +AddIcon /icons/uuencoded.gif .uu +AddIcon /icons/script.gif .conf .sh .shar .csh .ksh .tcl +AddIcon /icons/tex.gif .tex +AddIcon /icons/bomb.gif core + +AddIcon /icons/back.gif .. +AddIcon /icons/hand.right.gif README +AddIcon /icons/folder.gif ^^DIRECTORY^^ +AddIcon /icons/blank.gif ^^BLANKICON^^ + +# DefaultIcon is which icon to show for files which do not have an icon +# explicitly set. +DefaultIcon /icons/unknown.gif + +# AddDescription allows you to place a short description after a file in +# server-generated indexes. These are only displayed for FancyIndexed +# directories. +# Format: AddDescription "description" filename + +#AddDescription "GZIP compressed document" .gz +#AddDescription "tar archive" .tar +#AddDescription "GZIP compressed tar archive" .tgz + +# ReadmeName is the name of the README file the server will look for by +# default, and append to directory listings. + +# HeaderName is the name of a file which should be prepended to +# directory indexes. +ReadmeName README.html +HeaderName HEADER.html + +# IndexIgnore is a set of filenames which directory indexing should ignore +# and not include in the listing. Shell-style wildcarding is permitted. +IndexIgnore .??* *~ *# HEADER* README* RCS CVS *,v *,t + + + +# vim: ts=4 filetype=apache diff --git a/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/00_mod_info.conf b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/00_mod_info.conf new file mode 100644 index 000000000..2cd32c477 --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/00_mod_info.conf @@ -0,0 +1,10 @@ + +# Allow remote server configuration reports, with the URL of +# http://servername/server-info + + SetHandler server-info + Require local + + + +# vim: ts=4 filetype=apache diff --git a/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/00_mod_log_config.conf b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/00_mod_log_config.conf new file mode 100644 index 000000000..ce0238eee --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/00_mod_log_config.conf @@ -0,0 +1,35 @@ + +# The following directives define some format nicknames for use with +# a CustomLog directive (see below). +LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined +LogFormat "%h %l %u %t \"%r\" %>s %b" common + +LogFormat "%{Referer}i -> %U" referer +LogFormat "%{User-Agent}i" agent +LogFormat "%v %h %l %u %t \"%r\" %>s %b %T" script +LogFormat "%v %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" VLOG=%{VLOG}e" vhost + + +# You need to enable mod_logio.c to use %I and %O +LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio +LogFormat "%v %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" vhostio + + +# The location and format of the access logfile (Common Logfile Format). +# If you do not define any access logfiles within a +# container, they will be logged here. Contrariwise, if you *do* +# define per- access logfiles, transactions will be +# logged therein and *not* in this file. +CustomLog /var/log/apache2/access_log common + +# If you would like to have agent and referer logfiles, +# uncomment the following directives. +#CustomLog /var/log/apache2/referer_log referer +#CustomLog /var/log/apache2/agent_logs agent + +# If you prefer a logfile with access, agent, and referer information +# (Combined Logfile Format) you can use the following directive. +#CustomLog /var/log/apache2/access_log combined + + +# vim: ts=4 filetype=apache diff --git a/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/00_mod_mime.conf b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/00_mod_mime.conf new file mode 100644 index 000000000..fb8a9a5d5 --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/00_mod_mime.conf @@ -0,0 +1,46 @@ + +# TypesConfig points to the file containing the list of mappings from +# filename extension to MIME-type. +TypesConfig /etc/mime.types + +# AddType allows you to add to or override the MIME configuration +# file specified in TypesConfig for specific file types. +#AddType application/x-gzip .tgz + +# AddEncoding allows you to have certain browsers uncompress +# information on the fly. Note: Not all browsers support this. +#AddEncoding x-compress .Z +#AddEncoding x-gzip .gz .tgz + +# If the AddEncoding directives above are commented-out, then you +# probably should define those extensions to indicate media types: +AddType application/x-compress .Z +AddType application/x-gzip .gz .tgz + +# AddHandler allows you to map certain file extensions to "handlers": +# actions unrelated to filetype. These can be either built into the server +# or added with the Action directive (see below) + +# To use CGI scripts outside of ScriptAliased directories: +# (You will also need to add "ExecCGI" to the "Options" directive.) +#AddHandler cgi-script .cgi + +# For type maps (negotiated resources): +#AddHandler type-map var + +# Filters allow you to process content before it is sent to the client. +# +# To parse .shtml files for server-side includes (SSI): +# (You will also need to add "Includes" to the "Options" directive.) +#AddType text/html .shtml +#AddOutputFilter INCLUDES .shtml + + + +# The mod_mime_magic module allows the server to use various hints from the +# contents of the file itself to determine its type. The MIMEMagicFile +# directive tells the module where the hint definitions are located. +MIMEMagicFile /etc/apache2/magic + + +# vim: ts=4 filetype=apache diff --git a/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/00_mod_status.conf b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/00_mod_status.conf new file mode 100644 index 000000000..ed8b3c7cb --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/00_mod_status.conf @@ -0,0 +1,15 @@ + +# Allow server status reports generated by mod_status, +# with the URL of http://servername/server-status + + SetHandler server-status + Require local + + +# ExtendedStatus controls whether Apache will generate "full" status +# information (ExtendedStatus On) or just basic information (ExtendedStatus +# Off) when the "server-status" handler is called. +ExtendedStatus On + + +# vim: ts=4 filetype=apache diff --git a/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/00_mod_userdir.conf b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/00_mod_userdir.conf new file mode 100644 index 000000000..0087126c4 --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/00_mod_userdir.conf @@ -0,0 +1,32 @@ +# Settings for user home directories + +# UserDir: The name of the directory that is appended onto a user's home +# directory if a ~user request is received. Note that you must also set +# the default access control for these directories, as in the example below. +UserDir public_html + +# Control access to UserDir directories. The following is an example +# for a site where these directories are restricted to read-only. + + AllowOverride FileInfo AuthConfig Limit Indexes + Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec + + Require all granted + + + Require all denied + + + +# Suexec isn't really required to run cgi-scripts, but it's a really good +# idea if you have multiple users serving websites... + + + Options ExecCGI + SetHandler cgi-script + + + + + +# vim: ts=4 filetype=apache diff --git a/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/00_mpm.conf b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/00_mpm.conf new file mode 100644 index 000000000..bcb9b6b47 --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/00_mpm.conf @@ -0,0 +1,99 @@ +# Server-Pool Management (MPM specific) + +# PidFile: The file in which the server should record its process +# identification number when it starts. +# +# DO NOT CHANGE UNLESS YOU KNOW WHAT YOU ARE DOING +PidFile /run/apache2.pid + +# The accept serialization lock file MUST BE STORED ON A LOCAL DISK. +# Mutex file:/run/apache_mpm_mutex + +# Only one of the below sections will be relevant on your +# installed httpd. Use "/usr/sbin/apache2 -l" to find out the +# active mpm. + +# common MPM configuration +# These configuration directives apply to all MPMs +# +# StartServers: Number of child server processes created at startup +# MaxRequestWorkers: Maximum number of child processes to serve requests +# MaxConnectionsPerChild: Limit on the number of connections that an individual +# child server will handle during its life + + +# prefork MPM +# This is the default MPM if USE=-threads +# +# MinSpareServers: Minimum number of idle child server processes +# MaxSpareServers: Maximum number of idle child server processes + + StartServers 5 + MinSpareServers 5 + MaxSpareServers 10 + MaxRequestWorkers 150 + MaxConnectionsPerChild 10000 + + +# worker MPM +# This is the default MPM if USE=threads +# +# MinSpareThreads: Minimum number of idle threads available to handle request spikes +# MaxSpareThreads: Maximum number of idle threads +# ThreadsPerChild: Number of threads created by each child process + + StartServers 2 + MinSpareThreads 25 + MaxSpareThreads 75 + ThreadsPerChild 25 + MaxRequestWorkers 150 + MaxConnectionsPerChild 10000 + + +# event MPM +# +# MinSpareThreads: Minimum number of idle threads available to handle request spikes +# MaxSpareThreads: Maximum number of idle threads +# ThreadsPerChild: Number of threads created by each child process + + StartServers 2 + MinSpareThreads 25 + MaxSpareThreads 75 + ThreadsPerChild 25 + MaxRequestWorkers 150 + MaxConnectionsPerChild 10000 + + +# peruser MPM +# +# MinSpareProcessors: Minimum number of idle child server processes +# MinProcessors: Minimum number of processors per virtual host +# MaxProcessors: Maximum number of processors per virtual host +# ExpireTimeout: Maximum idle time before a child is killed, 0 to disable +# Multiplexer: Specify a Multiplexer child configuration. +# Processor: Specify a user and group for a specific child process + + MinSpareProcessors 2 + MinProcessors 2 + MaxProcessors 10 + MaxRequestWorkers 150 + MaxConnectionsPerChild 1000 + ExpireTimeout 1800 + + Multiplexer nobody nobody + Processor apache apache + + +# itk MPM +# +# MinSpareServers: Minimum number of idle child server processes +# MaxSpareServers: Maximum number of idle child server processes + + StartServers 5 + MinSpareServers 5 + MaxSpareServers 10 + MaxRequestWorkers 150 + MaxConnectionsPerChild 10000 + + +# vim: ts=4 filetype=apache diff --git a/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/10_mod_mem_cache.conf b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/10_mod_mem_cache.conf new file mode 100644 index 000000000..520d9fd82 --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/10_mod_mem_cache.conf @@ -0,0 +1,10 @@ + +# 128MB cache for objects < 2MB +CacheEnable mem / +MCacheSize 131072 +MCacheMaxObjectCount 1000 +MCacheMinObjectSize 1 +MCacheMaxObjectSize 2097152 + + +# vim: ts=4 filetype=apache diff --git a/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/40_mod_ssl.conf b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/40_mod_ssl.conf new file mode 100644 index 000000000..f51de4641 --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/40_mod_ssl.conf @@ -0,0 +1,67 @@ +# Note: The following must must be present to support +# starting without SSL on platforms with no /dev/random equivalent +# but a statically compiled-in mod_ssl. + +SSLRandomSeed startup builtin +SSLRandomSeed connect builtin + + + +# This is the Apache server configuration file providing SSL support. +# It contains the configuration directives to instruct the server how to +# serve pages over an https connection. For detailing information about these +# directives see + +# Do NOT simply read the instructions in here without understanding +# what they do. They're here only as hints or reminders. If you are unsure +# consult the online docs. You have been warned. + +## Pseudo Random Number Generator (PRNG): +# Configure one or more sources to seed the PRNG of the SSL library. +# The seed data should be of good random quality. +# WARNING! On some platforms /dev/random blocks if not enough entropy +# is available. This means you then cannot use the /dev/random device +# because it would lead to very long connection times (as long as +# it requires to make more entropy available). But usually those +# platforms additionally provide a /dev/urandom device which doesn't +# block. So, if available, use this one instead. Read the mod_ssl User +# Manual for more details. +#SSLRandomSeed startup file:/dev/random 512 +#SSLRandomSeed startup file:/dev/urandom 512 +#SSLRandomSeed connect file:/dev/random 512 +#SSLRandomSeed connect file:/dev/urandom 512 + +## SSL Global Context: +# All SSL configuration in this context applies both to the main server and +# all SSL-enabled virtual hosts. + +# Some MIME-types for downloading Certificates and CRLs + + AddType application/x-x509-ca-cert .crt + AddType application/x-pkcs7-crl .crl + + +## Pass Phrase Dialog: +# Configure the pass phrase gathering process. The filtering dialog program +# (`builtin' is a internal terminal dialog) has to provide the pass phrase on +# stdout. +SSLPassPhraseDialog builtin + +## Inter-Process Session Cache: +# Configure the SSL Session Cache: First the mechanism to use and second the +# expiring timeout (in seconds). +#SSLSessionCache dbm:/run/ssl_scache +SSLSessionCache shmcb:/run/ssl_scache(512000) +SSLSessionCacheTimeout 300 + +## Semaphore: +# Configure the path to the mutual exclusion semaphore the SSL engine uses +# internally for inter-process synchronization. +Mutex file:/run/apache_ssl_mutex ssl-cache + +## SSL Compression: +# Known to be vulnerable thus disabled by default (bug #507324). +SSLCompression off + + +# vim: ts=4 filetype=apache diff --git a/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/41_mod_http2.conf b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/41_mod_http2.conf new file mode 100644 index 000000000..e4c9454e0 --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/41_mod_http2.conf @@ -0,0 +1,9 @@ + + + # enable debugging for this module + #LogLevel http2:info + + #Enable HTTP/2 support + Protocols h2 h2c http/1.1 + + diff --git a/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/45_mod_dav.conf b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/45_mod_dav.conf new file mode 100644 index 000000000..36f6b9cca --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/45_mod_dav.conf @@ -0,0 +1,19 @@ + +DavLockDB "/var/lib/dav/lockdb" + +# The following directives disable redirects on non-GET requests for +# a directory that does not include the trailing slash. This fixes a +# problem with several clients that do not appropriately handle +# redirects for folders with DAV methods. + +BrowserMatch "Microsoft Data Access Internet Publishing Provider" redirect-carefully +BrowserMatch "MS FrontPage" redirect-carefully +BrowserMatch "^WebDrive" redirect-carefully +BrowserMatch "^WebDAVFS/1.[012345678]" redirect-carefully +BrowserMatch "^gnome-vfs/1.0" redirect-carefully +BrowserMatch "^XML Spy" redirect-carefully +BrowserMatch "^Dreamweaver-WebDAV-SCM1" redirect-carefully + + + +# vim: ts=4 filetype=apache diff --git a/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/46_mod_ldap.conf b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/46_mod_ldap.conf new file mode 100644 index 000000000..883061fee --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/modules.d/46_mod_ldap.conf @@ -0,0 +1,18 @@ +# Examples below are taken from the online documentation +# Refer to: +# http://localhost/manual/mod/mod_ldap.html +# http://localhost/manual/mod/mod_auth_ldap.html + +LDAPSharedCacheSize 200000 +LDAPCacheEntries 1024 +LDAPCacheTTL 600 +LDAPOpCacheEntries 1024 +LDAPOpCacheTTL 600 + + + SetHandler ldap-status + Require local + + + +# vim: ts=4 filetype=apache diff --git a/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/vhosts.d/.keep_www-servers_apache-2 b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/vhosts.d/.keep_www-servers_apache-2 new file mode 100644 index 000000000..e69de29bb diff --git a/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/vhosts.d/00_default_ssl_vhost.conf b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/vhosts.d/00_default_ssl_vhost.conf new file mode 100644 index 000000000..bb395473c --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/vhosts.d/00_default_ssl_vhost.conf @@ -0,0 +1,191 @@ + + + +# see bug #178966 why this is in here + +# When we also provide SSL we have to listen to the HTTPS port +# Note: Configurations that use IPv6 but not IPv4-mapped addresses need two +# Listen directives: "Listen [::]:443" and "Listen 0.0.0.0:443" +Listen 443 + + + ServerName localhost + Include /etc/apache2/vhosts.d/default_vhost.include + ErrorLog /var/log/apache2/ssl_error_log + + + TransferLog /var/log/apache2/ssl_access_log + + + ## SSL Engine Switch: + # Enable/Disable SSL for this virtual host. + SSLEngine on + + ## SSLProtocol: + # Don't use SSLv2 anymore as it's considered to be broken security-wise. + # Also disable SSLv3 as most modern browsers are capable of TLS. + SSLProtocol ALL -SSLv2 -SSLv3 + + ## SSL Cipher Suite: + # List the ciphers that the client is permitted to negotiate. + # See the mod_ssl documentation for a complete list. + # This list of ciphers is recommended by mozilla and was stripped off + # its RC4 ciphers. (bug #506924) + SSLCipherSuite ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128:AES256:HIGH:!RC4:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK + + ## SSLHonorCipherOrder: + # Prefer the server's cipher preference order as the client may have a + # weak default order. + SSLHonorCipherOrder On + + ## Server Certificate: + # Point SSLCertificateFile at a PEM encoded certificate. If the certificate + # is encrypted, then you will be prompted for a pass phrase. Note that a + # kill -HUP will prompt again. Keep in mind that if you have both an RSA + # and a DSA certificate you can configure both in parallel (to also allow + # the use of DSA ciphers, etc.) + SSLCertificateFile /etc/ssl/apache2/server.crt + + ## Server Private Key: + # If the key is not combined with the certificate, use this directive to + # point at the key file. Keep in mind that if you've both a RSA and a DSA + # private key you can configure both in parallel (to also allow the use of + # DSA ciphers, etc.) + SSLCertificateKeyFile /etc/ssl/apache2/server.key + + ## Server Certificate Chain: + # Point SSLCertificateChainFile at a file containing the concatenation of + # PEM encoded CA certificates which form the certificate chain for the + # server certificate. Alternatively the referenced file can be the same as + # SSLCertificateFile when the CA certificates are directly appended to the + # server certificate for convinience. + #SSLCertificateChainFile /etc/ssl/apache2/ca.crt + + ## Certificate Authority (CA): + # Set the CA certificate verification path where to find CA certificates + # for client authentication or alternatively one huge file containing all + # of them (file must be PEM encoded). + # Note: Inside SSLCACertificatePath you need hash symlinks to point to the + # certificate files. Use the provided Makefile to update the hash symlinks + # after changes. + #SSLCACertificatePath /etc/ssl/apache2/ssl.crt + #SSLCACertificateFile /etc/ssl/apache2/ca-bundle.crt + + ## Certificate Revocation Lists (CRL): + # Set the CA revocation path where to find CA CRLs for client authentication + # or alternatively one huge file containing all of them (file must be PEM + # encoded). + # Note: Inside SSLCARevocationPath you need hash symlinks to point to the + # certificate files. Use the provided Makefile to update the hash symlinks + # after changes. + #SSLCARevocationPath /etc/ssl/apache2/ssl.crl + #SSLCARevocationFile /etc/ssl/apache2/ca-bundle.crl + + ## Client Authentication (Type): + # Client certificate verification type and depth. Types are none, optional, + # require and optional_no_ca. Depth is a number which specifies how deeply + # to verify the certificate issuer chain before deciding the certificate is + # not valid. + #SSLVerifyClient require + #SSLVerifyDepth 10 + + ## Access Control: + # With SSLRequire you can do per-directory access control based on arbitrary + # complex boolean expressions containing server variable checks and other + # lookup directives. The syntax is a mixture between C and Perl. See the + # mod_ssl documentation for more details. + # + # #SSLRequire ( %{SSL_CIPHER} !~ m/^(EXP|NULL)/ \ + # and %{SSL_CLIENT_S_DN_O} eq "Snake Oil, Ltd." \ + # and %{SSL_CLIENT_S_DN_OU} in {"Staff", "CA", "Dev"} \ + # and %{TIME_WDAY} >= 1 and %{TIME_WDAY} <= 5 \ + # and %{TIME_HOUR} >= 8 and %{TIME_HOUR} <= 20 ) \ + # or %{REMOTE_ADDR} =~ m/^192\.76\.162\.[0-9]+$/ + # + + ## SSL Engine Options: + # Set various options for the SSL engine. + + ## FakeBasicAuth: + # Translate the client X.509 into a Basic Authorisation. This means that the + # standard Auth/DBMAuth methods can be used for access control. The user + # name is the `one line' version of the client's X.509 certificate. + # Note that no password is obtained from the user. Every entry in the user + # file needs this password: `xxj31ZMTZzkVA'. + + ## ExportCertData: + # This exports two additional environment variables: SSL_CLIENT_CERT and + # SSL_SERVER_CERT. These contain the PEM-encoded certificates of the server + # (always existing) and the client (only existing when client + # authentication is used). This can be used to import the certificates into + # CGI scripts. + + ## StdEnvVars: + # This exports the standard SSL/TLS related `SSL_*' environment variables. + # Per default this exportation is switched off for performance reasons, + # because the extraction step is an expensive operation and is usually + # useless for serving static content. So one usually enables the exportation + # for CGI and SSI requests only. + + ## StrictRequire: + # This denies access when "SSLRequireSSL" or "SSLRequire" applied even under + # a "Satisfy any" situation, i.e. when it applies access is denied and no + # other module can change it. + + ## OptRenegotiate: + # This enables optimized SSL connection renegotiation handling when SSL + # directives are used in per-directory context. + #SSLOptions +FakeBasicAuth +ExportCertData +StrictRequire + + SSLOptions +StdEnvVars + + + + SSLOptions +StdEnvVars + + + ## SSL Protocol Adjustments: + # The safe and default but still SSL/TLS standard compliant shutdown + # approach is that mod_ssl sends the close notify alert but doesn't wait + # for the close notify alert from client. When you need a different + # shutdown approach you can use one of the following variables: + + ## ssl-unclean-shutdown: + # This forces an unclean shutdown when the connection is closed, i.e. no + # SSL close notify alert is send or allowed to received. This violates the + # SSL/TLS standard but is needed for some brain-dead browsers. Use this when + # you receive I/O errors because of the standard approach where mod_ssl + # sends the close notify alert. + + ## ssl-accurate-shutdown: + # This forces an accurate shutdown when the connection is closed, i.e. a + # SSL close notify alert is send and mod_ssl waits for the close notify + # alert of the client. This is 100% SSL/TLS standard compliant, but in + # practice often causes hanging connections with brain-dead browsers. Use + # this only for browsers where you know that their SSL implementation works + # correctly. + # Notice: Most problems of broken clients are also related to the HTTP + # keep-alive facility, so you usually additionally want to disable + # keep-alive for those clients, too. Use variable "nokeepalive" for this. + # Similarly, one has to force some clients to use HTTP/1.0 to workaround + # their broken HTTP/1.1 implementation. Use variables "downgrade-1.0" and + # "force-response-1.0" for this. + + BrowserMatch ".*MSIE.*" \ + nokeepalive ssl-unclean-shutdown \ + downgrade-1.0 force-response-1.0 + + + ## Per-Server Logging: + # The home of a custom SSL log file. Use this when you want a compact + # non-error SSL logfile on a virtual host basis. + + CustomLog /var/log/apache2/ssl_request_log \ + "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b" + + + + + + +# vim: ts=4 filetype=apache diff --git a/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/vhosts.d/00_default_vhost.conf b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/vhosts.d/00_default_vhost.conf new file mode 100644 index 000000000..b9766b5f1 --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/vhosts.d/00_default_vhost.conf @@ -0,0 +1,45 @@ +# Virtual Hosts +# +# If you want to maintain multiple domains/hostnames on your +# machine you can setup VirtualHost containers for them. Most configurations +# use only name-based virtual hosts so the server doesn't need to worry about +# IP addresses. This is indicated by the asterisks in the directives below. +# +# Please see the documentation at +# +# for further details before you try to setup virtual hosts. +# +# You may use the command line option '-S' to verify your virtual host +# configuration. + + +# see bug #178966 why this is in here + +# Listen: Allows you to bind Apache to specific IP addresses and/or +# ports, instead of the default. See also the +# directive. +# +# Change this to Listen on specific IP addresses as shown below to +# prevent Apache from glomming onto all bound IP addresses. +# +#Listen 12.34.56.78:80 +Listen 80 + +# When virtual hosts are enabled, the main host defined in the default +# httpd.conf configuration will go away. We redefine it here so that it is +# still available. +# +# If you disable this vhost by removing -D DEFAULT_VHOST from +# /etc/conf.d/apache2, the first defined virtual host elsewhere will be +# the default. + + ServerName localhost + Include /etc/apache2/vhosts.d/default_vhost.include + + + ServerEnvironment apache apache + + + + +# vim: ts=4 filetype=apache diff --git a/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/vhosts.d/default_vhost.include b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/vhosts.d/default_vhost.include new file mode 100644 index 000000000..af6ece85b --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/vhosts.d/default_vhost.include @@ -0,0 +1,71 @@ +# ServerAdmin: Your address, where problems with the server should be +# e-mailed. This address appears on some server-generated pages, such +# as error documents. e.g. admin@your-domain.com +ServerAdmin root@localhost + +# DocumentRoot: The directory out of which you will serve your +# documents. By default, all requests are taken from this directory, but +# symbolic links and aliases may be used to point to other locations. +# +# If you change this to something that isn't under /var/www then suexec +# will no longer work. +DocumentRoot "/var/www/localhost/htdocs" + +# This should be changed to whatever you set DocumentRoot to. + + # Possible values for the Options directive are "None", "All", + # or any combination of: + # Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews + # + # Note that "MultiViews" must be named *explicitly* --- "Options All" + # doesn't give it to you. + # + # The Options directive is both complicated and important. Please see + # http://httpd.apache.org/docs/2.4/mod/core.html#options + # for more information. + Options Indexes FollowSymLinks + + # AllowOverride controls what directives may be placed in .htaccess files. + # It can be "All", "None", or any combination of the keywords: + # Options FileInfo AuthConfig Limit + AllowOverride All + + # Controls who can get stuff from this server. + Require all granted + + + + # Redirect: Allows you to tell clients about documents that used to + # exist in your server's namespace, but do not anymore. The client + # will make a new request for the document at its new location. + # Example: + # Redirect permanent /foo http://www.example.com/bar + + # Alias: Maps web paths into filesystem paths and is used to + # access content that does not live under the DocumentRoot. + # Example: + # Alias /webpath /full/filesystem/path + # + # If you include a trailing / on /webpath then the server will + # require it to be present in the URL. You will also likely + # need to provide a section to allow access to + # the filesystem path. + + # ScriptAlias: This controls which directories contain server scripts. + # ScriptAliases are essentially the same as Aliases, except that + # documents in the target directory are treated as applications and + # run by the server when requested rather than as documents sent to the + # client. The same rules about trailing "/" apply to ScriptAlias + # directives as to Alias. + ScriptAlias /cgi-bin/ "/var/www/localhost/cgi-bin/" + + +# "/var/www/localhost/cgi-bin" should be changed to whatever your ScriptAliased +# CGI directory exists, if you have that configured. + + AllowOverride None + Options None + Require all granted + + +# vim: ts=4 filetype=apache diff --git a/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/vhosts.d/gentoo.example.com.conf b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/vhosts.d/gentoo.example.com.conf new file mode 100644 index 000000000..41de4d236 --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/apache2/vhosts.d/gentoo.example.com.conf @@ -0,0 +1,7 @@ + + ServerName gentoo.example.com + ServerAdmin webmaster@localhost + DocumentRoot /var/www/html + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined + diff --git a/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/conf.d/apache2 b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/conf.d/apache2 new file mode 100644 index 000000000..b7ecb4f2a --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/conf.d/apache2 @@ -0,0 +1,74 @@ +# /etc/conf.d/apache2: config file for /etc/init.d/apache2 + +# When you install a module it is easy to activate or deactivate the modules +# and other features of apache using the APACHE2_OPTS line. Every module should +# install a configuration in /etc/apache2/modules.d. In that file will have an +# directive where NNN is the option to enable that module. +# +# Here are the options available in the default configuration: +# +# AUTH_DIGEST Enables mod_auth_digest +# AUTHNZ_LDAP Enables authentication through mod_ldap (available if USE=ldap) +# CACHE Enables mod_cache +# DAV Enables mod_dav +# ERRORDOCS Enables default error documents for many languages. +# INFO Enables mod_info, a useful module for debugging +# LANGUAGE Enables content-negotiation based on language and charset. +# LDAP Enables mod_ldap (available if USE=ldap) +# MANUAL Enables /manual/ to be the apache manual (available if USE=docs) +# MEM_CACHE Enables default configuration mod_mem_cache +# PROXY Enables mod_proxy +# SSL Enables SSL (available if USE=ssl) +# STATUS Enabled mod_status, a useful module for statistics +# SUEXEC Enables running CGI scripts (in USERDIR) through suexec. +# USERDIR Enables /~username mapping to /home/username/public_html +# +# +# The following two options provide the default virtual host for the HTTP and +# HTTPS protocol. YOU NEED TO ENABLE AT LEAST ONE OF THEM, otherwise apache +# will not listen for incomming connections on the approriate port. +# +# DEFAULT_VHOST Enables name-based virtual hosts, with the default +# virtual host being in /var/www/localhost/htdocs +# SSL_DEFAULT_VHOST Enables default vhost for SSL (you should enable this +# when you enable SSL) +# +APACHE2_OPTS="-D DEFAULT_VHOST -D INFO -D SSL -D SSL_DEFAULT_VHOST -D LANGUAGE" + +# Extended options for advanced uses of Apache ONLY +# You don't need to edit these unless you are doing crazy Apache stuff +# As not having them set correctly, or feeding in an incorrect configuration +# via them will result in Apache failing to start +# YOU HAVE BEEN WARNED. + +# PID file +#PIDFILE=/var/run/apache2.pid + +# timeout for startup/shutdown checks +#TIMEOUT=10 + +# ServerRoot setting +#SERVERROOT=/usr/lib64/apache2 + +# Configuration file location +# - If this does NOT start with a '/', then it is treated relative to +# $SERVERROOT by Apache +#CONFIGFILE=/etc/apache2/httpd.conf + +# Location to log startup errors to +# They are normally dumped to your terminal. +#STARTUPERRORLOG="/var/log/apache2/startuperror.log" + +# A command that outputs a formatted text version of the HTML at the URL +# of the command line. Designed for lynx, however other programs may work. +#LYNX="lynx -dump" + +# The URL to your server's mod_status status page. +# Required for status and fullstatus +#STATUSURL="http://localhost/server-status" + +# Method to use when reloading the server +# Valid options are 'restart' and 'graceful' +# See http://httpd.apache.org/docs/2.2/stopping.html for information on +# what they do and how they differ. +#RELOAD_TYPE="graceful" diff --git a/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/sites b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/sites new file mode 100644 index 000000000..7f0b3a8b3 --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/gentoo_apache/apache/sites @@ -0,0 +1,3 @@ +vhosts.d/gentoo.example.com.conf, gentoo.example.com +vhosts.d/00_default_vhost.conf, localhost +vhosts.d/00_default_ssl_vhost.conf, localhost diff --git a/certbot-apache/certbot_apache/tests/tls_sni_01_test.py b/certbot-apache/certbot_apache/tests/tls_sni_01_test.py index 62464d5d0..6c37c2ecc 100644 --- a/certbot-apache/certbot_apache/tests/tls_sni_01_test.py +++ b/certbot-apache/certbot_apache/tests/tls_sni_01_test.py @@ -23,7 +23,8 @@ class TlsSniPerformTest(util.ApacheTest): super(TlsSniPerformTest, self).setUp() config = util.get_apache_configurator( - self.config_path, self.vhost_path, self.config_dir, self.work_dir) + self.config_path, self.vhost_path, self.config_dir, + self.work_dir) config.config.tls_sni_01_port = 443 from certbot_apache import tls_sni_01 @@ -41,8 +42,8 @@ class TlsSniPerformTest(util.ApacheTest): @mock.patch("certbot.util.exe_exists") @mock.patch("certbot.util.run_script") def test_perform1(self, _, mock_exists): - mock_register = mock.Mock() - self.sni.configurator.reverter.register_undo_command = mock_register + self.sni.configurator.parser.modules.add("socache_shmcb_module") + self.sni.configurator.parser.modules.add("ssl_module") mock_exists.return_value = True self.sni.configurator.parser.update_runtime_variables = mock.Mock() @@ -55,10 +56,6 @@ class TlsSniPerformTest(util.ApacheTest): self.sni._setup_challenge_cert = mock_setup_cert responses = self.sni.perform() - - # Make sure that register_undo_command was called into temp directory. - self.assertEqual(True, mock_register.call_args[0][0]) - mock_setup_cert.assert_called_once_with(achall) # Check to make sure challenge config path is included in apache config @@ -71,7 +68,7 @@ class TlsSniPerformTest(util.ApacheTest): def test_perform2(self): # Avoid load module self.sni.configurator.parser.modules.add("ssl_module") - + self.sni.configurator.parser.modules.add("socache_shmcb_module") acme_responses = [] for achall in self.achalls: self.sni.add_chall(achall) @@ -81,7 +78,8 @@ class TlsSniPerformTest(util.ApacheTest): # pylint: disable=protected-access self.sni._setup_challenge_cert = mock_setup_cert - with mock.patch("certbot_apache.configurator.ApacheConfigurator.enable_mod"): + with mock.patch( + "certbot_apache.override_debian.DebianConfigurator.enable_mod"): sni_responses = self.sni.perform() self.assertEqual(mock_setup_cert.call_count, 2) diff --git a/certbot-apache/certbot_apache/tests/util.py b/certbot-apache/certbot_apache/tests/util.py index 34d2476f7..2405110c5 100644 --- a/certbot-apache/certbot_apache/tests/util.py +++ b/certbot-apache/certbot_apache/tests/util.py @@ -1,5 +1,6 @@ """Common utilities for certbot_apache.""" import os +import shutil import sys import unittest @@ -16,7 +17,7 @@ from certbot.plugins import common from certbot.tests import util as test_util from certbot_apache import configurator -from certbot_apache import constants +from certbot_apache import entrypoint from certbot_apache import obj @@ -38,6 +39,9 @@ class ApacheTest(unittest.TestCase): # pylint: disable=too-few-public-methods self.rsa512jwk = jose.JWKRSA.load(test_util.load_vector( "rsa512_key.pem")) + self.config = get_apache_configurator(self.config_path, vhost_root, + self.config_dir, self.work_dir) + # Make sure all vhosts in sites-enabled are symlinks (Python packaging # does not preserve symlinks) sites_enabled = os.path.join(self.config_path, "sites-enabled") @@ -55,8 +59,13 @@ class ApacheTest(unittest.TestCase): # pylint: disable=too-few-public-methods os.path.pardir, "sites-available", vhost_basename) os.symlink(target, vhost) + def tearDown(self): + shutil.rmtree(self.temp_dir) + shutil.rmtree(self.config_dir) + shutil.rmtree(self.work_dir) -class ParserTest(ApacheTest): # pytlint: disable=too-few-public-methods + +class ParserTest(ApacheTest): def setUp(self, test_dir="debian_apache_2_4/multiple_vhosts", config_root="debian_apache_2_4/multiple_vhosts/apache2", @@ -72,12 +81,16 @@ class ParserTest(ApacheTest): # pytlint: disable=too-few-public-methods with mock.patch("certbot_apache.parser.ApacheParser." "update_runtime_variables"): self.parser = ApacheParser( - self.aug, self.config_path, self.vhost_path) + self.aug, self.config_path, self.vhost_path, + configurator=self.config) -def get_apache_configurator( +def get_apache_configurator( # pylint: disable=too-many-arguments, too-many-locals config_path, vhost_path, - config_dir, work_dir, version=(2, 4, 7), conf=None): + config_dir, work_dir, version=(2, 4, 7), + conf=None, + os_info="generic", + conf_vhost_path=None): """Create an Apache Configurator with the specified options. :param conf: Function that returns binary paths. self.conf in Configurator @@ -86,8 +99,8 @@ def get_apache_configurator( backups = os.path.join(work_dir, "backups") mock_le_config = mock.MagicMock( apache_server_root=config_path, - apache_vhost_root=vhost_path, - apache_le_vhost_ext=constants.os_constant("le_vhost_ext"), + apache_vhost_root=conf_vhost_path, + apache_le_vhost_ext="-le-ssl.conf", apache_challenge_location=config_path, backup_dir=backups, config_dir=config_dir, @@ -95,22 +108,37 @@ def get_apache_configurator( in_progress_dir=os.path.join(backups, "IN_PROGRESS"), work_dir=work_dir) - with mock.patch("certbot_apache.configurator.util.run_script"): - with mock.patch("certbot_apache.configurator.util." - "exe_exists") as mock_exe_exists: - mock_exe_exists.return_value = True - with mock.patch("certbot_apache.parser.ApacheParser." - "update_runtime_variables"): - config = configurator.ApacheConfigurator( - config=mock_le_config, - name="apache", - version=version) - # This allows testing scripts to set it a bit more quickly - if conf is not None: - config.conf = conf # pragma: no cover + orig_os_constant = configurator.ApacheConfigurator(mock_le_config, + name="apache", + version=version).constant - config.prepare() + def mock_os_constant(key, vhost_path=vhost_path): + """Mock default vhost path""" + if key == "vhost_root": + return vhost_path + else: + return orig_os_constant(key) + with mock.patch("certbot_apache.configurator.ApacheConfigurator.constant") as mock_cons: + mock_cons.side_effect = mock_os_constant + with mock.patch("certbot_apache.configurator.util.run_script"): + with mock.patch("certbot_apache.configurator.util." + "exe_exists") as mock_exe_exists: + mock_exe_exists.return_value = True + with mock.patch("certbot_apache.parser.ApacheParser." + "update_runtime_variables"): + try: + config_class = entrypoint.OVERRIDE_CLASSES[os_info] + except KeyError: + config_class = configurator.ApacheConfigurator + config = config_class(config=mock_le_config, name="apache", + version=version) + # This allows testing scripts to set it a bit more + # quickly + if conf is not None: + config.conf = conf # pragma: no cover + + config.prepare() return config diff --git a/certbot-apache/setup.py b/certbot-apache/setup.py index b276f49f8..3270f2c79 100644 --- a/certbot-apache/setup.py +++ b/certbot-apache/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.20.0.dev0' +version = '0.21.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ @@ -63,7 +63,7 @@ setup( }, entry_points={ 'certbot.plugins': [ - 'apache = certbot_apache.configurator:ApacheConfigurator', + 'apache = certbot_apache.entrypoint:ENTRYPOINT', ], }, test_suite='certbot_apache', diff --git a/certbot-auto b/certbot-auto index 25f2ce889..444bee1b9 100755 --- a/certbot-auto +++ b/certbot-auto @@ -31,7 +31,7 @@ if [ -z "$VENV_PATH" ]; then fi VENV_BIN="$VENV_PATH/bin" BOOTSTRAP_VERSION_PATH="$VENV_PATH/certbot-auto-bootstrap-version.txt" -LE_AUTO_VERSION="0.19.0" +LE_AUTO_VERSION="0.20.0" BASENAME=$(basename $0) USAGE="Usage: $BASENAME [OPTIONS] A self-updating wrapper script for the Certbot ACME client. When run, updates @@ -1062,9 +1062,10 @@ zope.interface==4.1.3 \ --hash=sha256:928138365245a0e8869a5999fbcc2a45475a0a6ed52a494d60dbdc540335fedd \ --hash=sha256:0d841ba1bb840eea0e6489dc5ecafa6125554971f53b5acb87764441e61bceba \ --hash=sha256:b09c8c1d47b3531c400e0195697f1414a63221de6ef478598a4f1460f7d9a392 -mock==2.0.0 \ - --hash=sha256:5ce3c71c5545b472da17b72268978914d0252980348636840bd34a00b5cc96c1 \ - --hash=sha256:b158b6df76edd239b8208d481dc46b6afd45a846b7812ff0ce58971cf5bc8bba +# Using an older version of mock here prevents regressions of #5276. +mock==1.3.0 \ + --hash=sha256:3f573a18be94de886d1191f27c168427ef693e8dcfcecf95b170577b2eb69cbb \ + --hash=sha256:1e247dbecc6ce057299eb7ee019ad68314bb93152e81d9a6110d35f4d5eca0f6 # Contains the requirements for the letsencrypt package. # @@ -1077,18 +1078,18 @@ letsencrypt==0.7.0 \ --hash=sha256:105a5fb107e45bcd0722eb89696986dcf5f08a86a321d6aef25a0c7c63375ade \ --hash=sha256:c36e532c486a7e92155ee09da54b436a3c420813ec1c590b98f635d924720de9 -certbot==0.19.0 \ - --hash=sha256:3207ee5319bfc37e855c25a43148275fcfb37869eefde9087405012049734a20 \ - --hash=sha256:a7230791dff5d085738119fc22d88ad9d8a35d0b6a3d67806fe33990c7c79d53 -acme==0.19.0 \ - --hash=sha256:c612eafe234d722d97bb5d3dbc49e5522f44be29611f7577954eb893e5c2d6de \ - --hash=sha256:1fa23d64d494aaf001e6fe857c461fcfff10f75a1c2c35ec831447f641e1e822 -certbot-apache==0.19.0 \ - --hash=sha256:fadb28b33bfabc85cdb962b5b149bef58b98f0606b78581db7895fe38323f37c \ - --hash=sha256:70306ca2d5be7f542af68d46883c0ae39527cf202f17ef92cd256fb0bc3f1619 -certbot-nginx==0.19.0 \ - --hash=sha256:4909cb3db49919fb35590793cac28e1c0b6dbd29cbedf887b9106e5fcef5362c \ - --hash=sha256:cb5a224a3f277092555c25096d1678fc735306fd3a43447649ebe524c7ca79e1 +certbot==0.20.0 \ + --hash=sha256:c6b6bd288700898d1eb31a65b605e3a5fc10f1e3213ce468207d76a2decb9d35 \ + --hash=sha256:cabf505b64fb400c4239dcdbaeb882079477eb6a8442268596a8791b9e34de88 +acme==0.20.0 \ + --hash=sha256:8b0cee192c0d76d6f4045bdb14b3cfd29d9720e0dad2046794a2a555f1eaccb7 \ + --hash=sha256:45121aed6c8cc2f31896ac1083068dfdeb613f3edeff9576dc0d10632ea5a3d5 +certbot-apache==0.20.0 \ + --hash=sha256:f7e4dbc154d2e9d1461118b6dd3dbd16f6892da468f060eeaa162aff673347e2 \ + --hash=sha256:0ba499706451ffbccb172bcf93d6ef4c6cc8599157077a4fa6dfbe5a83c7921f +certbot-nginx==0.20.0 \ + --hash=sha256:b6e372e8740b20dd9bd63837646157ac97b3c9a65affd3954571b8e872ae9ecf \ + --hash=sha256:6379fdf20d9a7651fe30bb8d4b828cbea178cc263d7af5a380fc4508d793b9ae UNLIKELY_EOF # ------------------------------------------------------------------------- diff --git a/certbot-compatibility-test/certbot_compatibility_test/configurators/apache/common.py b/certbot-compatibility-test/certbot_compatibility_test/configurators/apache/common.py index 2e9e68daf..1d2cfdeca 100644 --- a/certbot-compatibility-test/certbot_compatibility_test/configurators/apache/common.py +++ b/certbot-compatibility-test/certbot_compatibility_test/configurators/apache/common.py @@ -9,8 +9,7 @@ import zope.interface from certbot import configuration from certbot import errors as le_errors from certbot import util as certbot_util -from certbot_apache import configurator -from certbot_apache import constants +from certbot_apache import entrypoint from certbot_compatibility_test import errors from certbot_compatibility_test import interfaces from certbot_compatibility_test import util @@ -56,13 +55,14 @@ class Proxy(configurators_common.Proxy): def _prepare_configurator(self): """Prepares the Apache plugin for testing""" - for k in constants.CLI_DEFAULTS_DEBIAN.keys(): - setattr(self.le_config, "apache_" + k, constants.os_constant(k)) + for k in entrypoint.ENTRYPOINT.OS_DEFAULTS.keys(): + setattr(self.le_config, "apache_" + k, + entrypoint.ENTRYPOINT.OS_DEFAULTS[k]) # An alias self.le_config.apache_handle_modules = self.le_config.apache_handle_mods - self._configurator = configurator.ApacheConfigurator( + self._configurator = entrypoint.ENTRYPOINT( config=configuration.NamespaceConfig(self.le_config), name="apache") self._configurator.prepare() diff --git a/certbot-compatibility-test/setup.py b/certbot-compatibility-test/setup.py index 166c383b3..1faf30643 100644 --- a/certbot-compatibility-test/setup.py +++ b/certbot-compatibility-test/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.20.0.dev0' +version = '0.21.0.dev0' install_requires = [ 'certbot', diff --git a/certbot-dns-cloudflare/setup.py b/certbot-dns-cloudflare/setup.py index 6392e483c..428271045 100644 --- a/certbot-dns-cloudflare/setup.py +++ b/certbot-dns-cloudflare/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.20.0.dev0' +version = '0.21.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/certbot-dns-cloudxns/setup.py b/certbot-dns-cloudxns/setup.py index 304acf110..4a103193f 100644 --- a/certbot-dns-cloudxns/setup.py +++ b/certbot-dns-cloudxns/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.20.0.dev0' +version = '0.21.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/certbot-dns-digitalocean/certbot_dns_digitalocean/dns_digitalocean_test.py b/certbot-dns-digitalocean/certbot_dns_digitalocean/dns_digitalocean_test.py index 0fdacf4ad..3b8edce64 100644 --- a/certbot-dns-digitalocean/certbot_dns_digitalocean/dns_digitalocean_test.py +++ b/certbot-dns-digitalocean/certbot_dns_digitalocean/dns_digitalocean_test.py @@ -131,7 +131,7 @@ class DigitalOceanClientTest(unittest.TestCase): self.digitalocean_client.del_txt_record(DOMAIN, self.record_name, self.record_content) - correct_record_mock.destroy.assert_called() + self.assertTrue(correct_record_mock.destroy.called) self.assertFalse(first_record_mock.destroy.call_args_list) self.assertFalse(last_record_mock.destroy.call_args_list) diff --git a/certbot-dns-digitalocean/setup.py b/certbot-dns-digitalocean/setup.py index 489321435..23098d4b6 100644 --- a/certbot-dns-digitalocean/setup.py +++ b/certbot-dns-digitalocean/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.20.0.dev0' +version = '0.21.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/certbot-dns-dnsimple/setup.py b/certbot-dns-dnsimple/setup.py index 67d68ee16..4ed5a06ca 100644 --- a/certbot-dns-dnsimple/setup.py +++ b/certbot-dns-dnsimple/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.20.0.dev0' +version = '0.21.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/certbot-dns-dnsmadeeasy/setup.py b/certbot-dns-dnsmadeeasy/setup.py index 1ddec208b..8a0b88aab 100644 --- a/certbot-dns-dnsmadeeasy/setup.py +++ b/certbot-dns-dnsmadeeasy/setup.py @@ -4,15 +4,13 @@ from setuptools import setup from setuptools import find_packages -version = '0.20.0.dev0' +version = '0.21.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ 'acme=={0}'.format(version), 'certbot=={0}'.format(version), - # new versions of lexicon require that we install dnsmadeeasy extras and - # 2.1.11 is the first version that defines them. - 'dns-lexicon[dnsmadeeasy]>=2.1.11', + 'dns-lexicon', 'mock', # For pkg_resources. >=1.0 so pip resolves it to a version cryptography # will tolerate; see #2599: diff --git a/certbot-dns-google/setup.py b/certbot-dns-google/setup.py index b40899e80..b00bd1ac3 100644 --- a/certbot-dns-google/setup.py +++ b/certbot-dns-google/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.20.0.dev0' +version = '0.21.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/certbot-dns-luadns/setup.py b/certbot-dns-luadns/setup.py index 1b72168e8..b8f50254e 100644 --- a/certbot-dns-luadns/setup.py +++ b/certbot-dns-luadns/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.20.0.dev0' +version = '0.21.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/certbot-dns-nsone/setup.py b/certbot-dns-nsone/setup.py index e9dc2b31d..2a388e487 100644 --- a/certbot-dns-nsone/setup.py +++ b/certbot-dns-nsone/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.20.0.dev0' +version = '0.21.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/certbot-dns-rfc2136/setup.py b/certbot-dns-rfc2136/setup.py index 79b523aed..78007afb5 100644 --- a/certbot-dns-rfc2136/setup.py +++ b/certbot-dns-rfc2136/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.20.0.dev0' +version = '0.21.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/certbot-dns-route53/certbot_dns_route53/dns_route53_test.py b/certbot-dns-route53/certbot_dns_route53/dns_route53_test.py index ff07b6ccd..d5f1b2816 100644 --- a/certbot-dns-route53/certbot_dns_route53/dns_route53_test.py +++ b/certbot-dns-route53/certbot_dns_route53/dns_route53_test.py @@ -31,7 +31,7 @@ class AuthenticatorTest(unittest.TestCase, dns_test_common.BaseAuthenticatorTest self.auth._change_txt_record.assert_called_once_with("UPSERT", '_acme-challenge.' + DOMAIN, mock.ANY) - self.auth._wait_for_change.assert_called_once() + self.assertEqual(self.auth._wait_for_change.call_count, 1) def test_perform_no_credentials_error(self): self.auth._change_txt_record = mock.MagicMock(side_effect=NoCredentialsError) @@ -183,7 +183,8 @@ class ClientTest(unittest.TestCase): self.client._change_txt_record("FOO", DOMAIN, "foo") - self.client.r53.change_resource_record_sets.assert_called_once() + call_count = self.client.r53.change_resource_record_sets.call_count + self.assertEqual(call_count, 1) def test_wait_for_change(self): self.client.r53.get_change = mock.MagicMock( @@ -192,7 +193,7 @@ class ClientTest(unittest.TestCase): self.client._wait_for_change(1) - self.client.r53.get_change.assert_called() + self.assertTrue(self.client.r53.get_change.called) if __name__ == "__main__": diff --git a/certbot-dns-route53/setup.py b/certbot-dns-route53/setup.py index 2a14e8ab1..7d1eb0bc9 100644 --- a/certbot-dns-route53/setup.py +++ b/certbot-dns-route53/setup.py @@ -3,7 +3,7 @@ import sys from distutils.core import setup from setuptools import find_packages -version = '0.20.0.dev0' +version = '0.21.0.dev0' install_requires = [ 'acme=={0}'.format(version), diff --git a/certbot-nginx/certbot_nginx/configurator.py b/certbot-nginx/certbot_nginx/configurator.py index 98990664f..e9d4e36d4 100644 --- a/certbot-nginx/certbot_nginx/configurator.py +++ b/certbot-nginx/certbot_nginx/configurator.py @@ -23,43 +23,24 @@ from certbot import util from certbot.plugins import common from certbot_nginx import constants -from certbot_nginx import tls_sni_01 +from certbot_nginx import nginxparser from certbot_nginx import parser +from certbot_nginx import tls_sni_01 logger = logging.getLogger(__name__) -REDIRECT_BLOCK = [[ - ['\n ', 'if', ' ', '($scheme', ' ', '!=', ' ', '"https")'], - [['\n ', 'return', ' ', '301', ' ', 'https://$host$request_uri'], - '\n '] -], ['\n']] - -TEST_REDIRECT_BLOCK = [ - [ - ['if', '($scheme', '!=', '"https")'], - [ - ['return', '301', 'https://$host$request_uri'] - ] - ], - ['#', ' managed by Certbot'] +REDIRECT_BLOCK = [ + ['\n ', 'return', ' ', '301', ' ', 'https://$host$request_uri'], + ['\n'] ] REDIRECT_COMMENT_BLOCK = [ ['\n ', '#', ' Redirect non-https traffic to https'], - ['\n ', '#', ' if ($scheme != "https") {'], - ['\n ', '#', " return 301 https://$host$request_uri;"], - ['\n ', '#', " } # managed by Certbot"], + ['\n ', '#', ' return 301 https://$host$request_uri;'], ['\n'] ] -TEST_REDIRECT_COMMENT_BLOCK = [ - ['#', ' Redirect non-https traffic to https'], - ['#', ' if ($scheme != "https") {'], - ['#', " return 301 https://$host$request_uri;"], - ['#', " } # managed by Certbot"], -] - @zope.interface.implementer(interfaces.IAuthenticator, interfaces.IInstaller) @zope.interface.provider(interfaces.IPluginFactory) class NginxConfigurator(common.Installer): @@ -194,9 +175,7 @@ class NginxConfigurator(common.Installer): "The nginx plugin currently requires --fullchain-path to " "install a cert.") - vhost = self.choose_vhost(domain, raise_if_no_match=False) - if vhost is None: - vhost = self._vhost_from_duplicated_default(domain) + vhost = self.choose_vhost(domain, create_if_no_match=True) cert_directives = [['\n ', 'ssl_certificate', ' ', fullchain_path], ['\n ', 'ssl_certificate_key', ' ', key_path]] @@ -214,7 +193,7 @@ class NginxConfigurator(common.Installer): ####################### # Vhost parsing methods ####################### - def choose_vhost(self, target_name, raise_if_no_match=True): + def choose_vhost(self, target_name, create_if_no_match=False): """Chooses a virtual host based on the given domain name. .. note:: This makes the vhost SSL-enabled if it isn't already. Follows @@ -228,8 +207,8 @@ class NginxConfigurator(common.Installer): hostname. Currently we just ignore this. :param str target_name: domain name - :param bool raise_if_no_match: True iff not finding a match is an error; - otherwise, return None + :param bool create_if_no_match: If we should create a new vhost from default + when there is no match found :returns: ssl vhost associated with name :rtype: :class:`~certbot_nginx.obj.VirtualHost` @@ -240,7 +219,9 @@ class NginxConfigurator(common.Installer): matches = self._get_ranked_matches(target_name) vhost = self._select_best_name_match(matches) if not vhost: - if raise_if_no_match: + if create_if_no_match: + vhost = self._vhost_from_duplicated_default(target_name) + else: # No matches. Raise a misconfiguration error. raise errors.MisconfigurationError( ("Cannot find a VirtualHost matching domain %s. " @@ -248,16 +229,12 @@ class NginxConfigurator(common.Installer): "please add a corresponding server_name directive to your " "nginx configuration: " "https://nginx.org/en/docs/http/server_names.html") % (target_name)) - else: - return None - else: - # Note: if we are enhancing with ocsp, vhost should already be ssl. - if not vhost.ssl: - self._make_server_ssl(vhost) + # Note: if we are enhancing with ocsp, vhost should already be ssl. + if not vhost.ssl: + self._make_server_ssl(vhost) return vhost - def ipv6_info(self, port): """Returns tuple of booleans (ipv6_active, ipv6only_present) ipv6_active is true if any server block listens ipv6 address in any port @@ -285,18 +262,19 @@ class NginxConfigurator(common.Installer): def _vhost_from_duplicated_default(self, domain): if self.new_vhost is None: default_vhost = self._get_default_vhost() - self.new_vhost = self.parser.create_new_vhost_from_default(default_vhost) - if not self.new_vhost.ssl: - self._make_server_ssl(self.new_vhost) + self.new_vhost = self.parser.duplicate_vhost(default_vhost, delete_default=True) self.new_vhost.names = set() - self.new_vhost.names.add(domain) + self._add_server_name_to_vhost(self.new_vhost, domain) + return self.new_vhost + + def _add_server_name_to_vhost(self, vhost, domain): + vhost.names.add(domain) name_block = [['\n ', 'server_name']] - for name in self.new_vhost.names: + for name in vhost.names: name_block[0].append(' ') name_block[0].append(name) - self.parser.add_server_directives(self.new_vhost, name_block, replace=True) - return self.new_vhost + self.parser.add_server_directives(vhost, name_block, replace=True) def _get_default_vhost(self): vhost_list = self.parser.get_vhosts() @@ -505,11 +483,7 @@ class NginxConfigurator(common.Installer): def _make_server_ssl(self, vhost): """Make a server SSL. - Make a server SSL based on server_name and filename by adding a - ``listen IConfig.tls_sni_01_port ssl`` directive to the server block. - - .. todo:: Maybe this should create a new block instead of modifying - the existing one? + Make a server SSL by adding new listen and SSL directives. :param vhost: The vhost to add SSL to. :type vhost: :class:`~certbot_nginx.obj.VirtualHost` @@ -529,7 +503,9 @@ class NginxConfigurator(common.Installer): ipv6_block = ['\n ', 'listen', ' ', - '[::]:{0} ssl'.format(self.config.tls_sni_01_port)] + '[::]:{0}'.format(self.config.tls_sni_01_port), + ' ', + 'ssl'] if not ipv6info[1]: # ipv6only=on is absent in global config ipv6_block.append(' ') @@ -539,8 +515,9 @@ class NginxConfigurator(common.Installer): ipv4_block = ['\n ', 'listen', ' ', - '{0} ssl'.format(self.config.tls_sni_01_port)] - + '{0}'.format(self.config.tls_sni_01_port), + ' ', + 'ssl'] snakeoil_cert, snakeoil_key = self._get_snakeoil_paths() @@ -584,10 +561,12 @@ class NginxConfigurator(common.Installer): raise def _has_certbot_redirect(self, vhost): - return vhost.contains_list(TEST_REDIRECT_BLOCK) + test_redirect_block = _test_block_from_block(REDIRECT_BLOCK) + return vhost.contains_list(test_redirect_block) def _has_certbot_redirect_comment(self, vhost): - return vhost.contains_list(TEST_REDIRECT_COMMENT_BLOCK) + test_redirect_comment_block = _test_block_from_block(REDIRECT_COMMENT_BLOCK) + return vhost.contains_list(test_redirect_comment_block) def _add_redirect_block(self, vhost, active=True): """Add redirect directive to vhost @@ -603,7 +582,8 @@ class NginxConfigurator(common.Installer): def _enable_redirect(self, domain, unused_options): """Redirect all equivalent HTTP traffic to ssl_vhost. - Add rewrite directive to non https traffic + If the vhost is listening plaintextishly, separate out the + relevant directives into a new server block and add a rewrite directive. .. note:: This function saves the configuration @@ -616,26 +596,46 @@ class NginxConfigurator(common.Installer): vhost = None # If there are blocks listening plaintextishly on self.DEFAULT_LISTEN_PORT, # choose the most name-matching one. + vhost = self.choose_redirect_vhost(domain, port) if vhost is None: logger.info("No matching insecure server blocks listening on port %s found.", self.DEFAULT_LISTEN_PORT) + return + + if vhost.ssl: + new_vhost = self.parser.duplicate_vhost(vhost, + only_directives=['listen', 'server_name']) + + def _ssl_match_func(directive): + return 'ssl' in directive + + def _no_ssl_match_func(directive): + return 'ssl' not in directive + + # remove all ssl addresses from the new block + self.parser.remove_server_directives(new_vhost, 'listen', match_func=_ssl_match_func) + + # remove all non-ssl addresses from the existing block + self.parser.remove_server_directives(vhost, 'listen', match_func=_no_ssl_match_func) + + vhost = new_vhost + + if self._has_certbot_redirect(vhost): + logger.info("Traffic on port %s already redirecting to ssl in %s", + self.DEFAULT_LISTEN_PORT, vhost.filep) + elif vhost.has_redirect(): + if not self._has_certbot_redirect_comment(vhost): + self._add_redirect_block(vhost, active=False) + logger.info("The appropriate server block is already redirecting " + "traffic. To enable redirect anyway, uncomment the " + "redirect lines in %s.", vhost.filep) else: - if self._has_certbot_redirect(vhost): - logger.info("Traffic on port %s already redirecting to ssl in %s", - self.DEFAULT_LISTEN_PORT, vhost.filep) - elif vhost.has_redirect(): - if not self._has_certbot_redirect_comment(vhost): - self._add_redirect_block(vhost, active=False) - logger.info("The appropriate server block is already redirecting " - "traffic. To enable redirect anyway, uncomment the " - "redirect lines in %s.", vhost.filep) - else: - # Redirect plaintextish host to https - self._add_redirect_block(vhost, active=True) - logger.info("Redirecting all traffic on port %s to ssl in %s", - self.DEFAULT_LISTEN_PORT, vhost.filep) + # Redirect plaintextish host to https + self._add_redirect_block(vhost, active=True) + logger.info("Redirecting all traffic on port %s to ssl in %s", + self.DEFAULT_LISTEN_PORT, vhost.filep) def _enable_ocsp_stapling(self, domain, chain_path): """Include OCSP response in TLS handshake @@ -809,6 +809,7 @@ class NginxConfigurator(common.Installer): """ super(NginxConfigurator, self).recovery_routine() + self.new_vhost = None self.parser.load() def revert_challenge_config(self): @@ -818,6 +819,7 @@ class NginxConfigurator(common.Installer): """ self.revert_temporary_config() + self.new_vhost = None self.parser.load() def rollback_checkpoints(self, rollback=1): @@ -830,6 +832,7 @@ class NginxConfigurator(common.Installer): """ super(NginxConfigurator, self).rollback_checkpoints(rollback) + self.new_vhost = None self.parser.load() ########################################################################### @@ -882,6 +885,11 @@ class NginxConfigurator(common.Installer): self.restart() +def _test_block_from_block(block): + test_block = nginxparser.UnspacedList(block) + parser.comment_directive(test_block, 0) + return test_block[:-1] + def nginx_restart(nginx_ctl, nginx_conf): """Restarts the Nginx Server. diff --git a/certbot-nginx/certbot_nginx/obj.py b/certbot-nginx/certbot_nginx/obj.py index 5816c5571..f5ac5c2e3 100644 --- a/certbot-nginx/certbot_nginx/obj.py +++ b/certbot-nginx/certbot_nginx/obj.py @@ -205,7 +205,7 @@ class VirtualHost(object): # pylint: disable=too-few-public-methods def contains_list(self, test): """Determine if raw server block contains test list at top level """ - for i in six.moves.range(0, len(self.raw) - len(test)): + for i in six.moves.range(0, len(self.raw) - len(test) + 1): if self.raw[i:i + len(test)] == test: return True return False @@ -220,6 +220,8 @@ class VirtualHost(object): # pylint: disable=too-few-public-methods def ipv4_enabled(self): """Return true if one or more of the listen directives in vhost are IPv4 only""" + if self.addrs is None or len(self.addrs) == 0: + return True for a in self.addrs: if not a.ipv6: return True diff --git a/certbot-nginx/certbot_nginx/parser.py b/certbot-nginx/certbot_nginx/parser.py index 3eb6264aa..9f13bc59f 100644 --- a/certbot-nginx/certbot_nginx/parser.py +++ b/certbot-nginx/certbot_nginx/parser.py @@ -1,5 +1,6 @@ """NginxParser is a member object of the NginxConfigurator class.""" import copy +import functools import glob import logging import os @@ -294,6 +295,30 @@ class NginxParser(object): :param bool replace: Whether to only replace existing directives """ + self._modify_server_directives(vhost, + functools.partial(_add_directives, directives, replace)) + + def remove_server_directives(self, vhost, directive_name, match_func=None): + """Remove all directives of type directive_name. + + :param :class:`~certbot_nginx.obj.VirtualHost` vhost: The vhost + to remove directives from + :param string directive_name: The directive type to remove + :param callable match_func: Function of the directive that returns true for directives + to be deleted. + """ + self._modify_server_directives(vhost, + functools.partial(_remove_directives, directive_name, match_func)) + + def _update_vhost_based_on_new_directives(self, vhost, directives_list): + new_server = self._get_included_directives(directives_list) + parsed_server = self.parse_server(new_server) + vhost.addrs = parsed_server['addrs'] + vhost.ssl = parsed_server['ssl'] + vhost.names = parsed_server['names'] + vhost.raw = new_server + + def _modify_server_directives(self, vhost, block_func): filename = vhost.filep try: result = self.parsed[filename] @@ -302,42 +327,52 @@ class NginxParser(object): if not isinstance(result, list) or len(result) != 2: raise errors.MisconfigurationError("Not a server block.") result = result[1] - _add_directives(result, directives, replace) + block_func(result) - # update vhost based on new directives - new_server = self._get_included_directives(result) - parsed_server = self.parse_server(new_server) - vhost.addrs = parsed_server['addrs'] - vhost.ssl = parsed_server['ssl'] - vhost.names = parsed_server['names'] - vhost.raw = new_server + self._update_vhost_based_on_new_directives(vhost, result) except errors.MisconfigurationError as err: raise errors.MisconfigurationError("Problem in %s: %s" % (filename, str(err))) - def create_new_vhost_from_default(self, vhost_template): - """Duplicate the default vhost in the configuration files. + def duplicate_vhost(self, vhost_template, delete_default=False, only_directives=None): + """Duplicate the vhost in the configuration files. :param :class:`~certbot_nginx.obj.VirtualHost` vhost_template: The vhost whose information we copy + :param bool delete_default: If we should remove default_server + from listen directives in the block. + :param list only_directives: If it exists, only duplicate the named directives. Only + looks at first level of depth; does not expand includes. :returns: A vhost object for the newly created vhost :rtype: :class:`~certbot_nginx.obj.VirtualHost` """ # TODO: https://github.com/certbot/certbot/issues/5185 # put it in the same file as the template, at the same level + new_vhost = copy.deepcopy(vhost_template) + enclosing_block = self.parsed[vhost_template.filep] for index in vhost_template.path[:-1]: enclosing_block = enclosing_block[index] - new_location = vhost_template.path[-1] + 1 raw_in_parsed = copy.deepcopy(enclosing_block[vhost_template.path[-1]]) - enclosing_block.insert(new_location, raw_in_parsed) - new_vhost = copy.deepcopy(vhost_template) - new_vhost.path[-1] = new_location - for addr in new_vhost.addrs: - addr.default = False - for directive in enclosing_block[new_vhost.path[-1]][1]: - if len(directive) > 0 and directive[0] == 'listen' and 'default_server' in directive: - del directive[directive.index('default_server')] + + if only_directives is not None: + new_directives = nginxparser.UnspacedList([]) + for directive in raw_in_parsed[1]: + if len(directive) > 0 and directive[0] in only_directives: + new_directives.append(directive) + raw_in_parsed[1] = new_directives + + self._update_vhost_based_on_new_directives(new_vhost, new_directives) + + enclosing_block.append(raw_in_parsed) + new_vhost.path[-1] = len(enclosing_block) - 1 + if delete_default: + for addr in new_vhost.addrs: + addr.default = False + for directive in enclosing_block[new_vhost.path[-1]][1]: + if (len(directive) > 0 and directive[0] == 'listen' + and 'default_server' in directive): + del directive[directive.index('default_server')] return new_vhost def _parse_ssl_options(ssl_options): @@ -486,7 +521,7 @@ def _is_ssl_on_directive(entry): len(entry) == 2 and entry[0] == 'ssl' and entry[1] == 'on') -def _add_directives(block, directives, replace): +def _add_directives(directives, replace, block): """Adds or replaces directives in a config block. When replace=False, it's an error to try and add a directive that already @@ -498,8 +533,9 @@ def _add_directives(block, directives, replace): ..todo :: Find directives that are in included files. - :param list block: The block to replace in :param list directives: The new directives. + :param bool replace: Described above. + :param list block: The block to replace in """ for directive in directives: @@ -513,8 +549,12 @@ REPEATABLE_DIRECTIVES = set(['server_name', 'listen', INCLUDE]) COMMENT = ' managed by Certbot' COMMENT_BLOCK = [' ', '#', COMMENT] -def _comment_directive(block, location): - """Add a comment to the end of the line at location.""" +def comment_directive(block, location): + """Add a ``#managed by Certbot`` comment to the end of the line at location. + + :param list block: The block containing the directive to be commented + :param int location: The location within ``block`` of the directive to be commented + """ next_entry = block[location + 1] if location + 1 < len(block) else None if isinstance(next_entry, list) and next_entry: if len(next_entry) >= 2 and next_entry[-2] == "#" and COMMENT in next_entry[-1]: @@ -551,6 +591,12 @@ def _comment_out_directive(block, location, include_location): block[location] = new_dir[0] # set the now-single-line-comment directive back in place +def _find_location(block, directive_name, match_func=None): + """Finds the index of the first instance of directive_name in block. + If no line exists, use None.""" + return next((index for index, line in enumerate(block) \ + if line and line[0] == directive_name and (match_func is None or match_func(line))), None) + def _add_directive(block, directive, replace): """Adds or replaces a single directive in a config block. @@ -566,19 +612,12 @@ def _add_directive(block, directive, replace): block.append(directive) return - def find_location(direc): - """ Find the index of a config line where the name of the directive matches - the name of the directive we want to add. If no line exists, use None. - """ - return next((index for index, line in enumerate(block) \ - if line and line[0] == direc[0]), None) - - location = find_location(directive) + location = _find_location(block, directive[0]) if replace: if location is not None: block[location] = directive - _comment_directive(block, location) + comment_directive(block, location) return # Append directive. Fail if the name is not a repeatable directive name, # and there is already a copy of that directive with a different value @@ -602,7 +641,7 @@ def _add_directive(block, directive, replace): included_directives = _parse_ssl_options(directive[1]) for included_directive in included_directives: - included_dir_loc = find_location(included_directive) + included_dir_loc = _find_location(block, included_directive[0]) included_dir_name = included_directive[0] if not is_whitespace_or_comment(included_directive) \ and not can_append(included_dir_loc, included_dir_name): @@ -614,10 +653,19 @@ def _add_directive(block, directive, replace): if can_append(location, directive_name): block.append(directive) - _comment_directive(block, len(block) - 1) + comment_directive(block, len(block) - 1) elif block[location] != directive: raise errors.MisconfigurationError(err_fmt.format(directive, block[location])) +def _remove_directives(directive_name, match_func, block): + """Removes directives of name directive_name from a config block if match_func matches. + """ + while True: + location = _find_location(block, directive_name, match_func=match_func) + if location is None: + return + del block[location] + def _apply_global_addr_ssl(addr_to_ssl, parsed_server): """Apply global sslishness information to the parsed server block """ diff --git a/certbot-nginx/certbot_nginx/tests/configurator_test.py b/certbot-nginx/certbot_nginx/tests/configurator_test.py index 996bd238b..e708b159a 100644 --- a/certbot-nginx/certbot_nginx/tests/configurator_test.py +++ b/certbot-nginx/certbot_nginx/tests/configurator_test.py @@ -443,10 +443,7 @@ class NginxConfiguratorTest(util.NginxTest): def test_redirect_enhance(self): # Test that we successfully add a redirect when there is # a listen directive - expected = [ - ['if', '($scheme', '!=', '"https")'], - [['return', '301', 'https://$host$request_uri']] - ] + expected = ['return', '301', 'https://$host$request_uri'] example_conf = self.config.parser.abs_path('sites-enabled/example.com') self.config.enhance("www.example.com", "redirect") @@ -462,6 +459,35 @@ class NginxConfiguratorTest(util.NginxTest): generated_conf = self.config.parser.parsed[migration_conf] self.assertTrue(util.contains_at_depth(generated_conf, expected, 2)) + def test_split_for_redirect(self): + example_conf = self.config.parser.abs_path('sites-enabled/example.com') + self.config.deploy_cert( + "example.org", + "example/cert.pem", + "example/key.pem", + "example/chain.pem", + "example/fullchain.pem") + self.config.enhance("www.example.com", "redirect") + generated_conf = self.config.parser.parsed[example_conf] + self.assertEqual( + [[['server'], [ + ['server_name', '.example.com'], + ['server_name', 'example.*'], [], + ['listen', '5001', 'ssl'], ['#', ' managed by Certbot'], + ['ssl_certificate', 'example/fullchain.pem'], ['#', ' managed by Certbot'], + ['ssl_certificate_key', 'example/key.pem'], ['#', ' managed by Certbot'], + ['include', self.config.mod_ssl_conf], ['#', ' managed by Certbot'], + ['ssl_dhparam', self.config.ssl_dhparams], ['#', ' managed by Certbot'], + [], []]], + [['server'], [ + ['listen', '69.50.225.155:9000'], + ['listen', '127.0.0.1'], + ['server_name', '.example.com'], + ['server_name', 'example.*'], + ['return', '301', 'https://$host$request_uri'], ['#', ' managed by Certbot'], + [], []]]], + generated_conf) + @mock.patch('certbot_nginx.obj.VirtualHost.contains_list') @mock.patch('certbot_nginx.obj.VirtualHost.has_redirect') def test_certbot_redirect_exists(self, mock_has_redirect, mock_contains_list): @@ -494,9 +520,38 @@ class NginxConfiguratorTest(util.NginxTest): generated_conf = self.config.parser.parsed[example_conf] expected = [ ['#', ' Redirect non-https traffic to https'], - ['#', ' if ($scheme != "https") {'], - ['#', ' return 301 https://$host$request_uri;'], - ['#', ' } # managed by Certbot'] + ['#', ' return 301 https://$host$request_uri;'], + ] + for line in expected: + self.assertTrue(util.contains_at_depth(generated_conf, line, 2)) + + @mock.patch('certbot_nginx.obj.VirtualHost.contains_list') + @mock.patch('certbot_nginx.obj.VirtualHost.has_redirect') + def test_non_certbot_redirect_exists_has_ssl_copy(self, mock_has_redirect, mock_contains_list): + # Test that we add a redirect as a comment if there is already a + # redirect-class statement in the block that isn't managed by certbot + example_conf = self.config.parser.abs_path('sites-enabled/example.com') + + self.config.deploy_cert( + "example.org", + "example/cert.pem", + "example/key.pem", + "example/chain.pem", + "example/fullchain.pem") + + # Has a non-Certbot redirect, and has no existing comment + mock_contains_list.return_value = False + mock_has_redirect.return_value = True + with mock.patch("certbot_nginx.configurator.logger") as mock_logger: + self.config.enhance("www.example.com", "redirect") + self.assertEqual(mock_logger.info.call_args[0][0], + "The appropriate server block is already redirecting " + "traffic. To enable redirect anyway, uncomment the " + "redirect lines in %s.") + generated_conf = self.config.parser.parsed[example_conf] + expected = [ + ['#', ' Redirect non-https traffic to https'], + ['#', ' return 301 https://$host$request_uri;'], ] for line in expected: self.assertTrue(util.contains_at_depth(generated_conf, line, 2)) @@ -704,14 +759,18 @@ class NginxConfiguratorTest(util.NginxTest): self.config.parser.load() - expected = [ - ['if', '($scheme', '!=', '"https")'], - [['return', '301', 'https://$host$request_uri']] - ] + expected = ['return', '301', 'https://$host$request_uri'] generated_conf = self.config.parser.parsed[default_conf] self.assertTrue(util.contains_at_depth(generated_conf, expected, 2)) + @mock.patch('certbot.reverter.logger') + @mock.patch('certbot_nginx.parser.NginxParser.load') + def test_parser_reload_after_config_changes(self, mock_parser_load, unused_mock_logger): + self.config.recovery_routine() + self.config.revert_challenge_config() + self.config.rollback_checkpoints() + self.assertTrue(mock_parser_load.call_count == 3) class InstallSslOptionsConfTest(util.NginxTest): """Test that the options-ssl-nginx.conf file is installed and updated properly.""" diff --git a/certbot-nginx/certbot_nginx/tests/obj_test.py b/certbot-nginx/certbot_nginx/tests/obj_test.py index ba136bb78..92cb0e086 100644 --- a/certbot-nginx/certbot_nginx/tests/obj_test.py +++ b/certbot-nginx/certbot_nginx/tests/obj_test.py @@ -171,8 +171,8 @@ class VirtualHostTest(unittest.TestCase): def test_contains_list(self): from certbot_nginx.obj import VirtualHost from certbot_nginx.obj import Addr - from certbot_nginx.configurator import TEST_REDIRECT_BLOCK - test_needle = TEST_REDIRECT_BLOCK + from certbot_nginx.configurator import REDIRECT_BLOCK, _test_block_from_block + test_needle = _test_block_from_block(REDIRECT_BLOCK) test_haystack = [['listen', '80'], ['root', '/var/www/html'], ['index', 'index.html index.htm index.nginx-debian.html'], ['server_name', 'two.functorkitten.xyz'], ['listen', '443 ssl'], @@ -181,9 +181,7 @@ class VirtualHostTest(unittest.TestCase): ['#', ' managed by Certbot'], ['ssl_certificate_key', '/etc/letsencrypt/live/two.functorkitten.xyz/privkey.pem'], ['#', ' managed by Certbot'], - [['if', '($scheme', '!=', '"https")'], - [['return', '301', 'https://$host$request_uri']] - ], + ['return', '301', 'https://$host$request_uri'], ['#', ' managed by Certbot'], []] vhost_haystack = VirtualHost( "filp", diff --git a/certbot-nginx/certbot_nginx/tests/parser_test.py b/certbot-nginx/certbot_nginx/tests/parser_test.py index ca5de7ff6..e21acb8ea 100644 --- a/certbot-nginx/certbot_nginx/tests/parser_test.py +++ b/certbot-nginx/certbot_nginx/tests/parser_test.py @@ -334,9 +334,9 @@ class NginxParserTest(util.NginxTest): #pylint: disable=too-many-public-methods ["\n", "a", " ", "b", "\n"], ["c", " ", "d"], ["\n", "e", " ", "f"]]) - from certbot_nginx.parser import _comment_directive, COMMENT_BLOCK - _comment_directive(block, 1) - _comment_directive(block, 0) + from certbot_nginx.parser import comment_directive, COMMENT_BLOCK + comment_directive(block, 1) + comment_directive(block, 0) self.assertEqual(block.spaced, [ ["\n", "a", " ", "b", "\n"], COMMENT_BLOCK, @@ -406,12 +406,12 @@ class NginxParserTest(util.NginxTest): #pylint: disable=too-many-public-methods ]) self.assertTrue(server['ssl']) - def test_create_new_vhost_from_default(self): + def test_duplicate_vhost(self): nparser = parser.NginxParser(self.config_path) vhosts = nparser.get_vhosts() default = [x for x in vhosts if 'default' in x.filep][0] - new_vhost = nparser.create_new_vhost_from_default(default) + new_vhost = nparser.duplicate_vhost(default, delete_default=True) nparser.filedump(ext='') # check properties of new vhost diff --git a/certbot-nginx/certbot_nginx/tls_sni_01.py b/certbot-nginx/certbot_nginx/tls_sni_01.py index 7f597ac4a..eca198bfe 100644 --- a/certbot-nginx/certbot_nginx/tls_sni_01.py +++ b/certbot-nginx/certbot_nginx/tls_sni_01.py @@ -55,7 +55,7 @@ class NginxTlsSni01(common.TLSSNI01): self.configurator.config.tls_sni_01_port) for achall in self.achalls: - vhost = self.configurator.choose_vhost(achall.domain, raise_if_no_match=False) + vhost = self.configurator.choose_vhost(achall.domain, create_if_no_match=True) if vhost is not None and vhost.addrs: addresses.append(list(vhost.addrs)) diff --git a/certbot-nginx/setup.py b/certbot-nginx/setup.py index f3919413d..2ad7aaf08 100644 --- a/certbot-nginx/setup.py +++ b/certbot-nginx/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.20.0.dev0' +version = '0.21.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/certbot/__init__.py b/certbot/__init__.py index 231a0f5f5..cbea701ee 100644 --- a/certbot/__init__.py +++ b/certbot/__init__.py @@ -1,4 +1,4 @@ """Certbot client.""" # version number like 1.2.3a0, must have at least 2 parts, like 1.2 -__version__ = '0.20.0.dev0' +__version__ = '0.21.0.dev0' diff --git a/certbot/main.py b/certbot/main.py index 9e2850891..72af7fbba 100644 --- a/certbot/main.py +++ b/certbot/main.py @@ -43,7 +43,15 @@ logger = logging.getLogger(__name__) def _suggest_donation_if_appropriate(config): - """Potentially suggest a donation to support Certbot.""" + """Potentially suggest a donation to support Certbot. + + :param config: Configuration object + :type config: interfaces.IConfig + + :returns: `None` + :rtype: None + + """ assert config.verb != "renew" if config.staging: # --dry-run implies --staging @@ -55,6 +63,15 @@ def _suggest_donation_if_appropriate(config): reporter_util.add_message(msg, reporter_util.LOW_PRIORITY) def _report_successful_dry_run(config): + """Reports on successful dry run + + :param config: Configuration object + :type config: interfaces.IConfig + + :returns: `None` + :rtype: None + + """ reporter_util = zope.component.getUtility(interfaces.IReporter) assert config.verb != "renew" reporter_util.add_message("The dry run was successful.", @@ -68,8 +85,23 @@ def _get_and_save_cert(le_client, config, domains=None, certname=None, lineage=N then performs that action. Includes calls to hooks, various reports, checks, and requests for user input. + :param config: Configuration object + :type config: interfaces.IConfig + + :param domains: List of domain names to get a certificate. Defaults to `None` + :type domains: `list` of `str` + + :param certname: Name of new certificate. Defaults to `None` + :type certname: str + + :param lineage: Certificate lineage object. Defaults to `None` + :type lineage: storage.RenewableCert + :returns: the issued certificate or `None` if doing a dry run - :rtype: `storage.RenewableCert` or `None` + :rtype: storage.RenewableCert or None + + :raises errors.Error: if certificate could not be obtained + """ hooks.pre_hook(config) try: @@ -96,11 +128,18 @@ def _get_and_save_cert(le_client, config, domains=None, certname=None, lineage=N def _handle_subset_cert_request(config, domains, cert): """Figure out what to do if a previous cert had a subset of the names now requested - :param storage.RenewableCert cert: + :param config: Configuration object + :type config: interfaces.IConfig + + :param domains: List of domain names + :type domains: `list` of `str` + + :param cert: Certificate object + :type cert: storage.RenewableCert :returns: Tuple of (str action, cert_or_None) as per _find_lineage_for_domains_and_certname action can be: "newcert" | "renew" | "reinstall" - :rtype: tuple + :rtype: `tuple` of `str` """ existing = ", ".join(cert.names()) @@ -137,11 +176,15 @@ def _handle_subset_cert_request(config, domains, cert): def _handle_identical_cert_request(config, lineage): """Figure out what to do if a lineage has the same names as a previously obtained one - :param storage.RenewableCert lineage: + :param config: Configuration object + :type config: interfaces.IConfig + + :param lineage: Certificate lineage object + :type lineage: storage.RenewableCert :returns: Tuple of (str action, cert_or_None) as per _find_lineage_for_domains_and_certname action can be: "newcert" | "renew" | "reinstall" - :rtype: tuple + :rtype: `tuple` of `str` """ if not lineage.ensure_deployed(): @@ -186,11 +229,18 @@ def _find_lineage_for_domains(config, domains): the client run if the user chooses to cancel the operation when prompted). + :param config: Configuration object + :type config: interfaces.IConfig + + :param domains: List of domain names + :type domains: `list` of `str` + :returns: Two-element tuple containing desired new-certificate behavior as a string token ("reinstall", "renew", or "newcert"), plus either - a RenewableCert instance or None if renewal shouldn't occur. + a RenewableCert instance or `None` if renewal shouldn't occur. + :rtype: `tuple` of `str` and :class:`storage.RenewableCert` or `None` - :raises .Error: If the user would like to rerun the client again. + :raises errors.Error: If the user would like to rerun the client again. """ # Considering the possibility that the requested certificate is @@ -214,9 +264,20 @@ def _find_lineage_for_domains(config, domains): def _find_cert(config, domains, certname): """Finds an existing certificate object given domains and/or a certificate name. + :param config: Configuration object + :type config: interfaces.IConfig + + :param domains: List of domain names + :type domains: `list` of `str` + + :param certname: Name of certificate + :type certname: str + :returns: Two-element tuple of a boolean that indicates if this function should be followed by a call to fetch a certificate from the server, and either a RenewableCert instance or None. + :rtype: `tuple` of `bool` and :class:`storage.RenewableCert` or `None` + """ action, lineage = _find_lineage_for_domains_and_certname(config, domains, certname) if action == "reinstall": @@ -226,11 +287,22 @@ def _find_cert(config, domains, certname): def _find_lineage_for_domains_and_certname(config, domains, certname): """Find appropriate lineage based on given domains and/or certname. + :param config: Configuration object + :type config: interfaces.IConfig + + :param domains: List of domain names + :type domains: `list` of `str` + + :param certname: Name of certificate + :type certname: str + :returns: Two-element tuple containing desired new-certificate behavior as a string token ("reinstall", "renew", or "newcert"), plus either - a RenewableCert instance or None if renewal shouldn't occur. + a RenewableCert instance or None if renewal should not occur. - :raises .Error: If the user would like to rerun the client again. + :rtype: `tuple` of `str` and :class:`storage.RenewableCert` or `None` + + :raises errors.Error: If the user would like to rerun the client again. """ if not certname: @@ -253,18 +325,57 @@ def _find_lineage_for_domains_and_certname(config, domains, certname): "Use -d to specify domains, or run certbot --certificates to see " "possible certificate names.".format(certname)) +def _get_added_removed(after, before): + """Get lists of items removed from `before` + and a lists of items added to `after` + """ + added = list(set(after) - set(before)) + removed = list(set(before) - set(after)) + added.sort() + removed.sort() + return added, removed + +def _format_list(character, strings): + """Format list with given character + """ + formatted = "{br}{ch} " + "{br}{ch} ".join(strings) + return formatted.format( + ch=character, + br=os.linesep + ) + def _ask_user_to_confirm_new_names(config, new_domains, certname, old_domains): """Ask user to confirm update cert certname to contain new_domains. + + :param config: Configuration object + :type config: interfaces.IConfig + + :param new_domains: List of new domain names + :type new_domains: `list` of `str` + + :param certname: Name of certificate + :type certname: str + + :param old_domains: List of old domain names + :type old_domains: `list` of `str` + + :returns: None + :rtype: None + + :raises errors.ConfigurationError: if cert name and domains mismatch + """ if config.renew_with_new_domains: return - msg = ("You are updating certificate {0} to include domains: {1}{br}{br}" - "It previously included domains: {2}{br}{br}" + added, removed = _get_added_removed(new_domains, old_domains) + + msg = ("You are updating certificate {0} to include new domain(s): {1}{br}{br}" + "You are also removing previously included domain(s): {2}{br}{br}" "Did you intend to make this change?".format( certname, - ", ".join(new_domains), - ", ".join(old_domains), + _format_list("+", added), + _format_list("-", removed), br=os.linesep)) obj = zope.component.getUtility(interfaces.IDisplay) if not obj.yesno(msg, "Update cert", "Cancel", default=True): @@ -272,6 +383,19 @@ def _ask_user_to_confirm_new_names(config, new_domains, certname, old_domains): def _find_domains_or_certname(config, installer): """Retrieve domains and certname from config or user input. + + :param config: Configuration object + :type config: interfaces.IConfig + + :param installer: Installer object + :type installer: interfaces.IInstaller + + + :returns: Two-part tuple of domains and certname + :rtype: `tuple` of list of `str` and `str` + + :raises errors.Error: Usage message, if parameters are not used correctly + """ domains = None certname = config.certname @@ -299,9 +423,17 @@ def _find_domains_or_certname(config, installer): def _report_new_cert(config, cert_path, fullchain_path, key_path=None): """Reports the creation of a new certificate to the user. - :param str cert_path: path to cert - :param str fullchain_path: path to full chain - :param str key_path: path to private key, if available + :param cert_path: path to certificate + :type cert_path: str + + :param fullchain_path: path to full chain + :type fullchain_path: str + + :param key_path: path to private key, if available + :type key_path: str + + :returns: `None` + :rtype: None """ if config.dry_run: @@ -337,14 +469,14 @@ def _determine_account(config): if ``config.account`` is ``None``, it will be updated based on the user input. Same for ``config.email``. - :param argparse.Namespace config: CLI arguments - :param certbot.interface.IConfig config: Configuration object - :param .AccountStorage account_storage: Account storage. + :param config: Configuration object + :type config: interfaces.IConfig :returns: Account and optionally ACME client API (biproduct of new registration). - :rtype: `tuple` of `certbot.account.Account` and - `acme.client.Client` + :rtype: tuple of :class:`certbot.account.Account` and :class:`acme.client.Client` + + :raises errors.Error: If unable to register an account with ACME server """ account_storage = account.AccountFileStorage(config) @@ -392,9 +524,13 @@ def _delete_if_appropriate(config): # pylint: disable=too-many-locals,too-many-b deleting happens automatically, unless if both `--cert-name` and `--cert-path` were specified with conflicting values. - :param `configuration.NamespaceConfig` config: parsed command line arguments + :param config: parsed command line arguments + :type config: interfaces.IConfig - :raises `error.Errors`: If anything goes wrong, including bad user input, if an overlapping + :returns: `None` + :rtype: None + + :raises errors.Error: If anything goes wrong, including bad user input, if an overlapping archive dir is found for the specified lineage, etc ... """ display = zope.component.getUtility(interfaces.IDisplay) @@ -474,6 +610,20 @@ def _delete_if_appropriate(config): # pylint: disable=too-many-locals,too-many-b def _init_le_client(config, authenticator, installer): + """Initialize Let's Encrypt Client + + :param config: Configuration object + :type config: interfaces.IConfig + + :param authenticator: Acme authentication handler + :type authenticator: interfaces.IAuthenticator + :param installer: Installer object + :type installer: interfaces.IInstaller + + :returns: client: Client object + :rtype: client.Client + + """ if authenticator is not None: # if authenticator was given, then we will need account... acc, acme = _determine_account(config) @@ -487,7 +637,18 @@ def _init_le_client(config, authenticator, installer): def unregister(config, unused_plugins): - """Deactivate account on server""" + """Deactivate account on server + + :param config: Configuration object + :type config: interfaces.IConfig + + :param unused_plugins: List of plugins (deprecated) + :type unused_plugins: `list` of `str` + + :returns: `None` + :rtype: None + + """ account_storage = account.AccountFileStorage(config) accounts = account_storage.find_all() reporter_util = zope.component.getUtility(interfaces.IReporter) @@ -516,8 +677,18 @@ def unregister(config, unused_plugins): def register(config, unused_plugins): - """Create or modify accounts on the server.""" + """Create or modify accounts on the server. + :param config: Configuration object + :type config: interfaces.IConfig + + :param unused_plugins: List of plugins (deprecated) + :type unused_plugins: `list` of `str` + + :returns: `None` or a string indicating and error + :rtype: None or str + + """ # Portion of _determine_account logic to see whether accounts already # exist or not. account_storage = account.AccountFileStorage(config) @@ -558,6 +729,24 @@ def register(config, unused_plugins): add_msg("Your e-mail address was updated to {0}.".format(config.email)) def _install_cert(config, le_client, domains, lineage=None): + """Install a cert + + :param config: Configuration object + :type config: interfaces.IConfig + + :param le_client: Client object + :type le_client: client.Client + + :param plugins: List of domains + :type plugins: `list` of `str` + + :param lineage: Certificate lineage object. Defaults to `None` + :type lineage: storage.RenewableCert + + :returns: `None` + :rtype: None + + """ path_provider = lineage if lineage else config assert path_provider.cert_path is not None @@ -566,7 +755,18 @@ def _install_cert(config, le_client, domains, lineage=None): le_client.enhance_config(domains, path_provider.chain_path) def install(config, plugins): - """Install a previously obtained cert in a server.""" + """Install a previously obtained cert in a server. + + :param config: Configuration object + :type config: interfaces.IConfig + + :param plugins: List of plugins + :type plugins: `list` of `str` + + :returns: `None` + :rtype: None + + """ # XXX: Update for renewer/RenewableCert # FIXME: be consistent about whether errors are raised or returned from # this function ... @@ -582,7 +782,18 @@ def install(config, plugins): def plugins_cmd(config, plugins): - """List server software plugins.""" + """List server software plugins. + + :param config: Configuration object + :type config: interfaces.IConfig + + :param plugins: List of plugins + :type plugins: `list` of `str` + + :returns: `None` + :rtype: None + + """ logger.debug("Expected interfaces: %s", config.ifaces) ifaces = [] if config.ifaces is None else config.ifaces @@ -610,7 +821,18 @@ def plugins_cmd(config, plugins): def rollback(config, plugins): - """Rollback server configuration changes made during install.""" + """Rollback server configuration changes made during install. + + :param config: Configuration object + :type config: interfaces.IConfig + + :param plugins: List of plugins + :type plugins: `list` of `str` + + :returns: `None` + :rtype: None + + """ client.rollback(config.installer, config.checkpoints, config, plugins) @@ -619,6 +841,15 @@ def config_changes(config, unused_plugins): View checkpoints and associated configuration changes. + :param config: Configuration object + :type config: interfaces.IConfig + + :param unused_plugins: List of plugins (deprecated) + :type unused_plugins: `list` of `str` + + :returns: `None` + :rtype: None + """ client.view_config_changes(config, num=config.num) @@ -627,6 +858,16 @@ def update_symlinks(config, unused_plugins): Use the information in the config file to make symlinks point to the correct archive directory. + + :param config: Configuration object + :type config: interfaces.IConfig + + :param unused_plugins: List of plugins (deprecated) + :type unused_plugins: `list` of `str` + + :returns: `None` + :rtype: None + """ cert_manager.update_live_symlinks(config) @@ -635,6 +876,16 @@ def rename(config, unused_plugins): Use the information in the config file to rename an existing lineage. + + :param config: Configuration object + :type config: interfaces.IConfig + + :param unused_plugins: List of plugins (deprecated) + :type unused_plugins: `list` of `str` + + :returns: `None` + :rtype: None + """ cert_manager.rename_lineage(config) @@ -643,16 +894,47 @@ def delete(config, unused_plugins): Use the information in the config file to delete an existing lineage. + + :param config: Configuration object + :type config: interfaces.IConfig + + :param unused_plugins: List of plugins (deprecated) + :type unused_plugins: `list` of `str` + + :returns: `None` + :rtype: None + """ cert_manager.delete(config) def certificates(config, unused_plugins): """Display information about certs configured with Certbot + + :param config: Configuration object + :type config: interfaces.IConfig + + :param unused_plugins: List of plugins (deprecated) + :type unused_plugins: `list` of `str` + + :returns: `None` + :rtype: None + """ cert_manager.certificates(config) def revoke(config, unused_plugins): # TODO: coop with renewal config - """Revoke a previously obtained certificate.""" + """Revoke a previously obtained certificate. + + :param config: Configuration object + :type config: interfaces.IConfig + + :param unused_plugins: List of plugins (deprecated) + :type unused_plugins: `list` of `str` + + :returns: `None` or string indicating error in case of error + :rtype: None or str + + """ # For user-agent construction config.installer = config.authenticator = "None" if config.key_path is not None: # revocation by cert key @@ -678,7 +960,18 @@ def revoke(config, unused_plugins): # TODO: coop with renewal config def run(config, plugins): # pylint: disable=too-many-branches,too-many-locals - """Obtain a certificate and install.""" + """Obtain a certificate and install. + + :param config: Configuration object + :type config: interfaces.IConfig + + :param plugins: List of plugins + :type plugins: `list` of `str` + + :returns: `None` + :rtype: None + + """ # TODO: Make run as close to auth + install as possible # Possible difficulties: config.csr was hacked into auth try: @@ -718,6 +1011,16 @@ def _csr_get_and_save_cert(config, le_client): This works differently in the CSR case (for now) because we don't have the privkey, and therefore can't construct the files for a lineage. So we just save the cert & chain to disk :/ + + :param config: Configuration object + :type config: interfaces.IConfig + + :param client: Client object + :type client: client.Client + + :returns: `cert_path` and `fullchain_path` as absolute paths to the actual files + :rtype: `tuple` of `str` + """ csr, _ = config.actual_csr certr, chain = le_client.obtain_certificate_from_csr(config.domains, csr) @@ -730,7 +1033,23 @@ def _csr_get_and_save_cert(config, le_client): return cert_path, fullchain_path def renew_cert(config, plugins, lineage): - """Renew & save an existing cert. Do not install it.""" + """Renew & save an existing cert. Do not install it. + + :param config: Configuration object + :type config: interfaces.IConfig + + :param plugins: List of plugins + :type plugins: `list` of `str` + + :param lineage: Certificate lineage object + :type lineage: storage.RenewableCert + + :returns: `None` + :rtype: None + + :raises errors.PluginSelectionError: MissingCommandlineFlag if supplied parameters do not pass + + """ try: # installers are used in auth mode to determine domain names installer, auth = plug_sel.choose_configurator_plugins(config, plugins, "certonly") @@ -757,8 +1076,20 @@ def renew_cert(config, plugins, lineage): def certonly(config, plugins): """Authenticate & obtain cert, but do not install it. - This implements the 'certonly' subcommand.""" + This implements the 'certonly' subcommand. + :param config: Configuration object + :type config: interfaces.IConfig + + :param plugins: List of plugins + :type plugins: `list` of `str` + + :returns: `None` + :rtype: None + + :raises errors.Error: If specified plugin could not be used + + """ # SETUP: Select plugins and construct a client instance try: # installers are used in auth mode to determine domain names @@ -792,7 +1123,18 @@ def certonly(config, plugins): _suggest_donation_if_appropriate(config) def renew(config, unused_plugins): - """Renew previously-obtained certificates.""" + """Renew previously-obtained certificates. + + :param config: Configuration object + :type config: interfaces.IConfig + + :param unused_plugins: List of plugins (deprecated) + :type unused_plugins: `list` of `str` + + :returns: `None` + :rtype: None + + """ try: renewal.handle_renewal_request(config) finally: @@ -800,7 +1142,15 @@ def renew(config, unused_plugins): def make_or_verify_needed_dirs(config): - """Create or verify existence of config, work, and hook directories.""" + """Create or verify existence of config, work, and hook directories. + + :param config: Configuration object + :type config: interfaces.IConfig + + :returns: `None` + :rtype: None + + """ util.set_up_core_dir(config.config_dir, constants.CONFIG_DIRS_MODE, os.geteuid(), config.strict_permissions) util.set_up_core_dir(config.work_dir, constants.CONFIG_DIRS_MODE, @@ -816,7 +1166,15 @@ def make_or_verify_needed_dirs(config): def set_displayer(config): - """Set the displayer""" + """Set the displayer + + :param config: Configuration object + :type config: interfaces.IConfig + + :returns: `None` + :rtype: None + + """ if config.quiet: config.noninteractive_mode = True displayer = display_util.NoninteractiveDisplay(open(os.devnull, "w")) @@ -829,7 +1187,14 @@ def set_displayer(config): def main(cli_args=sys.argv[1:]): - """Command line argument parsing and main script execution.""" + """Command line argument parsing and main script execution. + + :returns: result of requested command + + :raises errors.Error: OS errors triggered by wrong permissions + :raises errors.Error: error if plugin command is not supported + + """ log.pre_arg_parse_setup() plugins = plugins_disco.PluginsRegistry.find_all() @@ -850,6 +1215,9 @@ def main(cli_args=sys.argv[1:]): # Let plugins_cmd be run as un-privileged user. if config.func != plugins_cmd: raise + if sys.version_info[:2] == (3, 3): + logger.warning("Python 3.3 support will be dropped in the next release " + "of Certbot - please upgrade your Python version.") set_displayer(config) diff --git a/certbot/tests/main_test.py b/certbot/tests/main_test.py index 45e5db1df..1f690df26 100644 --- a/certbot/tests/main_test.py +++ b/certbot/tests/main_test.py @@ -356,7 +356,7 @@ class DeleteIfAppropriateTest(unittest.TestCase): mock_cert_path_for_cert_name.return_value = "/some/reasonable/path" mock_overlapping_archive_dirs.return_value = False self._call(config) - mock_delete.assert_called_once() + self.assertEqual(mock_delete.call_count, 1) # pylint: disable=too-many-arguments @mock.patch('certbot.storage.renewal_file_for_certname') @@ -375,7 +375,7 @@ class DeleteIfAppropriateTest(unittest.TestCase): mock_cert_path_to_lineage.return_value = "example.com" mock_overlapping_archive_dirs.return_value = False self._call(config) - mock_delete.assert_called_once() + self.assertEqual(mock_delete.call_count, 1) # pylint: disable=too-many-arguments @mock.patch('certbot.storage.renewal_file_for_certname') @@ -396,7 +396,7 @@ class DeleteIfAppropriateTest(unittest.TestCase): mock_full_archive_dir.return_value = "" mock_match_and_check_overlaps.return_value = "" self._call(config) - mock_delete.assert_called_once() + self.assertEqual(mock_delete.call_count, 1) # pylint: disable=too-many-arguments @mock.patch('certbot.storage.renewal_file_for_certname') @@ -415,7 +415,7 @@ class DeleteIfAppropriateTest(unittest.TestCase): mock_cert_path_to_lineage.return_value = config.certname mock_overlapping_archive_dirs.return_value = False self._call(config) - mock_delete.assert_called_once() + self.assertEqual(mock_delete.call_count, 1) # pylint: disable=too-many-arguments @mock.patch('certbot.cert_manager.match_and_check_overlaps') @@ -442,7 +442,7 @@ class DeleteIfAppropriateTest(unittest.TestCase): util_mock = mock_get_utility() util_mock.menu.return_value = (display_util.OK, 0) self._call(config) - mock_delete.assert_called_once() + self.assertEqual(mock_delete.call_count, 1) # pylint: disable=too-many-arguments @mock.patch('certbot.cert_manager.match_and_check_overlaps') diff --git a/certbot/util.py b/certbot/util.py index 30de0c157..b7e60a225 100644 --- a/certbot/util.py +++ b/certbot/util.py @@ -342,9 +342,9 @@ def get_os_info_ua(filepath="/etc/os-release"): """ if os.path.isfile(filepath): - os_ua = _get_systemd_os_release_var("PRETTY_NAME", filepath=filepath) + os_ua = get_var_from_file("PRETTY_NAME", filepath=filepath) if not os_ua: - os_ua = _get_systemd_os_release_var("NAME", filepath=filepath) + os_ua = get_var_from_file("NAME", filepath=filepath) if os_ua: return os_ua @@ -361,8 +361,8 @@ def get_systemd_os_info(filepath="/etc/os-release"): :rtype: `tuple` of `str` """ - os_name = _get_systemd_os_release_var("ID", filepath=filepath) - os_version = _get_systemd_os_release_var("VERSION_ID", filepath=filepath) + os_name = get_var_from_file("ID", filepath=filepath) + os_version = get_var_from_file("VERSION_ID", filepath=filepath) return (os_name, os_version) @@ -377,10 +377,10 @@ def get_systemd_os_like(filepath="/etc/os-release"): :rtype: `list` of `str` """ - return _get_systemd_os_release_var("ID_LIKE", filepath).split(" ") + return get_var_from_file("ID_LIKE", filepath).split(" ") -def _get_systemd_os_release_var(varname, filepath="/etc/os-release"): +def get_var_from_file(varname, filepath="/etc/os-release"): """ Get single value from systemd /etc/os-release @@ -405,7 +405,7 @@ def _get_systemd_os_release_var(varname, filepath="/etc/os-release"): def _normalize_string(orig): """ - Helper function for _get_systemd_os_release_var() to remove quotes + Helper function for get_var_from_file() to remove quotes and whitespaces """ return orig.replace('"', '').replace("'", "").strip() diff --git a/docs/cli-help.txt b/docs/cli-help.txt index 6b43fd0a2..abaa95b9b 100644 --- a/docs/cli-help.txt +++ b/docs/cli-help.txt @@ -107,7 +107,7 @@ optional arguments: case, and to know when to deprecate support for past Python versions and flags. If you wish to hide this information from the Let's Encrypt server, set this to - "". (default: CertbotACMEClient/0.19.0 (certbot; + "". (default: CertbotACMEClient/0.20.0 (certbot; Ubuntu 16.04.3 LTS) Authenticator/XXX Installer/YYY (SUBCOMMAND; flags: FLAGS) Py/2.7.12). The flags encoded in the user agent are: --duplicate, --force- @@ -121,7 +121,7 @@ optional arguments: (Example: Foo-Wrapper/1.0) (default: None) automation: - Arguments for automating execution & other tweaks + Flags for automating execution & other tweaks --keep-until-expiring, --keep, --reinstall If the requested certificate matches an existing @@ -228,7 +228,7 @@ testing: False) paths: - Arguments changing execution paths & servers + Flags for changing execution paths & servers --cert-path CERT_PATH Path to where certificate is saved (with auth --csr), diff --git a/docs/using.rst b/docs/using.rst index 4fd0b5ec8..ab4670052 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -106,7 +106,7 @@ specified ``--webroot-path``. So, for instance, :: - certbot certonly --webroot -w /var/www/example/ -d www.example.com -d example.com -w /var/www/other -d other.example.net -d another.other.example.net + certbot certonly --webroot -w /var/www/example -d www.example.com -d example.com -w /var/www/other -d other.example.net -d another.other.example.net would obtain a single certificate for all of those names, using the ``/var/www/example`` webroot directory for the first two, and diff --git a/letsencrypt-auto b/letsencrypt-auto index 25f2ce889..444bee1b9 100755 --- a/letsencrypt-auto +++ b/letsencrypt-auto @@ -31,7 +31,7 @@ if [ -z "$VENV_PATH" ]; then fi VENV_BIN="$VENV_PATH/bin" BOOTSTRAP_VERSION_PATH="$VENV_PATH/certbot-auto-bootstrap-version.txt" -LE_AUTO_VERSION="0.19.0" +LE_AUTO_VERSION="0.20.0" BASENAME=$(basename $0) USAGE="Usage: $BASENAME [OPTIONS] A self-updating wrapper script for the Certbot ACME client. When run, updates @@ -1062,9 +1062,10 @@ zope.interface==4.1.3 \ --hash=sha256:928138365245a0e8869a5999fbcc2a45475a0a6ed52a494d60dbdc540335fedd \ --hash=sha256:0d841ba1bb840eea0e6489dc5ecafa6125554971f53b5acb87764441e61bceba \ --hash=sha256:b09c8c1d47b3531c400e0195697f1414a63221de6ef478598a4f1460f7d9a392 -mock==2.0.0 \ - --hash=sha256:5ce3c71c5545b472da17b72268978914d0252980348636840bd34a00b5cc96c1 \ - --hash=sha256:b158b6df76edd239b8208d481dc46b6afd45a846b7812ff0ce58971cf5bc8bba +# Using an older version of mock here prevents regressions of #5276. +mock==1.3.0 \ + --hash=sha256:3f573a18be94de886d1191f27c168427ef693e8dcfcecf95b170577b2eb69cbb \ + --hash=sha256:1e247dbecc6ce057299eb7ee019ad68314bb93152e81d9a6110d35f4d5eca0f6 # Contains the requirements for the letsencrypt package. # @@ -1077,18 +1078,18 @@ letsencrypt==0.7.0 \ --hash=sha256:105a5fb107e45bcd0722eb89696986dcf5f08a86a321d6aef25a0c7c63375ade \ --hash=sha256:c36e532c486a7e92155ee09da54b436a3c420813ec1c590b98f635d924720de9 -certbot==0.19.0 \ - --hash=sha256:3207ee5319bfc37e855c25a43148275fcfb37869eefde9087405012049734a20 \ - --hash=sha256:a7230791dff5d085738119fc22d88ad9d8a35d0b6a3d67806fe33990c7c79d53 -acme==0.19.0 \ - --hash=sha256:c612eafe234d722d97bb5d3dbc49e5522f44be29611f7577954eb893e5c2d6de \ - --hash=sha256:1fa23d64d494aaf001e6fe857c461fcfff10f75a1c2c35ec831447f641e1e822 -certbot-apache==0.19.0 \ - --hash=sha256:fadb28b33bfabc85cdb962b5b149bef58b98f0606b78581db7895fe38323f37c \ - --hash=sha256:70306ca2d5be7f542af68d46883c0ae39527cf202f17ef92cd256fb0bc3f1619 -certbot-nginx==0.19.0 \ - --hash=sha256:4909cb3db49919fb35590793cac28e1c0b6dbd29cbedf887b9106e5fcef5362c \ - --hash=sha256:cb5a224a3f277092555c25096d1678fc735306fd3a43447649ebe524c7ca79e1 +certbot==0.20.0 \ + --hash=sha256:c6b6bd288700898d1eb31a65b605e3a5fc10f1e3213ce468207d76a2decb9d35 \ + --hash=sha256:cabf505b64fb400c4239dcdbaeb882079477eb6a8442268596a8791b9e34de88 +acme==0.20.0 \ + --hash=sha256:8b0cee192c0d76d6f4045bdb14b3cfd29d9720e0dad2046794a2a555f1eaccb7 \ + --hash=sha256:45121aed6c8cc2f31896ac1083068dfdeb613f3edeff9576dc0d10632ea5a3d5 +certbot-apache==0.20.0 \ + --hash=sha256:f7e4dbc154d2e9d1461118b6dd3dbd16f6892da468f060eeaa162aff673347e2 \ + --hash=sha256:0ba499706451ffbccb172bcf93d6ef4c6cc8599157077a4fa6dfbe5a83c7921f +certbot-nginx==0.20.0 \ + --hash=sha256:b6e372e8740b20dd9bd63837646157ac97b3c9a65affd3954571b8e872ae9ecf \ + --hash=sha256:6379fdf20d9a7651fe30bb8d4b828cbea178cc263d7af5a380fc4508d793b9ae UNLIKELY_EOF # ------------------------------------------------------------------------- diff --git a/letsencrypt-auto-source/certbot-auto.asc b/letsencrypt-auto-source/certbot-auto.asc index 834358464..eeab78cd6 100644 --- a/letsencrypt-auto-source/certbot-auto.asc +++ b/letsencrypt-auto-source/certbot-auto.asc @@ -1,11 +1,11 @@ -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 -iQEcBAABCAAGBQJZ1TJAAAoJEE0XyZXNl3XyWjAIAKxR5v0qbSyOEwM1LrSoLqud -V3KkyEUlMq7IPHxoPKXbqUrIi4eZuhpJz+84LtVJe4ZQ6HYP9lPogX+PtmWW7dyO -YerxA2rUVGB9rFZofZYwTuJyvO5Nc0aDyp1FHHPg/5khWWhhhxKpWqqG3zT01+Vf -W8Lvvn7vr7sjTvxBdqHQ3z3hlUY62P2IKui9C5un5ozlSQpDrWh3Thi9r6CxbASL -/r1PQ6EfnNdPAizVrJWe5iUd0Nzj7VMkFwZ02A3OlOUvrHGVb1H6oj0S1lZ8LEpj -awOTys8PVBQ3vW2qbAL3Zk7Lr+CGfVfmoWC9TQEKiSN1woYFrFD39S527vB1onc= -=Meks +iQEcBAABCAAGBQJaKHMlAAoJEE0XyZXNl3Xy6OEH/iPg6D6+zco4NHMwxYIcTWVt +XE4u3CjuLcEVsvEnJYNSA48NHyi9rIqMHd+IneLU+lCG2D7eBsisNNyVPIgHktTf +p9i0WoZB+axe1glv9FJSZvjvr2d/ic4/wYHBF1c+szb9p8Z7o5Lhqa9/gtLJ/SZX +OGU0wok4hPIB6emq5zvmi/+r1AiOECXE26lZ0STp6wDkvz+ahTJSk6UaPCDY+Az4 +X2VmnRSks/gk7Q8cloFnyiPXyFMQHdGIBRrIXsSix90QqmNUF7iYb8sbHksU23EI +/LmIwSJlDm6KNOO2nllBB/uIg2ki7g0z7R4uf7XF4im+P95PAL/tQQ45lVj8DXE= +=Is56 -----END PGP SIGNATURE----- diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index 215b684cf..8d2e8a6b6 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -31,7 +31,7 @@ if [ -z "$VENV_PATH" ]; then fi VENV_BIN="$VENV_PATH/bin" BOOTSTRAP_VERSION_PATH="$VENV_PATH/certbot-auto-bootstrap-version.txt" -LE_AUTO_VERSION="0.20.0.dev0" +LE_AUTO_VERSION="0.21.0.dev0" BASENAME=$(basename $0) USAGE="Usage: $BASENAME [OPTIONS] A self-updating wrapper script for the Certbot ACME client. When run, updates @@ -1062,9 +1062,10 @@ zope.interface==4.1.3 \ --hash=sha256:928138365245a0e8869a5999fbcc2a45475a0a6ed52a494d60dbdc540335fedd \ --hash=sha256:0d841ba1bb840eea0e6489dc5ecafa6125554971f53b5acb87764441e61bceba \ --hash=sha256:b09c8c1d47b3531c400e0195697f1414a63221de6ef478598a4f1460f7d9a392 -mock==2.0.0 \ - --hash=sha256:5ce3c71c5545b472da17b72268978914d0252980348636840bd34a00b5cc96c1 \ - --hash=sha256:b158b6df76edd239b8208d481dc46b6afd45a846b7812ff0ce58971cf5bc8bba +# Using an older version of mock here prevents regressions of #5276. +mock==1.3.0 \ + --hash=sha256:3f573a18be94de886d1191f27c168427ef693e8dcfcecf95b170577b2eb69cbb \ + --hash=sha256:1e247dbecc6ce057299eb7ee019ad68314bb93152e81d9a6110d35f4d5eca0f6 # Contains the requirements for the letsencrypt package. # @@ -1077,18 +1078,18 @@ letsencrypt==0.7.0 \ --hash=sha256:105a5fb107e45bcd0722eb89696986dcf5f08a86a321d6aef25a0c7c63375ade \ --hash=sha256:c36e532c486a7e92155ee09da54b436a3c420813ec1c590b98f635d924720de9 -certbot==0.19.0 \ - --hash=sha256:3207ee5319bfc37e855c25a43148275fcfb37869eefde9087405012049734a20 \ - --hash=sha256:a7230791dff5d085738119fc22d88ad9d8a35d0b6a3d67806fe33990c7c79d53 -acme==0.19.0 \ - --hash=sha256:c612eafe234d722d97bb5d3dbc49e5522f44be29611f7577954eb893e5c2d6de \ - --hash=sha256:1fa23d64d494aaf001e6fe857c461fcfff10f75a1c2c35ec831447f641e1e822 -certbot-apache==0.19.0 \ - --hash=sha256:fadb28b33bfabc85cdb962b5b149bef58b98f0606b78581db7895fe38323f37c \ - --hash=sha256:70306ca2d5be7f542af68d46883c0ae39527cf202f17ef92cd256fb0bc3f1619 -certbot-nginx==0.19.0 \ - --hash=sha256:4909cb3db49919fb35590793cac28e1c0b6dbd29cbedf887b9106e5fcef5362c \ - --hash=sha256:cb5a224a3f277092555c25096d1678fc735306fd3a43447649ebe524c7ca79e1 +certbot==0.20.0 \ + --hash=sha256:c6b6bd288700898d1eb31a65b605e3a5fc10f1e3213ce468207d76a2decb9d35 \ + --hash=sha256:cabf505b64fb400c4239dcdbaeb882079477eb6a8442268596a8791b9e34de88 +acme==0.20.0 \ + --hash=sha256:8b0cee192c0d76d6f4045bdb14b3cfd29d9720e0dad2046794a2a555f1eaccb7 \ + --hash=sha256:45121aed6c8cc2f31896ac1083068dfdeb613f3edeff9576dc0d10632ea5a3d5 +certbot-apache==0.20.0 \ + --hash=sha256:f7e4dbc154d2e9d1461118b6dd3dbd16f6892da468f060eeaa162aff673347e2 \ + --hash=sha256:0ba499706451ffbccb172bcf93d6ef4c6cc8599157077a4fa6dfbe5a83c7921f +certbot-nginx==0.20.0 \ + --hash=sha256:b6e372e8740b20dd9bd63837646157ac97b3c9a65affd3954571b8e872ae9ecf \ + --hash=sha256:6379fdf20d9a7651fe30bb8d4b828cbea178cc263d7af5a380fc4508d793b9ae UNLIKELY_EOF # ------------------------------------------------------------------------- diff --git a/letsencrypt-auto-source/letsencrypt-auto.sig b/letsencrypt-auto-source/letsencrypt-auto.sig index 708bbbee6..e276aae53 100644 --- a/letsencrypt-auto-source/letsencrypt-auto.sig +++ b/letsencrypt-auto-source/letsencrypt-auto.sig @@ -1,2 +1,2 @@ -לHɭm+~ESM4 KYjL7ZճddI.:dZMO|Km|g($bljЅ/A^`ra^0x咁w9ckN.[ ? -h/́8!\!U59bRlb-1(p> -%u2gn \ No newline at end of file +HtPdM-b_8Gݵx\cf<9n$-^56zOy3o-N~ֹn% ww''}q;̰: + M4\@)-:ǺzDY=쟭*' $certbot_auto_constraints dev_constraints="$(dirname $my_path)/pip_constraints.txt" +set -x + # install the requested packages using the pinned requirements as constraints -pip install --constraint $certbot_auto_constraints --constraint $dev_constraints "$@" +pip install -q --constraint $certbot_auto_constraints --constraint $dev_constraints "$@" diff --git a/tox.cover.sh b/tox.cover.sh index 3f0a5f72e..2b5a3cf19 100755 --- a/tox.cover.sh +++ b/tox.cover.sh @@ -16,7 +16,7 @@ fi cover () { if [ "$1" = "certbot" ]; then - min=97 + min=98 elif [ "$1" = "acme" ]; then min=100 elif [ "$1" = "certbot_apache" ]; then @@ -24,23 +24,23 @@ cover () { elif [ "$1" = "certbot_dns_cloudflare" ]; then min=98 elif [ "$1" = "certbot_dns_cloudxns" ]; then - min=98 + min=99 elif [ "$1" = "certbot_dns_digitalocean" ]; then min=98 elif [ "$1" = "certbot_dns_dnsimple" ]; then min=98 elif [ "$1" = "certbot_dns_dnsmadeeasy" ]; then - min=98 + min=99 elif [ "$1" = "certbot_dns_google" ]; then min=99 elif [ "$1" = "certbot_dns_luadns" ]; then min=98 elif [ "$1" = "certbot_dns_nsone" ]; then - min=98 + min=99 elif [ "$1" = "certbot_dns_rfc2136" ]; then min=99 elif [ "$1" = "certbot_dns_route53" ]; then - min=91 + min=92 elif [ "$1" = "certbot_nginx" ]; then min=97 elif [ "$1" = "letshelp_certbot" ]; then @@ -50,10 +50,12 @@ cover () { exit 1 fi - pytest --cov "$1" --cov-report term-missing \ - --cov-fail-under "$min" --numprocesses auto --pyargs "$1" + pkg_dir=$(echo "$1" | tr _ -) + pytest --cov "$pkg_dir" --cov-append --cov-report= --numprocesses auto --pyargs "$1" + coverage report --fail-under="$min" --include="$pkg_dir/*" --show-missing } +rm -f .coverage # --cov-append is on, make sure stats are correct for pkg in $pkgs do cover $pkg