certbot/letsencrypt-apache/letsencrypt_apache/dvsni.py

172 lines
5.6 KiB
Python
Raw Normal View History

"""ApacheDVSNI"""
import os
from letsencrypt.plugins import common
2015-07-15 17:34:24 -04:00
from letsencrypt_apache import obj
2015-05-10 06:47:58 -04:00
from letsencrypt_apache import parser
2015-01-09 08:30:15 -05:00
class ApacheDvsni(common.Dvsni):
"""Class performs DVSNI challenges within the Apache configurator.
:ivar configurator: ApacheConfigurator object
:type configurator: :class:`~apache.configurator.ApacheConfigurator`
2015-05-10 08:25:29 -04:00
:ivar list achalls: Annotated :class:`~letsencrypt.achallenges.DVSNI`
2015-02-13 17:37:45 -05:00
challenges.
:param list indices: Meant to hold indices of challenges in a
2015-01-10 01:25:36 -05:00
larger array. ApacheDvsni is capable of solving many challenges
at once which causes an indexing issue within ApacheConfigurator
who must return all responses in order. Imagine ApacheConfigurator
2015-06-11 12:04:46 -04:00
maintaining state about where all of the SimpleHTTP Challenges,
2015-01-10 01:25:36 -05:00
Dvsni Challenges belong in the response array. This is an optional
utility.
:param str challenge_conf: location of the challenge config file
"""
2015-03-25 06:21:01 -04:00
VHOST_TEMPLATE = """\
<VirtualHost {vhost}>
ServerName {server_name}
UseCanonicalName on
SSLStrictSNIVHostCheck on
LimitRequestBody 1048576
Include {ssl_options_conf_path}
SSLCertificateFile {cert_path}
SSLCertificateKeyFile {key_path}
DocumentRoot {document_root}
</VirtualHost>
"""
2015-07-19 05:22:10 -04:00
def __init__(self, *args, **kwargs):
2015-07-09 21:55:08 -04:00
super(ApacheDvsni, self).__init__(*args, **kwargs)
self.challenge_conf = os.path.join(
self.configurator.conf("server-root"),
"le_dvsni_cert_challenge.conf")
def perform(self):
2015-08-17 13:40:42 -04:00
"""Perform a DVSNI challenge."""
2015-02-13 17:37:45 -05:00
if not self.achalls:
2015-03-24 20:49:38 -04:00
return []
# Save any changes to the configuration as a precaution
# About to make temporary changes to the config
self.configurator.save()
2015-07-19 05:22:10 -04:00
# Prepare the server for HTTPS
self.configurator.prepare_server_https(
str(self.configurator.config.dvsni_port), True)
2015-07-19 05:22:10 -04:00
responses = []
# Create all of the challenge certs
2015-02-13 17:37:45 -05:00
for achall in self.achalls:
responses.append(self._setup_challenge_cert(achall))
# Setup the configuration
2015-07-15 17:34:24 -04:00
dvsni_addrs = self._mod_config()
self.configurator.make_addrs_sni_ready(dvsni_addrs)
# Save reversible changes
self.configurator.save("SNI Challenge", True)
return responses
2015-07-15 17:34:24 -04:00
def _mod_config(self):
"""Modifies Apache config files to include challenge vhosts.
Result: Apache config includes virtual servers for issued challs
2015-07-15 17:34:24 -04:00
:returns: All DVSNI addresses used
:rtype: set
"""
2015-07-15 17:34:24 -04:00
dvsni_addrs = set()
config_text = "<IfModule mod_ssl.c>\n"
2015-07-15 17:34:24 -04:00
for achall in self.achalls:
achall_addrs = self.get_dvsni_addrs(achall)
dvsni_addrs.update(achall_addrs)
2015-07-19 05:22:10 -04:00
config_text += self._get_config_text(achall, achall_addrs)
2015-07-15 17:34:24 -04:00
config_text += "</IfModule>\n"
self._conf_include_check(self.configurator.parser.loc["default"])
self.configurator.reverter.register_file_creation(
True, self.challenge_conf)
2015-03-26 20:39:08 -04:00
with open(self.challenge_conf, "w") as new_conf:
new_conf.write(config_text)
2015-07-15 17:34:24 -04:00
return dvsni_addrs
def get_dvsni_addrs(self, achall):
"""Return the Apache addresses needed for DVSNI."""
vhost = self.configurator.choose_vhost(achall.domain)
# TODO: Checkout _default_ rules.
dvsni_addrs = set()
2015-07-24 20:05:25 -04:00
default_addr = obj.Addr(("*", str(self.configurator.config.dvsni_port)))
2015-07-15 17:34:24 -04:00
for addr in vhost.addrs:
if "_default_" == addr.get_addr():
dvsni_addrs.add(default_addr)
else:
dvsni_addrs.add(
addr.get_sni_addr(self.configurator.config.dvsni_port))
return dvsni_addrs
2015-01-10 01:25:36 -05:00
def _conf_include_check(self, main_config):
"""Adds DVSNI challenge conf file into configuration.
Adds DVSNI challenge include file if it does not already exist
within mainConfig
:param str main_config: file path to main user apache config file
"""
if len(self.configurator.parser.find_dir(
parser.case_i("Include"), self.challenge_conf)) == 0:
# print "Including challenge virtual host(s)"
self.configurator.parser.add_dir(
parser.get_aug_path(main_config),
"Include", self.challenge_conf)
2015-02-13 17:37:45 -05:00
def _get_config_text(self, achall, ip_addrs):
"""Chocolate virtual server configuration text
2015-02-13 17:37:45 -05:00
:param achall: Annotated DVSNI challenge.
2015-05-10 08:25:29 -04:00
:type achall: :class:`letsencrypt.achallenges.DVSNI`
2015-02-13 17:37:45 -05:00
:param list ip_addrs: addresses of challenged domain
2015-07-15 17:34:24 -04:00
:class:`list` of type `~.obj.Addr`
:returns: virtual host configuration text
:rtype: str
"""
ips = " ".join(str(i) for i in ip_addrs)
document_root = os.path.join(
self.configurator.config.work_dir, "dvsni_page/")
2015-03-25 06:21:01 -04:00
# TODO: Python docs is not clear how mutliline string literal
# newlines are parsed on different platforms. At least on
2015-03-25 06:25:27 -04:00
# Linux (Debian sid), when source file uses CRLF, Python still
2015-03-26 20:39:08 -04:00
# parses it as "\n"... c.f.:
2015-03-25 06:21:01 -04:00
# https://docs.python.org/2.7/reference/lexical_analysis.html
return self.VHOST_TEMPLATE.format(
2015-08-05 18:39:31 -04:00
vhost=ips,
server_name=achall.gen_response(achall.account_key).z_domain,
2015-07-19 22:49:44 -04:00
ssl_options_conf_path=self.configurator.mod_ssl_conf,
2015-07-09 04:19:54 -04:00
cert_path=self.get_cert_path(achall),
key_path=self.get_key_path(achall),
2015-03-26 20:39:08 -04:00
document_root=document_root).replace("\n", os.linesep)