def get_version(self): # pylint: disable=no-self-use """Return version of Apache Server. Version is returned as tuple. (ie. 2.4.7 = (2, 4, 7)) :returns: version :rtype: tuple :raises errors.LetsEncryptConfiguratorError: Unable to find Apache version """ try: proc = subprocess.Popen( [self.config.apache_ctl, '-v'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) text = proc.communicate()[0] except (OSError, ValueError): raise errors.LetsEncryptConfiguratorError( "Unable to run %s -v" % self.config.apache_ctl) regex = re.compile(r"Apache/([0-9\.]*)", re.IGNORECASE) matches = regex.findall(text) if len(matches) != 1: raise errors.LetsEncryptConfiguratorError( "Unable to find Apache version") return tuple([int(i) for i in matches[0].split('.')])
def get_version(self): """Return version of Nginx Server. Version is returned as tuple. (ie. 2.4.7 = (2, 4, 7)) :returns: version :rtype: tuple :raises errors.LetsEncryptConfiguratorError: Unable to find Nginx version or version is unsupported """ try: proc = subprocess.Popen( [self.config.nginx_ctl, "-V"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) text = proc.communicate()[1] # nginx prints output to stderr except (OSError, ValueError): raise errors.LetsEncryptConfiguratorError( "Unable to run %s -V" % self.config.nginx_ctl) version_regex = re.compile(r"nginx/([0-9\.]*)", re.IGNORECASE) version_matches = version_regex.findall(text) sni_regex = re.compile(r"TLS SNI support enabled", re.IGNORECASE) sni_matches = sni_regex.findall(text) ssl_regex = re.compile(r" --with-http_ssl_module") ssl_matches = ssl_regex.findall(text) if not version_matches: raise errors.LetsEncryptConfiguratorError( "Unable to find Nginx version") if not ssl_matches: raise errors.LetsEncryptConfiguratorError( "Nginx build is missing SSL module (--with-http_ssl_module).") if not sni_matches: raise errors.LetsEncryptConfiguratorError( "Nginx build doesn't support SNI") nginx_version = tuple([int(i) for i in version_matches[0].split(".")]) # nginx < 0.8.21 doesn't use default_server if nginx_version < (0, 8, 21): raise errors.LetsEncryptConfiguratorError( "Nginx version must be 0.8.21+") return nginx_version
def mod_loaded(module, apache_ctl): """Checks to see if mod_ssl is loaded Uses ``apache_ctl`` to get loaded module list. This also effectively serves as a config_test. :param str apache_ctl: Path to apache2ctl binary. :returns: If ssl_module is included and active in Apache :rtype: bool """ try: proc = subprocess.Popen( [apache_ctl, '-M'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = proc.communicate() except (OSError, ValueError): logging.error( "Error accessing %s for loaded modules!", apache_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( "Apache is unable to check whether or not the module is " "loaded because Apache is misconfigured.") if module in stdout: return True return False
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.apache.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.apache.obj.VirtualHost`) """ if not mod_loaded("rewrite_module", self.config.apache_ctl): enable_mod("rewrite", self.config.apache_init_script, self.config.apache_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 enhance(self, domain, enhancement, options=None): """Enhance configuration. :param str domain: domain to enhance :param str enhancement: enhancement type defined in :const:`~letsencrypt.client.constants.ENHANCEMENTS` :param options: options for the enhancement See :const:`~letsencrypt.client.constants.ENHANCEMENTS` documentation for appropriate parameter. """ try: return self._enhance_func[enhancement](self.choose_vhost(domain), options) except (KeyError, ValueError): raise errors.LetsEncryptConfiguratorError( "Unsupported enhancement: {0}".format(enhancement)) except errors.LetsEncryptConfiguratorError: logging.warn("Failed %s for %s", enhancement, domain)
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.apache.obj.VirtualHost` :returns: Success, vhost :rtype: (bool, :class:`letsencrypt.client.apache.obj.VirtualHost`) """ # 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/apache2/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))