mirror of
https://github.com/certbot/certbot.git
synced 2026-02-24 10:20:58 -05:00
* Isort execution * Fix pylint, adapt coverage * New isort * Fix magic_typing lint * Second round * Fix pylint * Third round. Store isort configuration * Fix latest mistakes * Other fixes * Add newline * Fix lint errors
215 lines
8.5 KiB
Python
215 lines
8.5 KiB
Python
""" Distribution specific override class for CentOS family (RHEL, Fedora) """
|
|
import logging
|
|
|
|
import pkg_resources
|
|
import zope.interface
|
|
|
|
from acme.magic_typing import List # pylint: disable=unused-import, no-name-in-module
|
|
from certbot import errors
|
|
from certbot import interfaces
|
|
from certbot import util
|
|
from certbot.compat import os
|
|
from certbot.errors import MisconfigurationError
|
|
from certbot_apache._internal import apache_util
|
|
from certbot_apache._internal import configurator
|
|
from certbot_apache._internal import parser
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@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",
|
|
ctl="apachectl",
|
|
version_cmd=['apachectl', '-v'],
|
|
restart_cmd=['apachectl', 'graceful'],
|
|
restart_cmd_alt=['apachectl', 'restart'],
|
|
conftest_cmd=['apachectl', 'configtest'],
|
|
enmod=None,
|
|
dismod=None,
|
|
le_vhost_ext="-le-ssl.conf",
|
|
handle_modules=False,
|
|
handle_sites=False,
|
|
challenge_location="/etc/httpd/conf.d",
|
|
MOD_SSL_CONF_SRC=pkg_resources.resource_filename(
|
|
"certbot_apache", os.path.join("_internal", "centos-options-ssl-apache.conf"))
|
|
)
|
|
|
|
def config_test(self):
|
|
"""
|
|
Override config_test to mitigate configtest error in vanilla installation
|
|
of mod_ssl in Fedora. The error is caused by non-existent self-signed
|
|
certificates referenced by the configuration, that would be autogenerated
|
|
during the first (re)start of httpd.
|
|
"""
|
|
|
|
os_info = util.get_os_info()
|
|
fedora = os_info[0].lower() == "fedora"
|
|
|
|
try:
|
|
super(CentOSConfigurator, self).config_test()
|
|
except errors.MisconfigurationError:
|
|
if fedora:
|
|
self._try_restart_fedora()
|
|
else:
|
|
raise
|
|
|
|
def _try_restart_fedora(self):
|
|
"""
|
|
Tries to restart httpd using systemctl to generate the self signed keypair.
|
|
"""
|
|
|
|
try:
|
|
util.run_script(['systemctl', 'restart', 'httpd'])
|
|
except errors.SubprocessError as err:
|
|
raise errors.MisconfigurationError(str(err))
|
|
|
|
# Finish with actual config check to see if systemctl restart helped
|
|
super(CentOSConfigurator, self).config_test()
|
|
|
|
def _prepare_options(self):
|
|
"""
|
|
Override the options dictionary initialization in order to support
|
|
alternative restart cmd used in CentOS.
|
|
"""
|
|
super(CentOSConfigurator, self)._prepare_options()
|
|
self.options["restart_cmd_alt"][0] = self.option("ctl")
|
|
|
|
def get_parser(self):
|
|
"""Initializes the ApacheParser"""
|
|
return CentOSParser(
|
|
self.option("server_root"), self.option("vhost_root"),
|
|
self.version, configurator=self)
|
|
|
|
def _deploy_cert(self, *args, **kwargs): # pylint: disable=arguments-differ
|
|
"""
|
|
Override _deploy_cert in order to ensure that the Apache configuration
|
|
has "LoadModule ssl_module..." before parsing the VirtualHost configuration
|
|
that was created by Certbot
|
|
"""
|
|
super(CentOSConfigurator, self)._deploy_cert(*args, **kwargs)
|
|
if self.version < (2, 4, 0):
|
|
self._deploy_loadmodule_ssl_if_needed()
|
|
|
|
def _deploy_loadmodule_ssl_if_needed(self):
|
|
"""
|
|
Add "LoadModule ssl_module <pre-existing path>" to main httpd.conf if
|
|
it doesn't exist there already.
|
|
"""
|
|
|
|
loadmods = self.parser.find_dir("LoadModule", "ssl_module", exclude=False)
|
|
|
|
correct_ifmods = [] # type: List[str]
|
|
loadmod_args = [] # type: List[str]
|
|
loadmod_paths = [] # type: List[str]
|
|
for m in loadmods:
|
|
noarg_path = m.rpartition("/")[0]
|
|
path_args = self.parser.get_all_args(noarg_path)
|
|
if loadmod_args:
|
|
if loadmod_args != path_args:
|
|
msg = ("Certbot encountered multiple LoadModule directives "
|
|
"for LoadModule ssl_module with differing library paths. "
|
|
"Please remove or comment out the one(s) that are not in "
|
|
"use, and run Certbot again.")
|
|
raise MisconfigurationError(msg)
|
|
else:
|
|
loadmod_args = path_args
|
|
|
|
if self.parser.not_modssl_ifmodule(noarg_path): # pylint: disable=no-member
|
|
if self.parser.loc["default"] in noarg_path:
|
|
# LoadModule already in the main configuration file
|
|
if ("ifmodule/" in noarg_path.lower() or
|
|
"ifmodule[1]" in noarg_path.lower()):
|
|
# It's the first or only IfModule in the file
|
|
return
|
|
# Populate the list of known !mod_ssl.c IfModules
|
|
nodir_path = noarg_path.rpartition("/directive")[0]
|
|
correct_ifmods.append(nodir_path)
|
|
else:
|
|
loadmod_paths.append(noarg_path)
|
|
|
|
if not loadmod_args:
|
|
# Do not try to enable mod_ssl
|
|
return
|
|
|
|
# Force creation as the directive wasn't found from the beginning of
|
|
# httpd.conf
|
|
rootconf_ifmod = self.parser.create_ifmod(
|
|
parser.get_aug_path(self.parser.loc["default"]),
|
|
"!mod_ssl.c", beginning=True)
|
|
# parser.get_ifmod returns a path postfixed with "/", remove that
|
|
self.parser.add_dir(rootconf_ifmod[:-1], "LoadModule", loadmod_args)
|
|
correct_ifmods.append(rootconf_ifmod[:-1])
|
|
self.save_notes += "Added LoadModule ssl_module to main configuration.\n"
|
|
|
|
# Wrap LoadModule mod_ssl inside of <IfModule !mod_ssl.c> if it's not
|
|
# configured like this already.
|
|
for loadmod_path in loadmod_paths:
|
|
nodir_path = loadmod_path.split("/directive")[0]
|
|
# Remove the old LoadModule directive
|
|
self.parser.aug.remove(loadmod_path)
|
|
|
|
# Create a new IfModule !mod_ssl.c if not already found on path
|
|
ssl_ifmod = self.parser.get_ifmod(nodir_path, "!mod_ssl.c",
|
|
beginning=True)[:-1]
|
|
if ssl_ifmod not in correct_ifmods:
|
|
self.parser.add_dir(ssl_ifmod, "LoadModule", loadmod_args)
|
|
correct_ifmods.append(ssl_ifmod)
|
|
self.save_notes += ("Wrapped pre-existing LoadModule ssl_module "
|
|
"inside of <IfModule !mod_ssl> block.\n")
|
|
|
|
|
|
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):
|
|
""" Override for update_runtime_variables for custom parsing """
|
|
# Opportunistic, works if SELinux not enforced
|
|
super(CentOSParser, self).update_runtime_variables()
|
|
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:
|
|
self.variables[k] = defines[k]
|
|
|
|
def not_modssl_ifmodule(self, path):
|
|
"""Checks if the provided Augeas path has argument !mod_ssl"""
|
|
|
|
if "ifmodule" not in path.lower():
|
|
return False
|
|
|
|
# Trim the path to the last ifmodule
|
|
workpath = path.lower()
|
|
while workpath:
|
|
# Get path to the last IfModule (ignore the tail)
|
|
parts = workpath.rpartition("ifmodule")
|
|
|
|
if not parts[0]:
|
|
# IfModule not found
|
|
break
|
|
ifmod_path = parts[0] + parts[1]
|
|
# Check if ifmodule had an index
|
|
if parts[2].startswith("["):
|
|
# Append the index from tail
|
|
ifmod_path += parts[2].partition("/")[0]
|
|
# Get the original path trimmed to correct length
|
|
# This is required to preserve cases
|
|
ifmod_real_path = path[0:len(ifmod_path)]
|
|
if "!mod_ssl.c" in self.get_all_args(ifmod_real_path):
|
|
return True
|
|
# Set the workpath to the heading part
|
|
workpath = parts[0]
|
|
|
|
return False
|