Mini Shell
Direktori : /usr/share/cagefs/ |
|
Current File : //usr/share/cagefs/cagefsreconfigure.py |
# -*- 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(); ]
# Exclude .cagefs folder from ISPManager backup [ install_ISPManager_directory_exclude(); ]
# Disable ASSP Deluxe for CageFS
# Configure php.ini path for ISP manager
# CXS configure [ configure_cxs(); ]
from __future__ import print_function
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from future import standard_library
standard_library.install_aliases()
from builtins import *
import itertools
import cldetectlib as detect
import os, pwd, shutil, sys
import xml.dom.minidom as xml
import subprocess
from pkg_resources import parse_version
import cagefslib
from cagefslib import touch
from cagefshooks import write_file_content
import configparser
import re
import secureio
from cagefs_ispmanager_lib import install_ispmanager_directory_exclude
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')
# 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
try:
os.chown(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 == 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 != 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 not runOnStartUp_value 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 != 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()
install_ispmanager_directory_exclude()
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()
Zerion Mini Shell 1.0