def _choose_vhost_from_list(self, target_name): # Select a vhost from a list vhost = display_ops.select_vhost(target_name, self.vhosts) if vhost is None: logger.error( "No vhost exists with servername or alias of: %s. " "No vhost was selected. Please specify servernames " "in the Apache config", target_name) raise errors.PluginError("No vhost selected") elif not vhost.ssl: addrs = self._get_proposed_addrs(vhost, "443") # TODO: Conflicts is too conservative if not any(vhost.enabled and vhost.conflicts(addrs) for vhost in self.vhosts): vhost = self.make_vhost_ssl(vhost) else: logger.error( "The selected vhost would conflict with other HTTPS " "VirtualHosts within Apache. Please select another " "vhost or add ServerNames to your configuration.") raise errors.PluginError( "VirtualHost not able to be selected.") self.assoc[target_name] = vhost return vhost
def prepare(self): # pylint: disable=missing-docstring path_map = self.conf("map") if not path_map: raise errors.PluginError("--{0} must be set".format( self.option_name("path"))) for name, path in path_map.items(): if not os.path.isdir(path): raise errors.PluginError(path + " does not exist or is not a directory") self.full_roots[name] = os.path.join(path, challenges.HTTP01.URI_ROOT_PATH) logger.debug("Creating root challenges validation dir at %s", self.full_roots[name]) try: os.makedirs(self.full_roots[name]) # Set permissions as parent directory (GH #1389) # We don't use the parameters in makedirs because it # may not always work # https://stackoverflow.com/questions/5231901/permission-problems-when-creating-a-dir-with-os-makedirs-python stat_path = os.stat(path) filemode = stat.S_IMODE(stat_path.st_mode) os.chmod(self.full_roots[name], filemode) # Set owner and group, too os.chown(self.full_roots[name], stat_path.st_uid, stat_path.st_gid) except OSError as exception: if exception.errno != errno.EEXIST: raise errors.PluginError( "Couldn't create root for {0} http-01 " "challenge responses: {1}", name, exception)
def update_runtime_variables(self, ctl): """" .. note:: Compile time variables (apache2ctl -V) are not used within the dynamic configuration files. These should not be parsed or interpreted. .. todo:: Create separate compile time variables... simply for arg_get() """ stdout = self._get_runtime_cfg(ctl) variables = dict() matches = re.compile(r"Define: ([^ \n]*)").findall(stdout) try: matches.remove("DUMP_RUN_CFG") except ValueError: raise errors.PluginError("Unable to parse runtime variables") for match in matches: if match.count("=") > 1: logger.error("Unexpected number of equal signs in " "apache2ctl -D DUMP_RUN_CFG") raise errors.PluginError( "Error parsing Apache runtime variables") parts = match.partition("=") variables[parts[0]] = parts[2] self.variables = variables
def get_version(self): """Return version of Apache Server. Version is returned as tuple. (ie. 2.4.7 = (2, 4, 7)) :returns: version :rtype: tuple :raises .PluginError: if unable to find Apache version """ try: proc = subprocess.Popen([self.conf("ctl"), "-v"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) text = proc.communicate()[0] except (OSError, ValueError): raise errors.PluginError("Unable to run %s -v" % self.conf("ctl")) regex = re.compile(r"Apache/([0-9\.]*)", re.IGNORECASE) matches = regex.findall(text) if len(matches) != 1: raise errors.PluginError("Unable to find Apache version") return tuple([int(i) for i in matches[0].split(".")])
def prepare(self): """Prepare the plugin Get apikey and store in config """ self.api_key = self._api_key_from_args() or\ self._api_key_from_env() or\ self._api_key_from_gandi_cli() if not self.api_key: raise errors.PluginError("Api key is missing, couldn't found from " "neither gandi.cli, environment" "(GANDI_API_KEY), nor --{0}".format( self.option_name('api-key'))) self.shs_name = self.conf('name') if not self.shs_name: raise errors.PluginError("--{0} is a required parameter," "please provide a valid simple hosting " "name".format(self.option_name('name'))) self.vhost = self.conf('vhost') if not re.match('^(php|ruby|python)', self.shs_info['type']): raise errors.PluginError( "Sorry, only php and ruby instances are supported for now, " "we're doing our best to get everything supported with " "Let's Encrypt. Please check {0} for newer versions.".format( UPSTREAM_URL))
def _verify_no_redirects(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 :param vhost: vhost to check :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` :raises errors.PluginError: When another redirection exists """ rewrite_path = self.parser.find_dir( "RewriteRule", None, start=vhost.path) redirect_path = self.parser.find_dir("Redirect", None, start=vhost.path) if redirect_path: # "Existing Redirect directive for virtualhost" raise errors.PluginError("Existing Redirect present on HTTP vhost.") if rewrite_path: # "No existing redirection for virtualhost" if len(rewrite_path) != len(constants.REWRITE_HTTPS_ARGS): raise errors.PluginError("Unknown Existing RewriteRule") for match, arg in itertools.izip( rewrite_path, constants.REWRITE_HTTPS_ARGS): if self.aug.get(match) != arg: raise errors.PluginError("Unknown Existing RewriteRule") raise errors.PluginError( "Let's Encrypt has already enabled redirection")
def _get_root_path(self, achall): try: path = self.full_roots[achall.domain] except KeyError: raise errors.PluginError("Missing --webroot-path for domain: {0}" .format(achall.domain)) if not os.path.exists(path): raise errors.PluginError("Mysteriously missing path {0} for domain: {1}" .format(path, achall.domain)) return path
def _path_for_achall(self, achall): try: path = self.full_roots[achall.domain] except KeyError: raise errors.PluginError("Missing --webroot-path for domain: {0}" .format(achall.domain)) if not os.path.exists(path): raise errors.PluginError("Mysteriously missing path {0} for domain: {1}" .format(path, achall.domain)) return os.path.join(path, achall.chall.encode("token"))
def prepare(self): # pylint: disable=missing-docstring path_map = self.conf("map") if not path_map: raise errors.PluginError( "Missing parts of webroot configuration; please set either " "--webroot-path and --domains, or --webroot-map. Run with " " --help webroot for examples.") for name, path in path_map.items(): if not os.path.isdir(path): raise errors.PluginError( path + " does not exist or is not a directory") self.full_roots[name] = os.path.join( path, challenges.HTTP01.URI_ROOT_PATH) logger.debug("Creating root challenges validation dir at %s", self.full_roots[name]) # Change the permissions to be writable (GH #1389) # Umask is used instead of chmod to ensure the client can also # run as non-root (GH #1795) old_umask = os.umask(0o022) try: # This is coupled with the "umask" call above because # os.makedirs's "mode" parameter may not always work: # https://stackoverflow.com/questions/5231901/permission-problems-when-creating-a-dir-with-os-makedirs-python os.makedirs(self.full_roots[name], 0o0755) # Set owner as parent directory if possible try: stat_path = os.stat(path) os.chown(self.full_roots[name], stat_path.st_uid, stat_path.st_gid) except OSError as exception: if exception.errno == errno.EACCES: logger.debug( "Insufficient permissions to change owner and uid - ignoring" ) else: raise errors.PluginError( "Couldn't create root for {0} http-01 " "challenge responses: {1}", name, exception) except OSError as exception: if exception.errno != errno.EEXIST: raise errors.PluginError( "Couldn't create root for {0} http-01 " "challenge responses: {1}", name, exception) finally: os.umask(old_umask)
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 .PluginError: Unable to find Nginx version or version is unsupported """ try: proc = subprocess.Popen( [self.conf('ctl'), "-c", self.nginx_conf, "-V"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) text = proc.communicate()[1] # nginx prints output to stderr except (OSError, ValueError) as error: logging.debug(error, exc_info=True) raise errors.PluginError( "Unable to run %s -V" % self.conf('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.PluginError("Unable to find Nginx version") if not ssl_matches: raise errors.PluginError( "Nginx build is missing SSL module (--with-http_ssl_module).") if not sni_matches: raise errors.PluginError("Nginx build doesn't support SNI") nginx_version = tuple([int(i) for i in version_matches[0].split(".")]) # nginx < 0.8.48 uses machine hostname as default server_name instead of # the empty string if nginx_version < (0, 8, 48): raise errors.NotSupportedError("Nginx version must be 0.8.48+") return nginx_version
def install_cert(self): """Install certificate to the domain repository in Plesk.""" request = { 'packet': { 'certificate': { 'install': [{ 'name': self.cert_name() }, { 'site': self.domain }, { 'content': [ { 'csr': {} }, { 'pvt': self.key_data }, { 'cert': self.cert_data }, { 'ca': self.chain_data }, ] }] } } } response = self.plesk_api_client.request(request) api_result = response['packet']['certificate']['install']['result'] if 'ok' != api_result['status']: error_text = str(api_result['errtext']) raise errors.PluginError('Install certificate failure: %s' % error_text) self.cert_installed = True
def _perform_single(self, achall): # same path for each challenge response would be easier for # users, but will not work if multiple domains point at the # same server: default command doesn't support virtual hosts response, validation = achall.response_and_validation() port = (response.port if self.config.http01_port is None else int( self.config.http01_port)) command = self.CMD_TEMPLATE.format( root=self._root, achall=achall, response=response, # TODO(kuba): pipes still necessary? validation=pipes.quote(validation), encoded_token=achall.chall.encode("token"), port=port) if self.conf("test-mode"): logger.debug("Test mode. Executing the manual command: %s", command) # sh shipped with OS X does't support echo -n, but supports printf try: self._httpd = subprocess.Popen( command, # don't care about setting stdout and stderr, # we're in test mode anyway shell=True, executable=None, # "preexec_fn" is UNIX specific, but so is "command" preexec_fn=os.setsid) except OSError as error: # ValueError should not happen! logger.debug("Couldn't execute manual command: %s", error, exc_info=True) return False logger.debug("Manual command running as PID %s.", self._httpd.pid) # give it some time to bootstrap, before we try to verify # (cert generation in case of simpleHttpS might take time) self._test_mode_busy_wait(port) if self._httpd.poll() is not None: raise errors.Error("Couldn't execute manual command") else: if not self.conf("public-ip-logging-ok"): if not zope.component.getUtility(interfaces.IDisplay).yesno( self.IP_DISCLAIMER, "Yes", "No"): raise errors.PluginError( "Must agree to IP logging to proceed") self._notify_and_wait( self.MESSAGE_TEMPLATE.format(validation=validation, response=response, uri=achall.chall.uri( achall.domain), command=command)) if not response.simple_verify(achall.chall, achall.domain, achall.account_key.public_key(), self.config.http01_port): logger.warning("Self-verify of challenge failed.") return response
def get_arg(self, match): """Uses augeas.get to get argument value and interprets result. This also converts all variables and parameters appropriately. """ value = self.aug.get(match) # No need to strip quotes for variables, as apache2ctl already does # this, but we do need to strip quotes for all normal arguments. # Note: normal argument may be a quoted variable # e.g. strip now, not later value = value.strip("'\"") variables = ApacheParser.arg_var_interpreter.findall(value) for var in variables: # Strip off ${ and } try: value = value.replace(var, self.variables[var[2:-1]]) except KeyError: raise errors.PluginError("Error Parsing variable: %s" % var) return value
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_apache.obj.VirtualHost` :param unused_options: Not currently used :type unused_options: Not Available :returns: Success, general_vhost (HTTP vhost) :rtype: (bool, :class:`~letsencrypt_apache.obj.VirtualHost`) :raises .errors.PluginError: If no viable HTTP host can be created or used for the redirect. """ if "rewrite_module" not in self.parser.modules: self.enable_mod("rewrite") general_vh = self._get_http_vhost(ssl_vhost) if general_vh is None: # Add virtual_server with redirect logger.debug("Did not find http version of ssl virtual host " "attempting to create") redirect_addrs = self._get_proposed_addrs(ssl_vhost) for vhost in self.vhosts: if vhost.enabled and vhost.conflicts(redirect_addrs): raise errors.PluginError( "Unable to find corresponding HTTP vhost; " "Unable to create one as intended addresses conflict; " "Current configuration does not support automated " "redirection") self._create_redirect_vhost(ssl_vhost) else: # Check if redirection already exists self._verify_no_redirects(general_vh) # Add directives to server # Note: These are not immediately searchable in sites-enabled # even with save() and load() self.parser.add_dir(general_vh.path, "RewriteEngine", "on") self.parser.add_dir(general_vh.path, "RewriteRule", constants.REWRITE_HTTPS_ARGS) self.save_notes += ("Redirecting host in %s to ssl vhost in %s\n" % (general_vh.filep, ssl_vhost.filep)) self.save() logger.info("Redirecting vhost in %s to ssl vhost in %s", general_vh.filep, ssl_vhost.filep)
def _perform_single(self, achall): # same path for each challenge response would be easier for # users, but will not work if multiple domains point at the # same server: default command doesn't support virtual hosts response, validation = achall.gen_response_and_validation( tls=False) # SimpleHTTP TLS is dead: ietf-wg-acme/acme#7 port = (response.port if self.config.simple_http_port is None else int(self.config.simple_http_port)) command = self.CMD_TEMPLATE.format( root=self._root, achall=achall, response=response, validation=pipes.quote(validation.json_dumps()), encoded_token=achall.chall.encode("token"), ct=response.CONTENT_TYPE, port=port) if self.conf("test-mode"): logger.debug("Test mode. Executing the manual command: %s", command) # sh shipped with OS X does't support echo -n executable = "/bin/bash" if sys.platform == "darwin" else None try: self._httpd = subprocess.Popen( command, # don't care about setting stdout and stderr, # we're in test mode anyway shell=True, executable=executable, # "preexec_fn" is UNIX specific, but so is "command" preexec_fn=os.setsid) except OSError as error: # ValueError should not happen! logger.debug( "Couldn't execute manual command: %s", error, exc_info=True) return False logger.debug("Manual command running as PID %s.", self._httpd.pid) # give it some time to bootstrap, before we try to verify # (cert generation in case of simpleHttpS might take time) self._test_mode_busy_wait(port) if self._httpd.poll() is not None: raise errors.Error("Couldn't execute manual command") else: if not zope.component.getUtility(interfaces.IDisplay).yesno( self.IP_DISCLAIMER, "Yes", "No"): raise errors.PluginError("Must agree to IP logging to proceed") self._notify_and_wait(self.MESSAGE_TEMPLATE.format( validation=validation.json_dumps(), response=response, uri=response.uri(achall.domain, achall.challb.chall), ct=response.CONTENT_TYPE, command=command)) if response.simple_verify( achall.chall, achall.domain, achall.account_key.public_key(), self.config.simple_http_port): return response else: logger.error( "Self-verify of challenge failed, authorization abandoned.") if self.conf("test-mode") and self._httpd.poll() is not None: # simply verify cause command failure... return False return None
def shs_info(self): if not hasattr(self, 'api_key'): raise errors.PluginError("Api key is missing") if not hasattr(self, 'shs_name'): raise errors.PluginError("Simple hosting name is missing") if self._shs_info: return self._shs_info api = self._api() list = api.paas.list(self.api_key, {'name': self.shs_name}) if not list: raise errors.PluginError("Couldn't find any match for {0}".format( self.shs_name)) self._shs_info = api.paas.info(self.api_key, list[0]['id']) return self._shs_info
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_apache.obj.VirtualHost` :param unused_options: Not currently used :type unused_options: Not Available :returns: Success, general_vhost (HTTP vhost) :rtype: (bool, :class:`~letsencrypt_apache.obj.VirtualHost`) """ if not self.mod_loaded("rewrite_module"): self.enable_mod("rewrite") general_v = self._general_vhost(ssl_vhost) if general_v is None: # Add virtual_server with redirect logger.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: logger.debug("Redirect already added") logger.info( "Configuration is already redirecting traffic to HTTPS" ) return else: logger.info("Unknown redirect exists for this vhost") raise errors.PluginError("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.REWRITE_HTTPS_ARGS) self.save_notes += ("Redirecting host in %s to ssl vhost in %s\n" % (general_v.filep, ssl_vhost.filep)) self.save() logger.info("Redirecting vhost in %s to ssl vhost in %s", general_v.filep, ssl_vhost.filep)
def prepare(self): # pylint: disable=missing-docstring path = self.conf("path") if path is None: raise errors.PluginError("--{0} must be set".format( self.option_name("path"))) if not os.path.isdir(path): raise errors.PluginError(path + " does not exist or is not a directory") self.full_root = os.path.join(path, challenges.HTTP01.URI_ROOT_PATH) logger.debug("Creating root challenges validation dir at %s", self.full_root) try: os.makedirs(self.full_root) except OSError as exception: if exception.errno != errno.EEXIST: raise errors.PluginError( "Couldn't create root for http-01 " "challenge responses: {0}", exception)
def revert_challenge_config(self): """Used to cleanup challenge configurations. :raises .errors.PluginError: If unable to revert the challenge config. """ try: self.reverter.revert_temporary_config() except errors.ReverterError as err: raise errors.PluginError(str(err)) self.aug.load()
def view_config_changes(self): """Show all of the configuration changes that have taken place. :raises .errors.PluginError: If there is a problem while processing the checkpoints directories. """ try: self.reverter.view_config_changes() except errors.ReverterError as err: raise errors.PluginError(str(err))
def recovery_routine(self): """Revert all previously modified files. Reverts all modified files that have not been saved as a checkpoint :raises .errors.PluginError: If unable to recover the configuration """ try: self.reverter.recovery_routine() except errors.ReverterError as err: raise errors.PluginError(str(err)) self.parser.load()
def _validate_webroot(webroot_path): """Validates and returns the absolute path of webroot_path. :param str webroot_path: path to the webroot directory :returns: absolute path of webroot_path :rtype: str """ if not os.path.isdir(webroot_path): raise errors.PluginError(webroot_path + " does not exist or is not a directory") return os.path.abspath(webroot_path)
def save(self, title=None, temporary=False): """Saves all changes to the configuration files. :param str title: The title of the save. If a title is given, the configuration will be saved as a new checkpoint and put in a timestamped directory. :param bool temporary: Indicates whether the changes made will be quickly reversed in the future (ie. challenges) :raises .errors.PluginError: If there was an error in an attempt to save the configuration, or an error creating a checkpoint """ save_files = set(self.parser.parsed.keys()) try: # Create Checkpoint if temporary: self.reverter.add_to_temp_checkpoint( save_files, self.save_notes) else: self.reverter.add_to_checkpoint(save_files, self.save_notes) except errors.ReverterError as err: raise errors.PluginError(str(err)) self.save_notes = "" # Change 'ext' to something else to not override existing conf files self.parser.filedump(ext='') if title and not temporary: try: self.reverter.finalize_checkpoint(title) except errors.ReverterError as err: raise errors.PluginError(str(err)) return True
def choose_vhost(self, target_name): """Chooses a virtual host based on the given domain name. If there is no clear virtual host to be selected, the user is prompted with all available choices. :param str target_name: domain name :returns: ssl vhost associated with name :rtype: :class:`~letsencrypt_apache.obj.VirtualHost` :raises .errors.PluginError: If no vhost is available """ # Allows for domain names to be associated with a virtual host # Client isn't using create_dn_server_assoc(self, dn, vh) yet if target_name in self.assoc: return self.assoc[target_name] # Check for servernames/aliases for ssl hosts for vhost in self.vhosts: if vhost.ssl and target_name in vhost.names: self.assoc[target_name] = vhost return vhost # Checking for domain name in vhost address # This technique is not recommended by Apache but is technically valid target_addr = common.Addr((target_name, "443")) for vhost in self.vhosts: if target_addr in vhost.addrs: self.assoc[target_name] = vhost return vhost # Check for non ssl vhosts with servernames/aliases == "name" for vhost in self.vhosts: if not vhost.ssl and target_name in vhost.names: vhost = self.make_vhost_ssl(vhost) self.assoc[target_name] = vhost return vhost vhost = display_ops.select_vhost(target_name, self.vhosts) if vhost is not None: self.assoc[target_name] = vhost else: logger.error( "No vhost exists with servername or alias of: %s. " "No vhost was selected. Please specify servernames " "in the Apache config", target_name) raise errors.PluginError("No vhost selected") # TODO: Ask the user if they would like to add ServerName/Alias to VH return vhost
def get_version(self): """Return version of Apache Server. Version is returned as tuple. (ie. 2.4.7 = (2, 4, 7)) :returns: version :rtype: tuple :raises .PluginError: if unable to find Apache version """ try: stdout, _ = le_util.run_script([self.conf("ctl"), "-v"]) except errors.SubprocessError: raise errors.PluginError("Unable to run %s -v" % self.conf("ctl")) regex = re.compile(r"Apache/([0-9\.]*)", re.IGNORECASE) matches = regex.findall(stdout) if len(matches) != 1: raise errors.PluginError("Unable to find Apache version") return tuple([int(i) for i in matches[0].split(".")])
def prepare(self): """Prepare the plugin Get apikey and store in config """ self.api_key = self._api_key_from_args() or\ self._api_key_from_env() or\ self._api_key_from_gandi_cli() if not self.api_key: raise errors.PluginError("Api key is missing, couldn't found from " "neither gandi.cli, environment" "(GANDI_API_KEY), nor --{0}".format( self.option_name('api-key'))) self.shs_name = self.conf('name') if not self.shs_name: raise errors.PluginError("--{0} is a required parameter," "please provide a valid simple hosting " "name".format(self.option_name('name'))) self.vhost = self.conf('vhost')
def rollback_checkpoints(self, rollback=1): """Rollback saved checkpoints. :param int rollback: Number of checkpoints to revert :raises .errors.PluginError: If there is a problem with the input or the function is unable to correctly revert the configuration """ try: self.reverter.rollback_checkpoints(rollback) except errors.ReverterError as err: raise errors.PluginError(str(err)) self.aug.load()
def __call__(self, parser, namespace, webroot_path, option_string=None): if self._domain_before_webroot: raise errors.PluginError( "If you specify multiple webroot paths, " "one of them must precede all domain flags") if namespace.webroot_path: # Apply previous webroot to all matched # domains before setting the new webroot path prev_webroot = namespace.webroot_path[-1] for domain in namespace.domains: namespace.webroot_map.setdefault(domain, prev_webroot) elif namespace.domains: self._domain_before_webroot = True namespace.webroot_path.append(_validate_webroot(webroot_path))
def deploy_cert(self, domain, cert_path, key_path, chain_path=None, fullchain_path=None): if not fullchain_path: raise errors.PluginError( "The proxmox plugin currently requires --fullchain-path to " "install a cert.") logger.info("Copy certificate") copyfile(key_path, os.path.join(self.conf("location"), "pve-ssl.key")) copyfile(fullchain_path, os.path.join(self.conf("location"), "pve-ssl.pem"))
def assign_cert(self): """Assign certificate to the domain and enable SSL.""" request = { 'packet': { 'site': { 'set': [{ 'filter': { 'name': self.domain } }, { 'values': { 'hosting': { 'vrt_hst': [ { 'property': [ { 'name': 'ssl' }, { 'value': 'true' }, ] }, { 'property': [ { 'name': 'certificate_name' }, { 'value': self.cert_name() }, ] }, ] } } }] } } } response = self.plesk_api_client.request(request) api_result = response['packet']['site']['set']['result'] if 'ok' != api_result['status']: error_text = str(api_result['errtext']) raise errors.PluginError('Assign certificate failure: %s' % error_text) self.cert_assigned = True