mirror of
https://github.com/certbot/certbot.git
synced 2026-06-03 13:59:02 -04:00
Support dvsni_port, reorganize dvsni
This commit is contained in:
parent
1b13458463
commit
0d69e5cff4
3 changed files with 99 additions and 55 deletions
|
|
@ -53,25 +53,13 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
# pylint: disable=too-many-instance-attributes,too-many-public-methods
|
||||
"""Apache configurator.
|
||||
|
||||
State of Configurator: This code has been tested under Ubuntu 12.04
|
||||
Apache 2.2 and this code works for Ubuntu 14.04 Apache 2.4. Further
|
||||
notes below.
|
||||
|
||||
This class was originally developed for Apache 2.2 and I have been slowly
|
||||
transitioning the codebase to work with all of the 2.4 features.
|
||||
I have implemented most of the changes... the missing ones are
|
||||
mod_ssl.c vs ssl_mod, and I need to account for configuration variables.
|
||||
This class can adequately configure most typical configurations but
|
||||
is not ready to handle very complex configurations.
|
||||
State of Configurator: This code has been been tested and built for Ubuntu
|
||||
14.04 Apache 2.4 and it works for Ubuntu 12.04 Apache 2.2
|
||||
|
||||
.. 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.
|
||||
|
||||
:ivar config: Configuration.
|
||||
:type config: :class:`~letsencrypt.interfaces.IConfig`
|
||||
|
||||
|
|
@ -204,6 +192,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
else:
|
||||
self.aug.set(path["chain_path"][-1], chain_path)
|
||||
|
||||
# Save notes about the transaction that took place
|
||||
self.save_notes += ("Changed vhost at %s with addresses of %s\n" %
|
||||
(vhost.filep,
|
||||
", ".join(str(addr) for addr in vhost.addrs)))
|
||||
|
|
@ -360,7 +349,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
addrs = set()
|
||||
args = self.aug.match(path + "/arg")
|
||||
for arg in args:
|
||||
addrs.add(common.Addr.fromstring(self.parser.get_arg(arg)))
|
||||
addrs.add(obj.Addr.fromstring(self.parser.get_arg(arg)))
|
||||
is_ssl = False
|
||||
|
||||
if self.parser.find_dir(
|
||||
|
|
@ -400,7 +389,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
now NameVirtualHosts. If version is earlier than 2.4, check if addr
|
||||
has a NameVirtualHost directive in the Apache config
|
||||
|
||||
:param str target_addr: vhost address ie. \*:443
|
||||
:param target_addr: vhost address
|
||||
:type target_addr: :class:~letsencrypt_apache.obj.Addr
|
||||
|
||||
:returns: Success
|
||||
:rtype: bool
|
||||
|
|
@ -419,7 +409,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
def add_name_vhost(self, addr):
|
||||
"""Adds NameVirtualHost directive for given address.
|
||||
|
||||
:param str addr: Address that will be added as NameVirtualHost directive
|
||||
:param addr: Address that will be added as NameVirtualHost directive
|
||||
:type addr: :class:~letsencrypt_apache.obj.Addr
|
||||
|
||||
"""
|
||||
path = self.parser.add_dir_to_ifmodssl(
|
||||
|
|
@ -435,7 +426,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
Make sure that the ssl_module is loaded and that the server
|
||||
is appropriately listening on port.
|
||||
|
||||
:param int port: Port to listen on
|
||||
:param str port: Port to listen on
|
||||
|
||||
"""
|
||||
if "ssl_module" not in self.parser.modules:
|
||||
|
|
@ -444,30 +435,34 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
|
||||
# Check for Listen <port>
|
||||
# Note: This could be made to also look for ip:443 combo
|
||||
if not self.parser.find_dir(parser.case_i("Listen"), str(port)):
|
||||
if not self.parser.find_dir(parser.case_i("Listen"), port):
|
||||
logger.debug("No Listen {0} directive found. Setting the "
|
||||
"Apache Server to Listen on port {0}".format(port))
|
||||
path = self.parser.add_dir_to_ifmodssl(
|
||||
parser.get_aug_path(
|
||||
self.parser.loc["listen"]), "Listen", str(port))
|
||||
self.save_notes += "Added Listen %d directive to %s\n" % (
|
||||
self.parser.loc["listen"]), "Listen", port)
|
||||
self.save_notes += "Added Listen %s directive to %s\n" % (
|
||||
port, path)
|
||||
|
||||
def make_server_sni_ready(self, vhost, default_addr="*:443"):
|
||||
def make_addrs_sni_ready(
|
||||
self, addrs, default_addr=obj.Addr(("*", "443"))):
|
||||
"""Checks to see if the server is ready for SNI challenges.
|
||||
|
||||
:param vhost: VirtualHost to check SNI compatibility
|
||||
:type vhost: :class:`~letsencrypt_apache.obj.VirtualHost`
|
||||
:param addrs: Addresses to check SNI compatibility
|
||||
:type addrs: :class:`~letsencrypt_apache.obj.Addr`
|
||||
|
||||
:param str default_addr: TODO - investigate function further
|
||||
:param default_addr: TODO - investigate function further
|
||||
:type default_addr: :class:~letsencrypt_apache.obj.Addr
|
||||
|
||||
"""
|
||||
# Version 2.4 and later are automatically SNI ready.
|
||||
if self.version >= (2, 4):
|
||||
return
|
||||
|
||||
# TODO: Review this 3-year old demo code
|
||||
# Check for NameVirtualHost
|
||||
# First see if any of the vhost addresses is a _default_ addr
|
||||
for addr in vhost.addrs:
|
||||
for addr in addrs:
|
||||
if addr.get_addr() == "_default_":
|
||||
if not self.is_name_vhost(default_addr):
|
||||
logger.debug("Setting all VirtualHosts on %s to be "
|
||||
|
|
@ -475,7 +470,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
self.add_name_vhost(default_addr)
|
||||
|
||||
# No default addresses... so set each one individually
|
||||
for addr in vhost.addrs:
|
||||
for addr in addrs:
|
||||
if not self.is_name_vhost(addr):
|
||||
logger.debug("Setting VirtualHost at %s to be a name "
|
||||
"based virtual host", addr)
|
||||
|
|
@ -582,7 +577,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
ssl_addr_p = self.aug.match(vh_path + "/arg")
|
||||
|
||||
for addr in ssl_addr_p:
|
||||
old_addr = common.Addr.fromstring(
|
||||
old_addr = obj.Addr.fromstring(
|
||||
str(self.parser.get_arg(addr)))
|
||||
ssl_addr = old_addr.get_addr_obj("443")
|
||||
self.aug.set(addr, str(ssl_addr))
|
||||
|
|
@ -880,8 +875,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
# Instead... should look for vhost of the form *:80
|
||||
# Should we prompt the user?
|
||||
ssl_addrs = ssl_vhost.addrs
|
||||
if ssl_addrs == common.Addr.fromstring("_default_:443"):
|
||||
ssl_addrs = [common.Addr.fromstring("*:443")]
|
||||
if ssl_addrs == obj.Addr.fromstring("_default_:443"):
|
||||
ssl_addrs = [obj.Addr.fromstring("*:443")]
|
||||
|
||||
for vhost in self.vhosts:
|
||||
found = 0
|
||||
|
|
@ -975,7 +970,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
|
||||
if vhost.ssl:
|
||||
# TODO: Make this based on addresses
|
||||
self._prepare_server_https(443)
|
||||
self._prepare_server_https("443")
|
||||
if self.save_notes:
|
||||
self.save("Enabled TLS for Apache")
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import os
|
|||
|
||||
from letsencrypt.plugins import common
|
||||
|
||||
from letsencrypt_apache import obj
|
||||
from letsencrypt_apache import parser
|
||||
|
||||
|
||||
|
|
@ -59,21 +60,6 @@ class ApacheDvsni(common.Dvsni):
|
|||
# About to make temporary changes to the config
|
||||
self.configurator.save()
|
||||
|
||||
addresses = []
|
||||
default_addr = "*:443"
|
||||
for achall in self.achalls:
|
||||
vhost = self.configurator.choose_vhost(achall.domain)
|
||||
|
||||
# TODO - @jdkasten review this code to make sure it makes sense
|
||||
self.configurator.make_server_sni_ready(vhost, default_addr)
|
||||
|
||||
for addr in vhost.addrs:
|
||||
if "_default_" == addr.get_addr():
|
||||
addresses.append([default_addr])
|
||||
break
|
||||
else:
|
||||
addresses.append(list(vhost.addrs))
|
||||
|
||||
responses = []
|
||||
|
||||
# Create all of the challenge certs
|
||||
|
|
@ -81,29 +67,37 @@ class ApacheDvsni(common.Dvsni):
|
|||
responses.append(self._setup_challenge_cert(achall))
|
||||
|
||||
# Setup the configuration
|
||||
self._mod_config(addresses)
|
||||
dvsni_addrs = self._mod_config()
|
||||
|
||||
self.configurator.make_addrs_sni_ready(dvsni_addrs)
|
||||
|
||||
# Prepare the server for HTTPS
|
||||
# TODO: Base on addresses
|
||||
self.configurator._prepare_https_server(443)
|
||||
self.configurator._prepare_https_server(
|
||||
str(self.configurator.config.dvsni_port))
|
||||
|
||||
# Save reversible changes
|
||||
self.configurator.save("SNI Challenge", True)
|
||||
|
||||
return responses
|
||||
|
||||
def _mod_config(self, ll_addrs):
|
||||
def _mod_config(self):
|
||||
"""Modifies Apache config files to include challenge vhosts.
|
||||
|
||||
Result: Apache config includes virtual servers for issued challs
|
||||
|
||||
:param list ll_addrs: list of list of `~.common.Addr` to apply
|
||||
:returns: All DVSNI addresses used
|
||||
:rtype: set
|
||||
|
||||
"""
|
||||
# TODO: Use ip address of existing vhost instead of relying on FQDN
|
||||
dvsni_addrs = set()
|
||||
config_text = "<IfModule mod_ssl.c>\n"
|
||||
for idx, lis in enumerate(ll_addrs):
|
||||
config_text += self._get_config_text(self.achalls[idx], lis)
|
||||
|
||||
for achall in self.achalls:
|
||||
achall_addrs = self.get_dvsni_addrs(achall)
|
||||
dvsni_addrs.update(achall_addrs)
|
||||
|
||||
config_text += self._get_config_text(self.achalls, achall_addrs)
|
||||
|
||||
config_text += "</IfModule>\n"
|
||||
|
||||
self._conf_include_check(self.configurator.parser.loc["default"])
|
||||
|
|
@ -113,6 +107,27 @@ class ApacheDvsni(common.Dvsni):
|
|||
with open(self.challenge_conf, "w") as new_conf:
|
||||
new_conf.write(config_text)
|
||||
|
||||
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.
|
||||
# TODO: Need to separate out test mode and normal mode for DVSNI addrs
|
||||
dvsni_addrs = set()
|
||||
default_addr = obj.Addr(("*", self.configurator.config.dvsni_port))
|
||||
|
||||
for addr in vhost.addrs:
|
||||
# I don't think there can be two _default_ namebasedvhosts
|
||||
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
|
||||
|
||||
def _conf_include_check(self, main_config):
|
||||
"""Adds DVSNI challenge conf file into configuration.
|
||||
|
||||
|
|
@ -136,7 +151,7 @@ class ApacheDvsni(common.Dvsni):
|
|||
:type achall: :class:`letsencrypt.achallenges.DVSNI`
|
||||
|
||||
:param list ip_addrs: addresses of challenged domain
|
||||
:class:`list` of type `~.common.Addr`
|
||||
:class:`list` of type `~.obj.Addr`
|
||||
|
||||
:returns: virtual host configuration text
|
||||
:rtype: str
|
||||
|
|
|
|||
|
|
@ -1,4 +1,38 @@
|
|||
"""Module contains classes used by the Apache Configurator."""
|
||||
from letsencrypt.plugins import common
|
||||
|
||||
class Addr(common.Addr):
|
||||
|
||||
def __eq__(self, other):
|
||||
"""This is defined as equalivalent within Apache.
|
||||
|
||||
ip_addr:* == ip_addr
|
||||
|
||||
"""
|
||||
if isinstance(other, self.__class__):
|
||||
return ((self.tup == other.tup) or
|
||||
(self.tup[0] == other.tup[0]
|
||||
and self.is_wildcard() and other.is_wildcard()))
|
||||
return False
|
||||
|
||||
def is_wildcard(self):
|
||||
return tup[1] == "*" or not tup[1]
|
||||
|
||||
def get_sni_addr(self, port):
|
||||
"""Returns the least specific address that resolves on the port.
|
||||
|
||||
Example:
|
||||
1.2.3.4:443 -> 1.2.3.4:<port>
|
||||
1.2.3.4:* -> 1.2.3.4:*
|
||||
|
||||
:param str port: Desired port
|
||||
|
||||
"""
|
||||
if self.is_wildcard():
|
||||
return self
|
||||
|
||||
return self.get_addr_obj(port)
|
||||
|
||||
|
||||
|
||||
class VirtualHost(object): # pylint: disable=too-few-public-methods
|
||||
|
|
|
|||
Loading…
Reference in a new issue