mirror of
https://github.com/certbot/certbot.git
synced 2026-06-03 13:59:02 -04:00
Remove redirect enhancement, fix reload
This commit is contained in:
parent
37649966c2
commit
33ff366171
1 changed files with 6 additions and 338 deletions
|
|
@ -68,7 +68,7 @@ class NginxConfigurator(object):
|
|||
self.parser = None
|
||||
self.version = version
|
||||
self.vhosts = None
|
||||
self._enhance_func = {"redirect": self._enable_redirect}
|
||||
self._enhance_func = {} # TODO: Support at least redirects
|
||||
|
||||
def prepare(self):
|
||||
"""Prepare the authenticator/installer."""
|
||||
|
|
@ -345,7 +345,7 @@ class NginxConfigurator(object):
|
|||
|
||||
def supported_enhancements(self): # pylint: disable=no-self-use
|
||||
"""Returns currently supported enhancements."""
|
||||
return ["redirect"]
|
||||
return []
|
||||
|
||||
def enhance(self, domain, enhancement, options=None):
|
||||
"""Enhance configuration.
|
||||
|
|
@ -367,270 +367,6 @@ class NginxConfigurator(object):
|
|||
except errors.LetsEncryptConfiguratorError:
|
||||
logging.warn("Failed %s for %s", enhancement, domain)
|
||||
|
||||
def _enable_redirect(self, ssl_vhost, unused_options):
|
||||
"""Redirect all equivalent HTTP traffic to ssl_vhost.
|
||||
|
||||
.. todo:: This enhancement should be rewritten and will
|
||||
unfortunately require lots of debugging by hand.
|
||||
|
||||
Adds Redirect directive to the port 80 equivalent of ssl_vhost
|
||||
First the function attempts to find the vhost with equivalent
|
||||
ip addresses that serves on non-ssl ports
|
||||
The function then adds the directive
|
||||
|
||||
.. note:: This function saves the configuration
|
||||
|
||||
:param ssl_vhost: Destination of traffic, an ssl enabled vhost
|
||||
:type ssl_vhost:
|
||||
:class:`~letsencrypt.client.plugins.nginx.obj.VirtualHost`
|
||||
|
||||
:param unused_options: Not currently used
|
||||
:type unused_options: Not Available
|
||||
|
||||
:returns: Success, general_vhost (HTTP vhost)
|
||||
:rtype: (bool,
|
||||
:class:`~letsencrypt.client.plugins.nginx.obj.VirtualHost`)
|
||||
|
||||
"""
|
||||
if not mod_loaded("rewrite_module", self.config.nginx_ctl):
|
||||
enable_mod("rewrite", self.config.nginx_init_script,
|
||||
self.config.nginx_enmod)
|
||||
|
||||
general_v = self._general_vhost(ssl_vhost)
|
||||
if general_v is None:
|
||||
# Add virtual_server with redirect
|
||||
logging.debug(
|
||||
"Did not find http version of ssl virtual host... creating")
|
||||
return self._create_redirect_vhost(ssl_vhost)
|
||||
else:
|
||||
# Check if redirection already exists
|
||||
exists, code = self._existing_redirect(general_v)
|
||||
if exists:
|
||||
if code == 0:
|
||||
logging.debug("Redirect already added")
|
||||
logging.info(
|
||||
"Configuration is already redirecting traffic to HTTPS")
|
||||
return
|
||||
else:
|
||||
logging.info("Unknown redirect exists for this vhost")
|
||||
raise errors.LetsEncryptConfiguratorError(
|
||||
"Unknown redirect already exists "
|
||||
"in {}".format(general_v.filep))
|
||||
# Add directives to server
|
||||
self.parser.add_dir(general_v.path, "RewriteEngine", "On")
|
||||
self.parser.add_dir(general_v.path, "RewriteRule",
|
||||
constants.APACHE_REWRITE_HTTPS_ARGS)
|
||||
self.save_notes += ("Redirecting host in %s to ssl vhost in %s\n" %
|
||||
(general_v.filep, ssl_vhost.filep))
|
||||
self.save()
|
||||
|
||||
logging.info("Redirecting vhost in %s to ssl vhost in %s",
|
||||
general_v.filep, ssl_vhost.filep)
|
||||
|
||||
def _existing_redirect(self, vhost):
|
||||
"""Checks to see if existing redirect is in place.
|
||||
|
||||
Checks to see if virtualhost already contains a rewrite or redirect
|
||||
returns boolean, integer
|
||||
The boolean indicates whether the redirection exists...
|
||||
The integer has the following code:
|
||||
0 - Existing letsencrypt https rewrite rule is appropriate and in place
|
||||
1 - Virtual host contains a Redirect directive
|
||||
2 - Virtual host contains an unknown RewriteRule
|
||||
|
||||
-1 is also returned in case of no redirection/rewrite directives
|
||||
|
||||
:param vhost: vhost to check
|
||||
:type vhost: :class:`~letsencrypt.client.plugins.nginx.obj.VirtualHost`
|
||||
|
||||
:returns: Success, code value... see documentation
|
||||
:rtype: bool, int
|
||||
|
||||
"""
|
||||
rewrite_path = self.parser.find_dir(
|
||||
parser.case_i("RewriteRule"), None, vhost.path)
|
||||
redirect_path = self.parser.find_dir(
|
||||
parser.case_i("Redirect"), None, vhost.path)
|
||||
|
||||
if redirect_path:
|
||||
# "Existing Redirect directive for virtualhost"
|
||||
return True, 1
|
||||
if not rewrite_path:
|
||||
# "No existing redirection for virtualhost"
|
||||
return False, -1
|
||||
if len(rewrite_path) == len(constants.APACHE_REWRITE_HTTPS_ARGS):
|
||||
for idx, match in enumerate(rewrite_path):
|
||||
if (self.aug.get(match) !=
|
||||
constants.APACHE_REWRITE_HTTPS_ARGS[idx]):
|
||||
# Not a letsencrypt https rewrite
|
||||
return True, 2
|
||||
# Existing letsencrypt https rewrite rule is in place
|
||||
return True, 0
|
||||
# Rewrite path exists but is not a letsencrypt https rule
|
||||
return True, 2
|
||||
|
||||
def _create_redirect_vhost(self, ssl_vhost):
|
||||
"""Creates an http_vhost specifically to redirect for the ssl_vhost.
|
||||
|
||||
:param ssl_vhost: ssl vhost
|
||||
:type ssl_vhost:
|
||||
:class:`~letsencrypt.client.plugins.nginx.obj.VirtualHost`
|
||||
|
||||
:returns: tuple of the form
|
||||
(`success`,
|
||||
:class:`~letsencrypt.client.plugins.nginx.obj.VirtualHost`)
|
||||
:rtype: tuple
|
||||
|
||||
"""
|
||||
# Consider changing this to a dictionary check
|
||||
# Make sure adding the vhost will be safe
|
||||
conflict, host_or_addrs = self._conflicting_host(ssl_vhost)
|
||||
if conflict:
|
||||
raise errors.LetsEncryptConfiguratorError(
|
||||
"Unable to create a redirection vhost "
|
||||
"- {}".format(host_or_addrs))
|
||||
|
||||
redirect_addrs = host_or_addrs
|
||||
|
||||
# get servernames and serveraliases
|
||||
serveralias = ""
|
||||
servername = ""
|
||||
size_n = len(ssl_vhost.names)
|
||||
if size_n > 0:
|
||||
servername = "ServerName " + ssl_vhost.names[0]
|
||||
if size_n > 1:
|
||||
serveralias = " ".join(ssl_vhost.names[1:size_n])
|
||||
serveralias = "ServerAlias " + serveralias
|
||||
redirect_file = ("<VirtualHost" + redirect_addrs + ">\n"
|
||||
"%s \n"
|
||||
"%s \n"
|
||||
"ServerSignature Off\n"
|
||||
"\n"
|
||||
"RewriteEngine On\n"
|
||||
"RewriteRule %s\n"
|
||||
"\n"
|
||||
"ErrorLog /var/log/nginx2/redirect.error.log\n"
|
||||
"LogLevel warn\n"
|
||||
"</VirtualHost>\n"
|
||||
% (servername, serveralias,
|
||||
" ".join(constants.APACHE_REWRITE_HTTPS_ARGS)))
|
||||
|
||||
# Write out the file
|
||||
# This is the default name
|
||||
redirect_filename = "le-redirect.conf"
|
||||
|
||||
# See if a more appropriate name can be applied
|
||||
if len(ssl_vhost.names) > 0:
|
||||
# Sanity check...
|
||||
# make sure servername doesn't exceed filename length restriction
|
||||
if ssl_vhost.names[0] < (255-23):
|
||||
redirect_filename = "le-redirect-%s.conf" % ssl_vhost.names[0]
|
||||
|
||||
redirect_filepath = os.path.join(
|
||||
self.parser.root, "sites-available", redirect_filename)
|
||||
|
||||
# Register the new file that will be created
|
||||
# Note: always register the creation before writing to ensure file will
|
||||
# be removed in case of unexpected program exit
|
||||
self.reverter.register_file_creation(False, redirect_filepath)
|
||||
|
||||
# Write out file
|
||||
with open(redirect_filepath, "w") as redirect_fd:
|
||||
redirect_fd.write(redirect_file)
|
||||
logging.info("Created redirect file: %s", redirect_filename)
|
||||
|
||||
self.aug.load()
|
||||
# Make a new vhost data structure and add it to the lists
|
||||
new_vhost = self._create_vhost(parser.get_aug_path(redirect_filepath))
|
||||
self.vhosts.append(new_vhost)
|
||||
|
||||
# Finally create documentation for the change
|
||||
self.save_notes += ("Created a port 80 vhost, %s, for redirection to "
|
||||
"ssl vhost %s\n" %
|
||||
(new_vhost.filep, ssl_vhost.filep))
|
||||
|
||||
def _conflicting_host(self, ssl_vhost):
|
||||
"""Checks for conflicting HTTP vhost for ssl_vhost.
|
||||
|
||||
Checks for a conflicting host, such that a new port 80 host could not
|
||||
be created without ruining the nginx config
|
||||
Used with redirection
|
||||
|
||||
returns: conflict, host_or_addrs - boolean
|
||||
if conflict: returns conflicting vhost
|
||||
if not conflict: returns space separated list of new host addrs
|
||||
|
||||
:param ssl_vhost: SSL Vhost to check for possible port 80 redirection
|
||||
:type ssl_vhost:
|
||||
:class:`~letsencrypt.client.plugins.nginx.obj.VirtualHost`
|
||||
|
||||
:returns: TODO
|
||||
:rtype: TODO
|
||||
|
||||
"""
|
||||
# Consider changing this to a dictionary check
|
||||
redirect_addrs = ""
|
||||
for ssl_a in ssl_vhost.addrs:
|
||||
# Add space on each new addr, combine "VirtualHost"+redirect_addrs
|
||||
redirect_addrs = redirect_addrs + " "
|
||||
ssl_a_vhttp = ssl_a.get_addr_obj("80")
|
||||
# Search for a conflicting host...
|
||||
for vhost in self.vhosts:
|
||||
if vhost.enabled:
|
||||
if (ssl_a_vhttp in vhost.addrs or
|
||||
ssl_a.get_addr_obj("") in vhost.addrs or
|
||||
ssl_a.get_addr_obj("*") in vhost.addrs):
|
||||
# We have found a conflicting host... just return
|
||||
return True, vhost
|
||||
|
||||
redirect_addrs = redirect_addrs + ssl_a_vhttp
|
||||
|
||||
return False, redirect_addrs
|
||||
|
||||
def _general_vhost(self, ssl_vhost):
|
||||
"""Find appropriate HTTP vhost for ssl_vhost.
|
||||
|
||||
Function needs to be thoroughly tested and perhaps improved
|
||||
Will not do well with malformed configurations
|
||||
Consider changing this into a dict check
|
||||
|
||||
:param ssl_vhost: ssl vhost to check
|
||||
:type ssl_vhost:
|
||||
:class:`~letsencrypt.client.plugins.nginx.obj.VirtualHost`
|
||||
|
||||
:returns: HTTP vhost or None if unsuccessful
|
||||
:rtype: :class:`~letsencrypt.client.plugins.nginx.obj.VirtualHost`
|
||||
or None
|
||||
|
||||
"""
|
||||
# _default_:443 check
|
||||
# Instead... should look for vhost of the form *:80
|
||||
# Should we prompt the user?
|
||||
ssl_addrs = ssl_vhost.addrs
|
||||
if ssl_addrs == obj.Addr.fromstring("_default_:443"):
|
||||
ssl_addrs = [obj.Addr.fromstring("*:443")]
|
||||
|
||||
for vhost in self.vhosts:
|
||||
found = 0
|
||||
# Not the same vhost, and same number of addresses
|
||||
if vhost != ssl_vhost and len(vhost.addrs) == len(ssl_vhost.addrs):
|
||||
# Find each address in ssl_host in test_host
|
||||
for ssl_a in ssl_addrs:
|
||||
for test_a in vhost.addrs:
|
||||
if test_a.get_addr() == ssl_a.get_addr():
|
||||
# Check if found...
|
||||
if (test_a.get_port() == "80" or
|
||||
test_a.get_port() == "" or
|
||||
test_a.get_port() == "*"):
|
||||
found += 1
|
||||
break
|
||||
# Check to make sure all addresses were found
|
||||
# and names are equal
|
||||
if (found == len(ssl_vhost.addrs) and
|
||||
vhost.names == ssl_vhost.names):
|
||||
return vhost
|
||||
return None
|
||||
|
||||
def get_all_certs_keys(self):
|
||||
"""Find all existing keys, certs from configuration.
|
||||
|
||||
|
|
@ -717,7 +453,7 @@ class NginxConfigurator(object):
|
|||
:rtype: bool
|
||||
|
||||
"""
|
||||
return nginx_restart(self.config.nginx_init_script)
|
||||
return nginx_restart(self.config.nginx_ctl)
|
||||
|
||||
def config_test(self): # pylint: disable=no-self-use
|
||||
"""Check the configuration of Nginx for errors.
|
||||
|
|
@ -863,82 +599,14 @@ class NginxConfigurator(object):
|
|||
self.restart()
|
||||
|
||||
|
||||
def enable_mod(mod_name, nginx_init_script, nginx_enmod):
|
||||
"""Enables module in Nginx.
|
||||
|
||||
Both enables and restarts Nginx so module is active.
|
||||
|
||||
:param str mod_name: Name of the module to enable.
|
||||
:param str nginx_init_script: Path to the Nginx init script.
|
||||
:param str nginx_enmod: Path to the Nginx a2enmod script.
|
||||
|
||||
"""
|
||||
try:
|
||||
# Use check_output so the command will finish before reloading
|
||||
# TODO: a2enmod is debian specific...
|
||||
subprocess.check_call(["sudo", nginx_enmod, mod_name], # TODO: sudo?
|
||||
stdout=open("/dev/null", "w"),
|
||||
stderr=open("/dev/null", "w"))
|
||||
nginx_restart(nginx_init_script)
|
||||
except (OSError, subprocess.CalledProcessError) as err:
|
||||
logging.error("Error enabling mod_%s", mod_name)
|
||||
logging.error("Exception: %s", err)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def mod_loaded(module, nginx_ctl):
|
||||
"""Checks to see if mod_ssl is loaded
|
||||
|
||||
Uses ``nginx_ctl`` to get loaded module list. This also effectively
|
||||
serves as a config_test.
|
||||
|
||||
:param str nginx_ctl: Path to nginx2ctl binary.
|
||||
|
||||
:returns: If ssl_module is included and active in Nginx
|
||||
:rtype: bool
|
||||
|
||||
"""
|
||||
try:
|
||||
proc = subprocess.Popen(
|
||||
[nginx_ctl, "-M"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
stdout, stderr = proc.communicate()
|
||||
|
||||
except (OSError, ValueError):
|
||||
logging.error(
|
||||
"Error accessing %s for loaded modules!", nginx_ctl)
|
||||
raise errors.LetsEncryptConfiguratorError(
|
||||
"Error accessing loaded modules")
|
||||
# Small errors that do not impede
|
||||
if proc.returncode != 0:
|
||||
logging.warn("Error in checking loaded module list: %s", stderr)
|
||||
raise errors.LetsEncryptMisconfigurationError(
|
||||
"Nginx is unable to check whether or not the module is "
|
||||
"loaded because Nginx is misconfigured.")
|
||||
|
||||
if module in stdout:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def nginx_restart(nginx_init_script):
|
||||
def nginx_restart(nginx_ctl):
|
||||
"""Restarts the Nginx Server.
|
||||
|
||||
:param str nginx_init_script: Path to the Nginx init script.
|
||||
|
||||
.. todo:: Try to use reload instead. (This caused timing problems before)
|
||||
|
||||
.. todo:: On failure, this should be a recovery_routine call with another
|
||||
restart. This will confuse and inhibit developers from testing code
|
||||
though. This change should happen after
|
||||
the NginxConfigurator has been thoroughly tested. The function will
|
||||
need to be moved into the class again. Perhaps
|
||||
this version can live on... for testing purposes.
|
||||
:param str nginx_ctl: Path to the Nginx binary.
|
||||
|
||||
"""
|
||||
try:
|
||||
proc = subprocess.Popen([nginx_init_script, "restart"],
|
||||
proc = subprocess.Popen([nginx_ctl, "-s", "reload"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
stdout, stderr = proc.communicate()
|
||||
|
|
|
|||
Loading…
Reference in a new issue