From 89d810c06a966efc2e946351a81f7974fd320af7 Mon Sep 17 00:00:00 2001 From: James Kasten Date: Mon, 13 Jul 2015 14:16:51 -0700 Subject: [PATCH] Fix enable_mod --- .../letsencrypt_apache/configurator.py | 56 +++++++++++++++---- .../letsencrypt_apache/parser.py | 22 +++++++- letsencrypt_apache | 1 - letsencrypt_nginx | 1 - 4 files changed, 66 insertions(+), 14 deletions(-) delete mode 120000 letsencrypt_apache delete mode 120000 letsencrypt_nginx diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index 0d9fc4bda..cb6029477 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -64,7 +64,11 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): This class can adequately configure most typical configurations but is not ready to handle very complex configurations. - .. todo:: Add support for config file variables Define rootDir /var/www/ + .. todo:: Always use self.parser.aug_get rather than self.aug.get + .. todo:: Verify permissions on configuration root... it is easier than + checking permissions on each of the relative directories and less error + prone. + The API of this class will change in the coming weeks as the exact needs of clients are clarified with the new and developing protocol. @@ -929,26 +933,56 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): :param str mod_name: Name of the module to enable. (e.g. 'ssl') """ - try: - # Use check_output so the command will finish before reloading - # TODO: a2enmod is debian specific... - subprocess.check_call([self.conf("enmod"), mod_name], - stdout=open("/dev/null", "w"), - stderr=open("/dev/null", "w")) - except (OSError, subprocess.CalledProcessError): - logger.exception("Error enabling mod_%s", mod_name) + # Support Debian specific setup + if (not os.path.isdir(os.path.join(self.parser.root, "mods-available")) + or not os.path.isdir( + os.path.join(self.parser.root, "mods-enabled"))): raise errors.MisconfigurationError( - "Missing enable_mod binary or lack privileges") + "Unsupported directory layout. You may try to enable mod %s " + "and try again." % mod_name) + + self._enable_mod_debian(mod_name) + self.parser.modules.add(mod_name + "_module") self.parser.modules.add("mod_" + mod_name) + def _enable_mod_debian(self, mod_name): + """Assumes mods-available, mods-enabled layout.""" + + # TODO: This can be further updated to not require all files. + if mod_name == "ssl": + self._enable_mod_debian_files(["ssl.conf", "ssl.load"]) + elif mod_name == "rewrite": + self._enable_mod_debian_files(["rewrite.load"]) + else: + raise NotImplemented + + def _enable_mod_debian_files(self, filenames): + """Move over all required files into mods-enabled.""" + mods_available = os.path.join(self.parser.root, "mods-available") + mods_enabled = os.path.join(self.parser.root, "mods-enabled") + + # Check to see all files are available. + for filename in filenames: + if not os.path.isfile(os.path.join(mods_available, filename)): + raise errors.MisconfigurationError( + "Unable to enable module. Required files missing from " + "mods-available. %s" % str(filenames)) + + # Register and symlink files + for filename in files: + enabled_path = os.path.join(mods_enabled, filename) + self.reverter.register_file_creation(False, enabled_path) + os.symlink(os.path.join(mods_available, filename), enabled_path) + + def mod_loaded(self, module): """Checks to see if mod_ssl is loaded Uses ``apache_ctl`` to get loaded module list. This also effectively serves as a config_test. - :returns: If ssl_module is included and active in Apache + :returns: If module is loaded. :rtype: bool """ diff --git a/letsencrypt-apache/letsencrypt_apache/parser.py b/letsencrypt-apache/letsencrypt_apache/parser.py index 1f233f187..dfd94db98 100644 --- a/letsencrypt-apache/letsencrypt_apache/parser.py +++ b/letsencrypt-apache/letsencrypt_apache/parser.py @@ -12,6 +12,9 @@ from letsencrypt import errors logger = logging.getLogger(__name__) +arg_var_interpreter = re.compile(r"\$\{[^ \}]*}") + + class ApacheParser(object): """Class handles the fine details of parsing the Apache Configuration. @@ -71,7 +74,9 @@ class ApacheParser(object): def _init_runtime_variables(self, ctl): """" - ..todo:: Also use apache2ctl -V for compiled parameters + .. note:: Compile time variables (apache2ctl -V) are not used within the + dynamic configuration files. These should not be parsed or + interpreted. """ stdout = self._get_runtime_cfg(ctl) @@ -276,6 +281,21 @@ class ApacheParser(object): return ordered_matches + def get_arg(self, match): + """Uses augeas.get to get argument value and interprets result. + + This also converts all variables and parameters appropriately. + + """ + value = self.aug.get(match) + variables = arg_var_interpreter.findall(value) + + for var in variables: + # Strip off ${ and } + value = value.replace(var, self.variables[var[2:-1]]) + + return value + def _exclude_dirs(self, matches): """Exclude directives that are not loaded into the configuration.""" filters = [("ifmodule", self.modules), ("ifdefine", self.variables)] diff --git a/letsencrypt_apache b/letsencrypt_apache deleted file mode 120000 index f50a82d01..000000000 --- a/letsencrypt_apache +++ /dev/null @@ -1 +0,0 @@ -letsencrypt-apache \ No newline at end of file diff --git a/letsencrypt_nginx b/letsencrypt_nginx deleted file mode 120000 index c2f40bb9d..000000000 --- a/letsencrypt_nginx +++ /dev/null @@ -1 +0,0 @@ -letsencrypt-nginx/ \ No newline at end of file