관리-도구
편집 파일: panel.py
import base64 import logging import os from binascii import Error as base64Error from pathlib import Path from typing import Dict, List, Sequence, Set, Tuple, Union from xml.etree import ElementTree from defence360agent.application.determine_hosting_panel import ( is_plesk_installed, ) from defence360agent.utils import OsReleaseInfo from .. import base from . import api from .api import list_docroots from .utils import PleskConfig PLESK_KEY_REGISTRY = "/etc/sw/keys/" PLESK_IMUNIFY360_PRODUCT_NAME = "ext-imunify360" TCP_PORTS_PLESK = base.TCP_PORTS_COMMON + ["953", "990", "8443", "8447"] logger = logging.getLogger(__name__) def _safe_get_text(node, tag) -> str: """Avoid AttributeError if tag not found""" _node = node.find(tag) if _node is not None: return _node.text return "" def _get_key_data(key) -> Tuple[str, str]: """Return product name and filename from key data""" filename = "" key_product_name = "" for data in key.findall("value/struct/member"): if _safe_get_text(data, "name") == "filename": filename = _safe_get_text(data, "value/string") if _safe_get_text(data, "name") == "key_product_name": key_product_name = _safe_get_text(data, "value/string") return key_product_name, filename class PleskException(base.PanelException): pass class Plesk(base.AbstractPanel): NAME = "Plesk" OPEN_PORTS = { "tcp": { "in": ["143", "465", "8880", "49152-65535"] + TCP_PORTS_PLESK, "out": ["113", "5224"] + TCP_PORTS_PLESK, }, "udp": { "in": ["20", "21", "53", "443"], "out": ["20", "21", "53", "113", "123"], }, } exception = PleskException @classmethod def is_installed(cls): return is_plesk_installed() @staticmethod async def version(): with open("/usr/local/psa/version", "r") as f: return f.read().split()[0] @base.ensure_valid_panel() async def enable_imunify360_plugin(self, name=None): pass @base.ensure_valid_panel() async def disable_imunify360_plugin(self, plugin_name=None): pass async def get_users(self) -> List[str]: """Returns a list of Plesk system users""" try: return await api.get_users() except base.PanelException as e: logger.error("Failed to get users: %s", e) return [] async def get_user_domains(self): """ :return: list: domains hosted on server via plesk """ return await api.get_domains() async def get_domain_to_owner(self): """ :return: domain to list of users pairs """ return await api.get_domain_to_user() async def get_user_to_email(self) -> Dict[str, str]: """ Returns dict with user to email pairs """ return await api.get_user_to_email() async def get_domains_per_user(self): """ :return: user to list of domains pairs """ return await api.get_user_to_domain() async def users_count(self) -> int: return await api.count_customers_with_subscriptions() @classmethod def get_modsec_config_path(cls): if OsReleaseInfo.id_like() & OsReleaseInfo.DEBIAN: return "/etc/apache2/mods-available/security2.conf" else: return "/etc/httpd/conf.d/security2.conf" def basedirs(self) -> Set[str]: basedir = PleskConfig("HTTPD_VHOSTS_D").get() return {basedir} if basedir else set() @classmethod def base_home_dir(cls, _) -> Path: # Local import to save memory on other panels from configparser import ConfigParser with open("/etc/psa/psa.conf") as c: text = "[dummy section]\n" + c.read() config = ConfigParser(delimiters=[" ", "\t"]) config.read_string(text) base_dir = Path( config["dummy section"].get("HTTPD_VHOSTS_D", "/var/www/vhosts") ) return base_dir @classmethod def _retrieve_key(cls) -> Union[str, None]: """Parse xml of registry and corresponding key file to retrive product key. return: str key or None if not found. """ registry = ElementTree.parse( os.path.join(PLESK_KEY_REGISTRY, "registry.xml") ) for member in registry.getroot().findall("struct/member"): if _safe_get_text(member, "name") == "active": for key in member.findall("value/struct/member"): key_product_name, filename = _get_key_data(key) if key_product_name == PLESK_IMUNIFY360_PRODUCT_NAME: key_value = _safe_get_text( ElementTree.parse( os.path.join( PLESK_KEY_REGISTRY, "keys", filename ) ), "{http://parallels.com/schemas/keys/aps/3}" "key-body", ) return base64.b64decode(key_value.encode()).decode() return None @classmethod async def retrieve_key(cls) -> str: """Returns registration key from registered keys, if possible, raise PleskException if not successful.""" try: result = cls._retrieve_key() except (ElementTree.ParseError, base64Error, FileNotFoundError) as e: raise PleskException("failed to retrieve key with error %s" % e) if result: logger.info("key retrieved %s", result) return result raise PleskException("The key not found") async def list_docroots(self): """ :return: dict docroot to domain """ docroot_domains_users = await api.list_docroots_domains_users() return { docroot: domain for docroot, domain, _ in docroot_domains_users }