Mini Shell
#!/opt/cloudlinux/venv/bin/python3 -bb
# coding=utf-8
#
# Copyright © Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2018 All Rights Reserved
#
# Licensed under CLOUD LINUX LICENSE AGREEMENT
# http://cloudlinux.com/docs/LICENCE.TXT
#
import datetime
import getopt
import socket
import sys
import os
import stat
import tempfile
import subprocess
import urllib.request
from email import message_from_string
from email.mime.text import MIMEText
from typing import AnyStr # NOQA
from clcommon.utils import is_root_or_exit
from clcommon.mail_helper import MailHelper, MailSendFailed
import cldetectlib as detect
from cldiaglib import (
runner,
fake_cagefs_checker,
check_cp_diag,
check_symlinksifowner,
check_suexec,
check_suphp,
check_use_pam,
check_symlinkowngid,
check_existence_of_all_users_packages,
check_da_resellers_packages_files,
check_phpselector,
check_php_conf,
check_defaults_cfg,
check_lve_limits,
check_cagefs_partition_disk_quota,
is_email_notification_enabled,
check_domains_compatibility,
check_hidepid,
get_list_of_disabled_cron_checkers,
set_list_of_disabled_cron_checkers,
cron_cldiag_checkers_param_name,
check_jwt_token,
check_cl_plus_sender_service,
check_node_exporter_service,
check_cmt_packages,
check_lvestats_service,
check_low_pmem_limits
)
CAGEFS_SOURCES = '/usr/share/cagefs'
CAGEFS_INSTALLED = detect.is_cagefs_installed()
if CAGEFS_INSTALLED:
sys.path.append(CAGEFS_SOURCES)
try:
from sanity_check import (
check_cagefs_mount_points_exists,
check_cagefs_enabled_users_isdir,
check_cagefs_disabled_users_isdir,
check_cagefs_disabled_etcfs_exists,
check_users_can_enter_cagefs,
check_proxy_commands_configs_are_parsable,
check_all_virt_mp_files_syntax,
)
except ImportError:
check_cagefs_mount_points_exists = None
check_cagefs_enabled_users_isdir = None
check_cagefs_disabled_users_isdir = None
check_cagefs_disabled_etcfs_exists = None
check_users_can_enter_cagefs = None
check_proxy_commands_configs_are_parsable = None
check_all_virt_mp_files_syntax = None
cron_checkers = {
check_symlinksifowner: 'check-symlinksifowner',
check_use_pam: 'check-usepam',
check_symlinkowngid: 'check-symlinkowngid',
check_existence_of_all_users_packages: 'check-cpanel-packages',
check_da_resellers_packages_files: 'check-da-packages-lists',
check_lve_limits: 'check-lve-limits',
check_cagefs_mount_points_exists: 'check-cagefs-mount-points-exist',
check_cagefs_enabled_users_isdir: 'check-cagefs-enabled-users-is-dir',
check_cagefs_disabled_users_isdir: 'check-cagefs-disabled-users-is-dir',
check_cagefs_disabled_etcfs_exists: 'check-cagefs-disabled-etc-fs-exists',
check_users_can_enter_cagefs: 'check-users-can-enter-cagefs',
check_proxy_commands_configs_are_parsable: 'check-proxy-commands-configs-are-parsable',
check_all_virt_mp_files_syntax: 'check-all-virtmp-files-syntax',
check_hidepid: 'check-hidepid',
check_jwt_token: 'check-jwt-token',
check_cl_plus_sender_service: 'check-cl-plus-sender-service',
check_node_exporter_service: 'check-node-exporter-service',
check_cmt_packages: 'check-cmt-packages',
check_lvestats_service: 'check-lvestats-service',
check_cagefs_partition_disk_quota: 'check_cagefs_partition_disk_quota',
check_low_pmem_limits: 'check_low_pmem_limits',
}
RUN_BY_CRON_SCOPE = 'run_by_cron'
CAGEFS_SCOPE = 'cagefs'
CMT_SCOPE = 'cmt'
ALL_SCOPE = 'all'
# TODO: we should consider about changing "FROM" field
# now chances that this mail will go to spam are almost 100%
EMAIL_TPL = """\
Subject: Your CloudLinux Server has issues: [{host}] - {date}
From: noreply@{host}
To: {to}
<html>
<head></head>
<body>
<p>
We have detected several CloudLinux configuration issues on your system.
</p>
<ul>
<li>You may ignore some or all issues if you know that they don't cause
you any troubles or they are expected or already scheduled to be fixed
</li>
<li>You may contact <a target="_blank" href="https://cloudlinux.zendesk.com/">CloudLinux support</a>
to help to resolve issues that are hard to resolve yourself
</li>
<li>You may disable this cron checks permanently
See <a target="_blank" href="https://docs.cloudlinux.com/cldiag.html">docs</a> for steps
</li>
</ul>
<p>
See report below.
</p>
<pre>
{content}
</pre>
</body>
</html>
"""
# Show help
def print_usage():
print('-h | --help shows this message')
print('--json prints output as json')
print('-a | --all prints all diag')
print(" --diag-cp prints control panel and its version (CP_NAME, CP_VERSION)")
print(" --doctor runs the cldoctor script and displays its output")
print(' --symlinksifowner prints fs.enforce_symlinksifowner from sysctl conf')
print(' --check-cm-all runs all of checkers for Centralized Monitoring')
print(' --check-jwt-token prints warnings if JWT token has any error')
print(' --check-suexec prints warning if suexec binary without CageFS')
print(' --check-suphp prints warning if suphp binary without CageFS')
print(' --check-usepam prints warnings if SSHd UsePAM from /etc/ssh/sshd_config')
print(' --check-phpselector prints warnings if current PHP engine is not supported by CloudLinux PHP Selector')
print(' --check-multi-php prints warnings if current system version is alt-php')
print(' --check-domains-compatibility prints warnings if domains are not compatible')
print(" --check-cagefs runs same checks as cagefsctl --sanity-check and print it's result")
print(" --check-symlinkowngid prints warning if Apache user is not protected with fs.enforce_symlinksifowner")
print(' --check-cpanel-packages prints packages from /var/cpanel/users that do not exist in /var/cpanel/packages')
print(' --check-da-packages-lists check packages files encoding from /usr/local/directadmin/data/users/')
print(' --check-php-conf prints warnings if /etc/cl.selector/php.conf has wrong format')
print(' --check-defaults-cfg prints warnings if /etc/cl.selector/defaults.cfg has wrong format')
print(' --check-lve-limits prints warnings if lve limits isn\'t valid on the server')
print(' --check-hidepid prints warning if hidepid protection is disabled on the server')
print(' --check-cagefs-quota prints warning if /var/cagefs is located on partition with disk quota disabled')
print(' --check-low-pmem-limits prints warning if low PMEM limits present')
print(' --generate-email will run most of the checks, generate HTML report and send it via email to admin.')
print(' Default recipient email is root@localhost.localdomain (Can be affected by mail server settings).')
print(' To change default email add/edit control panel getemail script from /etc/sysconfig/cloudlinux.')
print(' --cron-check Do the same as --generate-email but taking into account /etc/sysconfig/cloudlinux ENABLE_CLDIAG setting')
print(' --disable-cron-checkers [c1,c2] You can disable a few cron checkers passing on list of checkers separated by comma.')
print(' Full list of cron checkers: {}'.format(', '.join(cron_checkers.values())))
print(' If you want to enable all checkers - call without args')
print('')
def _filter_checkers_list(checkers):
"""
Remove from list checkers which was disabled by client
"""
disabled_checkers = get_list_of_disabled_cron_checkers()
enabled_checkers = [checker for checker in checkers
if checker.public_name not in disabled_checkers]
return enabled_checkers
def check_and_send_notification(cron_mode: bool,
scope: AnyStr=ALL_SCOPE) -> None:
"""
Run checkers that are present in given scope. Also send
email to servers administrator if some of checkers fail.
:param cron_mode: True - started by cron, False - started manually
:param scope: the param defines set of checkers
"""
checkers = prepare_checkers(scope=scope, cron_mode=cron_mode)
if cron_mode:
checkers = _filter_checkers_list(checkers)
errors, output = runner(checkers, do_exit=False)
if not errors:
return
mail_helper = MailHelper()
message = EMAIL_TPL.format(
host=socket.gethostname(),
date=datetime.date.today(),
to=detect.getCPAdminEmail(),
content=''.join(output),
cldiag_param=cron_cldiag_checkers_param_name,
cl_config=detect.CL_CONFIG_FILE,
)
headers = message_from_string(message)
msg = MIMEText(headers.get_payload(), 'html')
msg['Subject'] = headers['Subject']
msg['From'] = headers['From']
msg['To'] = headers['To']
# TODO: probably we can change sendmail signature to just msg argument
try:
mail_helper.sendmail(msg['From'], msg['To'], msg=msg)
except MailSendFailed as e:
error_msg = "cldiag is unable to send email notification " \
"about server issues; the error is '%s'\n" \
"Check your mail server settings or contact " \
"CloudLinux support for help" % e
if scope == RUN_BY_CRON_SCOPE:
error_msg += "\nAlso you can disable automatic notifications. See " \
"https://docs.cloudlinux.com/cldiag.html for steps."
print(error_msg)
def _add_public_name_to_cron_checkers(checkers):
"""
Write additional field with public name to each cron checker.
"""
for checker in checkers:
checker.public_name = cron_checkers[checker]
def prepare_checkers(scope=ALL_SCOPE, cron_mode=False):
all_scopes = (
ALL_SCOPE,
CAGEFS_SCOPE,
RUN_BY_CRON_SCOPE,
CMT_SCOPE,
)
if scope not in all_scopes:
raise ValueError('Unknown checkers scope')
cmt_checkers = (
check_jwt_token,
check_cl_plus_sender_service,
check_node_exporter_service,
check_cmt_packages,
check_lvestats_service,
)
all_checkers = (
check_cp_diag,
check_symlinksifowner,
check_suexec,
check_suphp,
check_use_pam,
check_symlinkowngid,
check_existence_of_all_users_packages,
check_da_resellers_packages_files,
check_phpselector,
check_defaults_cfg,
check_php_conf,
check_lve_limits,
check_hidepid,
check_cagefs_partition_disk_quota,
check_low_pmem_limits,
)
cagefs_checkers = (
fake_cagefs_checker,
)
checkers_run_by_cron = (
check_symlinksifowner,
check_use_pam,
check_symlinkowngid,
check_existence_of_all_users_packages,
check_da_resellers_packages_files,
check_lve_limits,
check_hidepid,
check_cagefs_partition_disk_quota,
check_low_pmem_limits,
*cmt_checkers,
)
if CAGEFS_INSTALLED:
try:
from sanity_check import CAGEFS_CHECKERS
cagefs_checkers = tuple(CAGEFS_CHECKERS)
except ImportError:
pass
all_checkers = all_checkers + cagefs_checkers
if scope == ALL_SCOPE:
return all_checkers
if scope == CAGEFS_SCOPE and CAGEFS_INSTALLED:
return cagefs_checkers
if scope == RUN_BY_CRON_SCOPE:
if CAGEFS_INSTALLED:
try:
from sanity_check import (
check_multiphp_system_default,
)
checkers_run_by_cron += tuple(
checker for checker in cagefs_checkers
# LU-919; We exclude cagefs multiphp checker for checkers which run by cron
if checker != check_multiphp_system_default
)
except ImportError:
pass
if cron_mode:
_add_public_name_to_cron_checkers(checkers_run_by_cron)
return checkers_run_by_cron
if scope == CMT_SCOPE:
return cmt_checkers
return []
def run_cldoctor():
"""
Downloads the cldoctor script from the fixed URL to a temp file and runs it.
Prints the output.
:raises CalledProcessError: When the script returns a non-zero return code.
"""
doctor_url = r"https://repo.cloudlinux.com/cloudlinux/cldoctor/cldoctor.sh"
doctor_filename = os.path.join("/root/", "tmp_cldoctor.sh")
print(f"Downloading cldoctor script from {doctor_url}")
urllib.request.urlretrieve(doctor_url, doctor_filename)
# `check` will raise an exception if the script exited with a non-zero code.
subprocess.run(["/bin/bash", doctor_filename], shell=False, text=True, check=True, cwd="/root")
# Wipe the script after execution, don't wait for tempdir cleanup.
os.remove(doctor_filename)
def main():
is_root_or_exit()
try:
opts, args = getopt.getopt(sys.argv[1:], 'ha', [
'help',
'all',
'diag-cp',
'symlinksifowner',
'check-suexec',
'check-suphp',
'check-usepam',
'generate-email',
'cron-check',
'check-cagefs',
'check-symlinkowngid',
'check-cpanel-packages',
'check-da-packages-lists',
'check-phpselector',
'check-multi-php',
'check-domains-compatibility',
'check-hidepid',
'check-cagefs-quota',
'check-low-pmem-limits',
'json',
'check-php-conf',
'check-defaults-cfg',
'check-lve-limits',
'add-comment-to-config-about-cron-checkers',
'disable-cron-checkers',
'check-cm-all',
'check-jwt-token',
'doctor',
])
except getopt.GetoptError:
print('error: unknown command')
print_usage()
sys.exit(1)
executed = False
to_json = False
if ('--json', '') in opts:
to_json = True
scheduled_checkers = []
for o, a in opts:
if o in ('--help', '-h'):
executed = True
print_usage()
elif o in('--check-cm-all',):
executed = True
checkers = prepare_checkers(scope=CMT_SCOPE)
for checker in checkers:
scheduled_checkers.append(checker)
elif o in ('--all', '-a'):
executed = True
checkers = prepare_checkers()
runner(checkers, to_json)
elif o in ('--diag-cp', ):
scheduled_checkers.append(check_cp_diag)
elif o in ('--symlinksifowner', ):
scheduled_checkers.append(check_symlinksifowner)
elif o in ('--check-suexec', ):
scheduled_checkers.append(check_suexec)
elif o in ('--check-suphp', ):
scheduled_checkers.append(check_suphp)
elif o in ('--check-usepam',):
scheduled_checkers.append(check_use_pam)
elif o in ('--generate-email', ):
executed = True
check_and_send_notification(scope=ALL_SCOPE, cron_mode=False)
elif o in ('--check-cagefs',):
if not CAGEFS_INSTALLED:
print('Cagefs is not installed. Skipping check')
exit(0)
checkers = prepare_checkers(CAGEFS_SCOPE)
for checker in checkers:
scheduled_checkers.append(checker)
elif o in ('--check-low-pmem-limits',):
scheduled_checkers.append(check_low_pmem_limits)
elif o in ('--check-symlinkowngid',):
scheduled_checkers.append(check_symlinkowngid)
elif o in ('--cron-check',):
executed = True
if is_email_notification_enabled():
check_and_send_notification(cron_mode=True,
scope=RUN_BY_CRON_SCOPE)
elif o in ('--check-phpselector',):
scheduled_checkers.append(check_phpselector)
elif o in ('--check-multi-php',):
executed = True
if CAGEFS_INSTALLED:
from sanity_check import check_multiphp_system_default
scheduled_checkers.append(check_multiphp_system_default)
elif o in ('--check-domains-compatibility',):
scheduled_checkers.append(check_domains_compatibility)
elif o in ('--check-cpanel-packages',):
scheduled_checkers.append(check_existence_of_all_users_packages)
elif o in ('--check-da-packages-lists',):
scheduled_checkers.append(check_da_resellers_packages_files)
elif o in ('--check-php-conf',):
scheduled_checkers.append(check_php_conf)
elif o in ('--check-defaults-cfg',):
scheduled_checkers.append(check_defaults_cfg)
elif o in ('--check-lve-limits',):
scheduled_checkers.append(check_lve_limits)
elif o in ('--disable-cron-checkers',):
if args:
disabled_cron_checkers = [item.strip() for item in
str(args[0]).strip().split(',')]
else:
disabled_cron_checkers = []
for checker in disabled_cron_checkers:
if checker not in list(cron_checkers.values()):
print(f'error: wrong name of the checker: {checker}')
print(f'Full list of existing checkers: '
f'{", ".join(cron_checkers.values())}')
sys.exit(1)
set_list_of_disabled_cron_checkers(disabled_cron_checkers)
executed = True
elif o in ('--check-hidepid',):
scheduled_checkers.append(check_hidepid)
elif o in ('--check-jwt-token',):
scheduled_checkers.append(check_jwt_token)
elif o in ('--check-cagefs-quota',):
scheduled_checkers.append(check_cagefs_partition_disk_quota)
elif o in ('--doctor',):
executed = True
run_cldoctor()
if scheduled_checkers or to_json:
executed = True
runner(scheduled_checkers, to_json)
if not executed:
print('error: argument required')
print_usage()
sys.exit(1)
if __name__ == "__main__":
main()
Zerion Mini Shell 1.0