File manager - Edit - /usr/share/cagefs/cagefsreconfigure.py
Back
# -*- coding: utf-8 -*- # # Copyright © Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2019 All Rights Reserved # # Licensed under CLOUD LINUX LICENSE AGREEMENT # http://cloudlinux.com/docs/LICENSE.TXT # List of Actions # # Create /etc/cagefs/cagefs.base.home.dirs file for Plesk [ create_plesk_base_home_dirs(); ] # PostGreSQL configure for CageFS [ postgresql_configure(); ] # LiteSpeed configure for CageFS [ litespeed_configure(); ] # Disable ASSP Deluxe for CageFS # CXS configure [ configure_cxs(); ] from builtins import * import itertools import cldetectlib as detect import os import pwd import shutil import stat import sys import xml.dom.minidom as xml import subprocess from packaging.version import parse as parse_version import cagefslib from cagefslib import touch from cagefshooks import write_file_content import configparser import re import secureio import cagefs_da_lib from clcagefslib.selector.configure import is_ea4_enabled, read_cpanel_ea4_php_conf from cagefsctl import PROXY_COMMANDS, check_cagefs_skeleton from clcommon.utils import mod_makedirs, get_file_lines, delete_line_from_file LITESPEED_XML = "" LSPHP5_PATH = "/usr/local/bin/lsphp" DEFAULT_POSTGRES_FOLDER = "/var/run/postgres" POSTGRES_CONF = ( "/etc/sysconfig/postgres" # file exists on CL5, CL6. the file does not exist on CL7 ) POSTGRES_CL7_FOLDER = "/var/run/postgresql" CXS_IGNORE_COMMAND = "hdir:/.cagefs" PLESK_BASE_HOME_DIRS_FILE = "/etc/cagefs/cagefs.base.home.dirs" PLESK_PHP_SESSION_DIR = "/var/lib/php/session" DA_SHARED_DIR = "/usr/local/directadmin/shared" DA_BIN = "/usr/local/directadmin/directadmin" # Plesk base home dirs def create_plesk_base_home_dirs(): if not detect.is_plesk(): return if os.path.isdir(PLESK_BASE_HOME_DIRS_FILE): secureio.print_error( 'Error: "{}" shouldn\'t be a folder'.format(PLESK_BASE_HOME_DIRS_FILE) ) return current_vhosts_d_regexp = "^{}/[^/]+\n".format(cagefslib.PLESK_VHOSTS_D) try: if os.path.exists(PLESK_BASE_HOME_DIRS_FILE): with open(PLESK_BASE_HOME_DIRS_FILE, "r+") as f: content = f.readlines() if current_vhosts_d_regexp not in content: f.write(current_vhosts_d_regexp) else: with open(PLESK_BASE_HOME_DIRS_FILE, "w") as f: f.write("mount_basedir=1\n") f.write(current_vhosts_d_regexp) os.chmod(PLESK_BASE_HOME_DIRS_FILE, 0o600) except (OSError, IOError) as e: secureio.print_error("Failed to write ", PLESK_BASE_HOME_DIRS_FILE, str(e)) BOX_TRAPPER_DIR = "/usr/local/cpanel/sys_cpanel/boxtrapper-message" def add_boxtrapper_dir_cpanel(): add_mount_to_cagefs_mp(BOX_TRAPPER_DIR, read_only=True) def add_php_session_dir_plesk(): add_mount_to_cagefs_mp(PLESK_PHP_SESSION_DIR, personal=True, perm="700") def add_mounts_for_clamav(): """ Add mounts related to ClamAV into cagefs.mp file. """ clamav_database_dirs = ("/usr/local/share/clamav", "/var/lib/clamav") for directory in clamav_database_dirs: add_mount_to_cagefs_mp(directory, read_only=True) def add_mounts_for_passenger(): """ Add mounts related to Phusion Passenger into cagefs.mp file. """ passenger_dirs = ( "/var/run/ea-passenger-runtime", "/usr/share/passenger", "/usr/libexec/passenger", ) for directory in passenger_dirs: add_mount_to_cagefs_mp(directory, read_only=False) def add_mount_to_cagefs_mp(line, read_only=False, personal=False, perm=""): if read_only and personal: raise ValueError("read_only and personal can`t be true in the same time") import cagefsctl if (personal or os.path.isdir(line)) and os.path.isfile("/etc/cagefs/cagefs.mp"): cagefsctl.check_mp_file() mp_config = cagefsctl.MountpointConfig( path=cagefsctl.ETC_MPFILE, skip_errors=True, skip_cpanel_check=True, ) if line + "\n" not in mp_config.common_mounts and line not in itertools.chain( mp_config.read_only_mounts, mp_config.personal_mounts, mp_config.splitted_by_username_mounts, mp_config.splitted_by_uid_mounts, ): if personal: line = "@" + line if perm: line = line + "," + perm elif read_only: line = "!" + line f = open("/etc/cagefs/cagefs.mp", "at") f.write(line.strip() + "\n") f.close() touch("/usr/share/cagefs/need.remount") def _chown_via_component_walk(path, uid, gid): """Chown ``path`` after opening it via an O_NOFOLLOW component-by- component walk anchored at ``/``. F-06 (CLOS-5944) DiD, follow-up to the AI-scanner HIGH: opening only the final component with O_NOFOLLOW still lets a writable ancestor (e.g. an attacker-controllable segment above the configured SOCK_DIR) redirect the resulting fd via an ancestor-symlink swap between path resolution and open. Walking every component with lstat + O_NOFOLLOW + O_DIRECTORY closes that window: a symlink at any depth causes the walk to raise before the fchown runs. Silently returns when the path does not yet exist (there is nothing to chown; the caller mod_makedirs above creates it first). Raises OSError on a non-directory or symlinked component. """ abs_path = os.path.abspath(path) components = [c for c in abs_path.split("/") if c] base_fd = os.open( "/", os.O_RDONLY | os.O_DIRECTORY | os.O_CLOEXEC ) # Every fd we open (base_fd + each per-component fd) goes into # ``opened`` IMMEDIATELY on open, so the ``finally`` closes it even # if a later lstat/S_ISLNK/S_ISDIR check raises, the ENOENT early # return fires, or fchown itself fails. Without this the descriptor # of the last successfully-opened component would leak on every # such path (Bugbot LOW follow-up on !200). opened = [base_fd] try: current_fd = base_fd for comp in components: try: st = os.stat(comp, dir_fd=current_fd, follow_symlinks=False) except FileNotFoundError: return if stat.S_ISLNK(st.st_mode): raise PermissionError("Operation not permitted.") if not stat.S_ISDIR(st.st_mode): raise PermissionError("Operation not permitted.") fd = os.open( comp, os.O_RDONLY | os.O_NOFOLLOW | os.O_DIRECTORY | os.O_CLOEXEC, dir_fd=current_fd, ) opened.append(fd) current_fd = fd os.fchown(current_fd, uid, gid) finally: for fd in opened: try: os.close(fd) except OSError: pass # PostGreSQL configure for CageFS def postgresql_configure(): if detect.detect_postgresql(): if not os.path.isfile(POSTGRES_CONF): add_mount_to_cagefs_mp(POSTGRES_CL7_FOLDER) return True postgres_folder = detect.get_param_from_file(POSTGRES_CONF, "SOCK_DIR", "=") if postgres_folder == "": postgres_folder = DEFAULT_POSTGRES_FOLDER elif postgres_folder != DEFAULT_POSTGRES_FOLDER: try: os.symlink(postgres_folder, DEFAULT_POSTGRES_FOLDER) except (OSError,) as e: secureio.print_error( "failed to create symlink", DEFAULT_POSTGRES_FOLDER, str(e) ) return False try: pg_user = pwd.getpwnam("postgres") except (KeyError,) as e: secureio.print_error("failed to find 'postgres' user", str(e)) return False postgres_uid = pg_user.pw_uid postgres_gid = pg_user.pw_gid if not os.path.lexists(postgres_folder): try: mod_makedirs(postgres_folder, 0o755) except (OSError,) as e: secureio.print_error("failed to create", postgres_folder, str(e)) return False # F-06 (CLOS-5944) DiD: os.chown() follows symlinks, so a # postgres-writable (or otherwise attacker-writable) parent # anywhere along the configured SOCK_DIR could replace a # segment with a symlink and have an unrelated root-owned # target chowned to postgres between the lexists() check above # and the chown here. Walk every path component with lstat + # O_NOFOLLOW + O_DIRECTORY anchored at "/" (see # _chown_via_component_walk) so a symlink at any depth aborts # the walk before the fchown runs. Follow-up to the initial # F-06 fix, which only pinned the final component. try: _chown_via_component_walk(postgres_folder, postgres_uid, postgres_gid) except (OSError,) as e: secureio.print_error( "failed to change owner of", postgres_folder, str(e) ) return False add_mount_to_cagefs_mp(DEFAULT_POSTGRES_FOLDER) return True return False ################################ LITESPEED SECTION ################################ # Open LiteSpeed XML config def litespeed_config_read(): global LITESPEED_XML if LITESPEED_XML == "": try: LITESPEED_XML = xml.parse(detect.LITESPEED_CONFIG_FILE) except: import traceback print(traceback.format_exc(), file=sys.stderr) secureio.print_error("bad " + detect.LITESPEED_CONFIG_FILE + " file") sys.exit(1) # Write LiteSpeed XML config def litespeed_config_write(): if detect.detect_enterprise_litespeed(): shutil.copyfile( detect.LITESPEED_CONFIG_FILE, detect.LITESPEED_CONFIG_FILE + ".cagefs" ) f = open(detect.LITESPEED_CONFIG_FILE, "wb") f.write(LITESPEED_XML.toprettyxml(indent="", newl="", encoding="UTF-8")) f.close() # LiteSpeed enableLVE configure def litespeed_enableLVE_configure(force_value=None): if detect.detect_enterprise_litespeed(): litespeed_config_read() if force_value is None: new_value = 2 else: new_value = force_value try: enableLVE_value = ( LITESPEED_XML.getElementsByTagName("httpServerConfig")[0] .getElementsByTagName("enableLVE")[0] .firstChild.nodeValue ) result = enableLVE_value if force_value is not None or int(enableLVE_value) < 2: LITESPEED_XML.getElementsByTagName("httpServerConfig")[ 0 ].getElementsByTagName("enableLVE")[0].firstChild.nodeValue = str( new_value ) result = new_value except IndexError: enableLVE = LITESPEED_XML.createElement("enableLVE") enableLVE_value = LITESPEED_XML.createTextNode(str(new_value)) enableLVE.appendChild(enableLVE_value) LITESPEED_XML.getElementsByTagName("httpServerConfig")[0].appendChild( enableLVE ) result = new_value return result # LiteSpeed phpSuExec configure def litespeed_phpSuExec_configure(): if detect.detect_enterprise_litespeed(): litespeed_config_read() try: phpSuExec_value = ( LITESPEED_XML.getElementsByTagName("httpServerConfig")[0] .getElementsByTagName("phpSuExec")[0] .firstChild.nodeValue ) if int(phpSuExec_value) != 1: LITESPEED_XML.getElementsByTagName("httpServerConfig")[ 0 ].getElementsByTagName("phpSuExec")[0].firstChild.nodeValue = str(1) except IndexError: phpSuExec = LITESPEED_XML.createElement("phpSuExec") phpSuExec_value = LITESPEED_XML.createTextNode(str(1)) phpSuExec.appendChild(phpSuExec_value) LITESPEED_XML.getElementsByTagName("httpServerConfig")[0].appendChild( phpSuExec ) def check_extProcessorList(): """ CAG-914 Check if there is extProcessorList section If not, print doc link to configure LiteSpeed Starting from LSWS v5.3 external apps and script handlers are no longer required. """ litespeed_version = detect.get_litespeed_version() check = LITESPEED_XML.getElementsByTagName("httpServerConfig")[ 0 ].getElementsByTagName("extProcessorList") # Show warning message about configuration only if version of LSWS is lower than 5.3 if not check and parse_version(litespeed_version) < parse_version("5.3"): print( "No extProcessorList section in file:\n" + detect.LITESPEED_CONFIG_FILE + "\nPlease configure LiteSpeed Web Server " + "using Web Admin Console as discribed here:\n" + "https://docs.cloudlinux.com/php_selector/#litespeed-support", file=sys.stderr, ) return check # LiteSpeed runOnStartUp configure def litespeed_runOnStartUp_configure(): if detect.detect_enterprise_litespeed(): litespeed_config_read() try: check_nodelist = check_extProcessorList() if not check_nodelist: return True nodelist = check_nodelist[0].getElementsByTagName("extProcessor") for node in nodelist: if ( str(node.getElementsByTagName("name")[0].firstChild.nodeValue) == "lsphp5" ): try: if ( node.getElementsByTagName("runOnStartUp")[0].firstChild != None ): runOnStartUp_value = str( node.getElementsByTagName("runOnStartUp")[ 0 ].firstChild.nodeValue ) if runOnStartUp_value not in ("1", "0"): node.getElementsByTagName("runOnStartUp")[ 0 ].firstChild.nodeValue = str(0) except IndexError: runOnStartUp = LITESPEED_XML.createElement("runOnStartUp") runOnStartUp_value = LITESPEED_XML.createTextNode(str(0)) runOnStartUp.appendChild(runOnStartUp_value) node.appendChild(runOnStartUp) except IndexError: import traceback print(traceback.format_exc(), file=sys.stderr) secureio.print_error("bad " + detect.LITESPEED_CONFIG_FILE + " file") sys.exit(1) # LiteSpeed extProcessorList->path change for php5 def litespeed_lsphp5_path_change(lsphp5_path=LSPHP5_PATH): """ LiteSpeed extProcessorList->path change for php5 """ if detect.detect_enterprise_litespeed(): litespeed_config_read() try: check_nodelist = check_extProcessorList() if not check_nodelist: return True nodelist = check_nodelist[0].getElementsByTagName("extProcessor") for node in nodelist: if ( str(node.getElementsByTagName("name")[0].firstChild.nodeValue) == "lsphp5" ): node.getElementsByTagName("path")[0].firstChild.nodeValue = str( lsphp5_path ) except IndexError: import traceback print(traceback.format_exc(), file=sys.stderr) secureio.print_error("bad " + detect.LITESPEED_CONFIG_FILE + " file") sys.exit(1) return True else: return False def custombuild_set_cagefs(option): """ Perform '/usr/local/directadmin/custombuild/build set cagefs' command """ try: subprocess.run( ["/usr/local/directadmin/custombuild/build", "set", "cagefs", option], check=True, ) except subprocess.CalledProcessError: return def custombuild_rewrite_confs(): """ Perform '/usr/local/directadmin/custombuild/build rewrite_confs' command """ try: subprocess.run( ["/usr/local/directadmin/custombuild/build", "rewrite_confs"], check=True, ) except subprocess.CalledProcessError: return # LiteSpeed configure for selector def litespeed_configure_selector(): try: litespeed_lsphp5_path_change() litespeed_enableLVE_configure(2) litespeed_phpSuExec_configure() litespeed_runOnStartUp_configure() litespeed_config_write() return True except: return False def configure_open_litespeed(): """ OpenLitespeed configure for CageFS on DirectAdmin """ if detect.detect_open_litespeed() and detect.is_da(): custombuild_set_cagefs("yes") custombuild_rewrite_confs() return True return False # LiteSpeed configure for CageFS def litespeed_configure(): try: if int(litespeed_enableLVE_configure()) <= 2: litespeed_phpSuExec_configure() litespeed_config_write() return True except: return False ################################ END LITESPEED SECTION ################################ def disable_cagefs_for_assp_deluxe(): config_file = "/usr/local/assp/assp.cfg" if not detect.is_cpanel() or not os.path.isfile(config_file): return try: adm_domain = detect.get_param_from_file(config_file, "sendAllSpam", ":=") parts = adm_domain.split("@") if len(parts) == 2: # adm email found adm_domain = parts[1] # adm domain not found if adm_domain == "": return # 2. Call /scripts/whoowns <admDomain>, determine user p = subprocess.Popen( ["/scripts/whoowns", adm_domain], shell=False, stdin=open("/dev/null"), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True, cwd="/scripts/", text=True, ) user_name = p.stdout.read() # 3. create file /etc/cagefs/exclude/assp-deluxe with the name of the resulting user f = open("/etc/cagefs/exclude/assp-deluxe", "w") f.write(user_name + "\n") f.close() os.chmod("/etc/cagefs/exclude/assp-deluxe", 0o600) except (OSError, IOError) as e: secureio.print_error("Failed to disable CageFS for ASSP Deluxe: ", str(e)) def get_native_settings(param_name): """ Return parameter and its value from native php.ini :param param_name: name of parameter in php.ini :type param_name: string """ cagefslib.read_native_conf() ini_file = cagefslib.orig_binaries["php.ini"] if is_ea4_enabled() and not os.path.isfile(ini_file): conf = read_cpanel_ea4_php_conf() if conf: try: # get default system php version selected via MultiPHP Manager in cPanel WHM default_php = conf["default"] except KeyError: return [] # ignore php.ini parameters whensystem default version is alt-php if not default_php.startswith("ea-php"): return [] ini_file = "/opt/cpanel/{}/root/etc/php.ini".format(default_php) if os.path.isfile(ini_file): return [(param_name, detect.get_param_from_file(ini_file, param_name, "="))] secureio.print_error("failed to open file", str(ini_file)) return [] def get_global_php_settings(): """ Return settings from global_php.ini """ GLOBAL_INI = "/etc/cl.selector/global_php.ini" settings = [] if os.path.isfile(GLOBAL_INI): f = open(GLOBAL_INI) cfg = configparser.ConfigParser(interpolation=None, strict=False) try: cfg.readfp(f) except configparser.Error: return [] for sect in cfg.sections(): settings.extend(cfg.items(sect)) f.close() return settings def reconfigure_alt_settings(settings, overwrite_list=None): """ Replace or add settings in php.ini for all alt-php versions :param settings: list of tuples like [('date.timezone', 'Europe/Moscow'), ('error_log', 'error_log')] :param overwrite_list: list of options to overwrite; empty list [] == overwrite all the two options ('error_log', 'date.timezone') None == do not overwrite the options """ if not settings: return if overwrite_list == []: overwrite_list = ["error_log", "date.timezone"] elif overwrite_list is None: overwrite_list = [] for alt_dir in cagefslib.get_alt_dirs(): path = os.path.join("/opt/alt/", alt_dir, "etc/php.ini") if os.path.isfile(path): f = open(path, "r") text = f.readlines() for setting in settings: changed = False prog = re.compile(";*\s*" + setting[0] + "\s*=.*") for i in reversed(range(len(text))): # pylint: disable=range-builtin-not-iterating result = prog.match(text[i]) if result: if changed: if text[i][0] != ";": text[i] = ";" + text[i] + "\n" else: param, value = text[i].split("=", 1) if ( param.strip() not in ["error_log", "date.timezone"] or not value.strip() or param.strip() in overwrite_list ): text[i] = setting[0] + " = " + setting[1] + "\n" changed = True if not changed: text.append(setting[0] + " = " + setting[1] + "\n") f.close() f = open(path, "w") f.writelines(text) f.close() else: secureio.print_error("failed to open file", str(path)) def replace_alt_settings(options=None): """ Replace or add settings in php.ini for all alt-php versions :param options: list of options to overwrite; empty list [] == overwrite all the two options ('error_log', 'date.timezone') None == do not overwrite the options :type options: list """ overwrite_list = None if options is not None: overwrite_list = [] if options: for arg in options: s = set(arg.split(",")) if not s.issubset(["error_log", "date.timezone"]): print( "Error: incorrect parameter of --apply-global-php-ini option:" ) print( "Please use 0, 1 or 2 parameters from the list: error_log, date.timezone" ) print( "using --apply-global-php-ini without arguments applies all global php options including two above" ) sys.exit(1) overwrite_list.extend(s) settings = get_global_php_settings() if len([x for x in settings if x[0] == "error_log"]) == 0: settings.extend(get_native_settings("error_log")) if len([x for x in settings if x[0] == "date.timezone"]) == 0: settings.extend(get_native_settings("date.timezone")) reconfigure_alt_settings(settings, overwrite_list) # CXS hook install into /etc/cxs def configure_cxs(): if not detect.CXS_check(): return try: # Add exclusions for CageFS to /etc/cxs/cxs.ignore if os.path.isfile("/etc/cxs/cxs.ignore"): f = open("/etc/cxs/cxs.ignore", "r") content = f.readlines() f.close() CXS_HOOK_INSTALLED = False for line in content: if line.find(CXS_IGNORE_COMMAND) != -1: CXS_HOOK_INSTALLED = True break if not CXS_HOOK_INSTALLED: content.append("\n" + CXS_IGNORE_COMMAND + "\n") write_file_content("/etc/cxs/cxs.ignore", content) else: write_file_content("/etc/cxs/cxs.ignore", CXS_IGNORE_COMMAND + "\n") os.chmod("/etc/cxs/cxs.ignore", 0o644) # Modify or Create /etc/cxs/cxs.default if os.path.isfile("/etc/cxs/cxs.default"): f = open("/etc/cxs/cxs.default", "r") content = f.readlines() f.close() IGNORE_HOOK_INSTALLED = False for line in content: if line.strip().startswith("ignore=/etc/cxs/cxs.ignore"): IGNORE_HOOK_INSTALLED = True break if not IGNORE_HOOK_INSTALLED: content.append("\n" + "ignore=/etc/cxs/cxs.ignore" + "\n") write_file_content("/etc/cxs/cxs.default", content) else: write_file_content("/etc/cxs/cxs.default", "ignore=/etc/cxs/cxs.ignore\n") os.chmod("/etc/cxs/cxs.default", 0o644) except (OSError, IOError) as e: secureio.print_error("Failed to change CXS configuration for CageFS: ", str(e)) def _add_da_shared_dir_to_cagefs_mp(): """ Create '/usr/local/directadmin/shared' directory if it doesn't already exist and add a corresponding line into the '/etc/cagefs/cagefs.mp' file. This directory is intended for storing files available to all users. Recent versions of DA panel will create this directory themselves, creation of the directory in our code is made for backwards compatibility with older DA panels. """ if os.path.exists(DA_SHARED_DIR): add_mount_to_cagefs_mp(DA_SHARED_DIR) else: try: mod_makedirs(DA_SHARED_DIR, 0o755) except OSError as e: secureio.print_error("failed to create", DA_SHARED_DIR, str(e)) else: add_mount_to_cagefs_mp(DA_SHARED_DIR) def _configure_proxy_commands_file_for_da(): """ Add a line containing the path to DA binary into the 'proxy.commands' file if DA panel version < 1.62.8. If DA panel version >= 1.62.8 remove this line. Starting '1.62.8' version DA no longer supports having suid bit set on DA binary, version < 1.62.8 means DA binary have suid bit, it should be executed via proxyexec, hence, we should add a line that contains path to DA binary into the 'proxy.commands' file if it is not already there. """ da_binary_line = f"DIRECTADMIN={DA_BIN}" detect.getCP() if parse_version(detect.CP_VERSION) < parse_version("1.62.8"): # check if path to DA binary is already in 'proxy.commands' file # if not, add it into the file try: proxy_command_lines = get_file_lines(PROXY_COMMANDS) if da_binary_line not in (line.strip() for line in proxy_command_lines): if proxy_command_lines: proxy_command_lines[-1] = proxy_command_lines[-1].strip() + "\n" proxy_command_lines.append(da_binary_line + "\n") secureio.write_file_via_tempfile( "".join(proxy_command_lines), PROXY_COMMANDS, 0o600 ) except OSError as e: secureio.print_error( "failed to add", da_binary_line, "into", PROXY_COMMANDS, str(e) ) else: try: delete_line_from_file(PROXY_COMMANDS, da_binary_line) except OSError as e: secureio.print_error( "failed to remove", da_binary_line, "from", PROXY_COMMANDS, str(e) ) if check_cagefs_skeleton(): # CAG-1182: update /usr/loca/directadmin/directadmin binary in CageFS while update of cagefs package subprocess.run( ["/usr/sbin/cagefsctl", "--wait-lock", "--update-list"], input=f"{DA_BIN}\n", text=True, ) def directadmin_configure(): """ Configure CageFs for Directadmin panel. """ if not detect.is_da(): return _add_da_shared_dir_to_cagefs_mp() _configure_proxy_commands_file_for_da() def reconfigure_cagefs(): import cagefsctl if postgresql_configure(): cagefslib.detect_postgres() create_plesk_base_home_dirs() disable_cagefs_for_assp_deluxe() cagefs_da_lib.configure_selector_for_directadmin() configure_cxs() # Apply changes to exclusions list cagefsctl.check_exclude() # Add the spamassassin directories to CageFs in cPanel if detect.is_cpanel(): cagefsctl.add_spamassassin_dirs_cpanel() add_boxtrapper_dir_cpanel() if detect.is_plesk(): add_php_session_dir_plesk() if detect.is_da(): directadmin_configure() cagefsctl.add_mount_for_php_apm() cagefsctl.add_mounts_for_ea_php_sessions() # CAG-706: add mount for emulation of /var/run/utmp inside CageFS add_mount_to_cagefs_mp(cagefslib.VAR_RUN_CAGEFS, personal=True, perm="700") add_mounts_for_clamav() add_mounts_for_passenger()
| ver. 1.4 |
Github
|
.
| PHP 8.1.34 | Generation time: 0.04 |
proxy
|
phpinfo
|
Settings