Viewing file: http_01.py (7.78 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
"""A class that performs HTTP-01 challenges for Apache""" import logging import os
from acme.magic_typing import List, Set # pylint: disable=unused-import, no-name-in-module from certbot import errors from certbot.plugins import common from certbot_apache.obj import VirtualHost # pylint: disable=unused-import from certbot_apache.parser import get_aug_path
logger = logging.getLogger(__name__)
class ApacheHttp01(common.TLSSNI01): """Class that performs HTTP-01 challenges within the Apache configurator."""
CONFIG_TEMPLATE22_PRE = """\ RewriteEngine on RewriteRule ^/\\.well-known/acme-challenge/([A-Za-z0-9-_=]+)$ {0}/$1 [L]
""" CONFIG_TEMPLATE22_POST = """\ <Directory {0}> Order Allow,Deny Allow from all </Directory> <Location /.well-known/acme-challenge> Order Allow,Deny Allow from all </Location> """
CONFIG_TEMPLATE24_PRE = """\ RewriteEngine on RewriteRule ^/\\.well-known/acme-challenge/([A-Za-z0-9-_=]+)$ {0}/$1 [END] """ CONFIG_TEMPLATE24_POST = """\ <Directory {0}> Require all granted </Directory> <Location /.well-known/acme-challenge> Require all granted </Location> """
def __init__(self, *args, **kwargs): super(ApacheHttp01, self).__init__(*args, **kwargs) self.challenge_conf_pre = os.path.join( self.configurator.conf("challenge-location"), "le_http_01_challenge_pre.conf") self.challenge_conf_post = os.path.join( self.configurator.conf("challenge-location"), "le_http_01_challenge_post.conf") self.challenge_dir = os.path.join( self.configurator.config.work_dir, "http_challenges") self.moded_vhosts = set() # type: Set[VirtualHost]
def perform(self): """Perform all HTTP-01 challenges.""" if not self.achalls: return [] # Save any changes to the configuration as a precaution # About to make temporary changes to the config self.configurator.save("Changes before challenge setup", True)
self.configurator.ensure_listen(str( self.configurator.config.http01_port)) self.prepare_http01_modules()
responses = self._set_up_challenges()
self._mod_config() # Save reversible changes self.configurator.save("HTTP Challenge", True)
return responses
def prepare_http01_modules(self): """Make sure that we have the needed modules available for http01"""
if self.configurator.conf("handle-modules"): needed_modules = ["rewrite"] if self.configurator.version < (2, 4): needed_modules.append("authz_host") else: needed_modules.append("authz_core") for mod in needed_modules: if mod + "_module" not in self.configurator.parser.modules: self.configurator.enable_mod(mod, temp=True)
def _mod_config(self): selected_vhosts = [] # type: List[VirtualHost] http_port = str(self.configurator.config.http01_port) for chall in self.achalls: # Search for matching VirtualHosts for vh in self._matching_vhosts(chall.domain): selected_vhosts.append(vh)
# Ensure that we have one or more VirtualHosts that we can continue # with. (one that listens to port configured with --http-01-port) found = False for vhost in selected_vhosts: if any(a.is_wildcard() or a.get_port() == http_port for a in vhost.addrs): found = True
if not found: for vh in self._relevant_vhosts(): selected_vhosts.append(vh)
# Add the challenge configuration for vh in selected_vhosts: self._set_up_include_directives(vh)
self.configurator.reverter.register_file_creation( True, self.challenge_conf_pre) self.configurator.reverter.register_file_creation( True, self.challenge_conf_post)
if self.configurator.version < (2, 4): config_template_pre = self.CONFIG_TEMPLATE22_PRE config_template_post = self.CONFIG_TEMPLATE22_POST else: config_template_pre = self.CONFIG_TEMPLATE24_PRE config_template_post = self.CONFIG_TEMPLATE24_POST
config_text_pre = config_template_pre.format(self.challenge_dir) config_text_post = config_template_post.format(self.challenge_dir)
logger.debug("writing a pre config file with text:\n %s", config_text_pre) with open(self.challenge_conf_pre, "w") as new_conf: new_conf.write(config_text_pre) logger.debug("writing a post config file with text:\n %s", config_text_post) with open(self.challenge_conf_post, "w") as new_conf: new_conf.write(config_text_post)
def _matching_vhosts(self, domain): """Return all VirtualHost objects that have the requested domain name or a wildcard name that would match the domain in ServerName or ServerAlias directive. """ matching_vhosts = [] for vhost in self.configurator.vhosts: if self.configurator.domain_in_names(vhost.get_names(), domain): # domain_in_names also matches the exact names, so no need # to check "domain in vhost.get_names()" explicitly here matching_vhosts.append(vhost)
return matching_vhosts
def _relevant_vhosts(self): http01_port = str(self.configurator.config.http01_port) relevant_vhosts = [] for vhost in self.configurator.vhosts: if any(a.is_wildcard() or a.get_port() == http01_port for a in vhost.addrs): if not vhost.ssl: relevant_vhosts.append(vhost) if not relevant_vhosts: raise errors.PluginError( "Unable to find a virtual host listening on port {0} which is" " currently needed for Certbot to prove to the CA that you" " control your domain. Please add a virtual host for port" " {0}.".format(http01_port))
return relevant_vhosts
def _set_up_challenges(self): if not os.path.isdir(self.challenge_dir): os.makedirs(self.challenge_dir) os.chmod(self.challenge_dir, 0o755)
responses = [] for achall in self.achalls: responses.append(self._set_up_challenge(achall))
return responses
def _set_up_challenge(self, achall): response, validation = achall.response_and_validation()
name = os.path.join(self.challenge_dir, achall.chall.encode("token"))
self.configurator.reverter.register_file_creation(True, name) with open(name, 'wb') as f: f.write(validation.encode()) os.chmod(name, 0o644)
return response
def _set_up_include_directives(self, vhost): """Includes override configuration to the beginning and to the end of VirtualHost. Note that this include isn't added to Augeas search tree"""
if vhost not in self.moded_vhosts: logger.debug( "Adding a temporary challenge validation Include for name: %s " + "in: %s", vhost.name, vhost.filep) self.configurator.parser.add_dir_beginning( vhost.path, "Include", self.challenge_conf_pre) self.configurator.parser.add_dir( vhost.path, "Include", self.challenge_conf_post)
if not vhost.enabled: self.configurator.parser.add_dir( get_aug_path(self.configurator.parser.loc["default"]), "Include", vhost.filep)
self.moded_vhosts.add(vhost)
|