Mini Shell
Direktori : /usr/share/cagefs/ |
|
Current File : //usr/share/cagefs/repair_homes.py |
#!/opt/cloudlinux/venv/bin/python3 -bb
# -*- 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
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 getopt
import os
import pwd
import shutil
import subprocess
import sys
import time
from clcommon.utils import mod_makedirs
from clcagefslib.const import BASEDIR
from secureio import print_error
LOGFILE = '/usr/share/cagefs/repair_homes.log'
BASE_HOME_DIR = '/home'
PASSWD = '/etc/passwd'
HTTPD_CONF = '/usr/local/apache/conf/httpd.conf'
USERDATA_UPDATE = '/usr/local/cpanel/bin/userdata_update'
USERMOD = '/usr/sbin/usermod'
USERDATA = '/var/cpanel/userdata'
# Function to uninstall cagefs.etc
CAGEFS_ETC='/etc/cagefs.etc'
DEBUG_PREFIX=''
def uninstall_cagefs_etc():
try:
dirList = os.listdir(DEBUG_PREFIX + CAGEFS_ETC)
except OSError:
return
for _file in dirList:
target = DEBUG_PREFIX + '/etc/' + _file
origin = DEBUG_PREFIX + CAGEFS_ETC + '/' + _file
if not os.path.islink(target):
continue
try:
os.unlink(target)
except OSError:
print('Warning: failed to remove', target)
try:
os.rename(origin, target)
except OSError:
print('Warning: failed to move', origin, 'to', target)
# Functions for unmounting users for CageFS 2.0
UMOUNT='/bin/umount'
def umount_list(_list):
_list.sort()
_list.reverse()
for line in _list:
subprocess.call([UMOUNT, "-l", line])
def get_mounted_dirs():
mounts = open("/proc/mounts", "r").readlines()
_list = []
for line in mounts:
mountpoint = line.split()[1]
if mountpoint.find(BASEDIR) != -1:
_list.append(mountpoint[mountpoint.find('/'):])
return _list
def umount(user):
subdir = BASEDIR + '/'+user[-2:]+ '/'+user+'/'
mounts = open("/proc/mounts", "r").readlines()
mylist = []
for line in mounts:
mountpoint = line.split()[1]
if mountpoint.find(subdir) != -1:
mylist.append(mountpoint[mountpoint.find('/'):])
umount_list(mylist)
# Returns True if unmounting is done
def umount_all():
dirs = get_mounted_dirs()
if len(dirs) != 0:
umount_list(dirs)
return True
return False
# Functions for enabling/disabling users for CageFS 2.0
INIPREFIX='/etc/cagefs/'
disabled_dir = INIPREFIX+'users.disabled'
enabled_dir = INIPREFIX+'users.enabled'
def toggle_file(_dir, username, enable):
prefix = username[-2:]
fname = '/'+prefix+'/'+username
if enable:
try:
os.remove(_dir + fname)
except OSError:
pass
try:
os.rmdir(_dir + '/'+prefix)
except OSError:
pass
else:
try:
mod_makedirs(_dir+'/'+prefix, 0o751)
except OSError:
pass
try:
open(_dir + fname, 'w').close()
os.chmod(_dir + fname, 0o644)
except OSError:
pass
def toggle_user(username, enable):
if os.path.isdir(disabled_dir):
toggle_file(disabled_dir, username, enable)
if os.path.isdir(enabled_dir):
toggle_file(enabled_dir, username, not enable)
def disable_user(user):
toggle_user(user, False)
umount(user)
def enable_user(user):
toggle_user(user, True)
def is_text_file(path):
if os.path.isfile(path):
p = subprocess.Popen(['file','-bi', path], stdout=subprocess.PIPE, text=True)
out, _ = p.communicate()
if 'text' in out:
return True
return False
def confirm(message):
print(message, end=' ', flush=True)
while True:
line = sys.stdin.readline()
if line == "yes\n":
break
elif line == "no\n":
print("Aborting")
sys.exit(0)
print("Please, reply with yes or no")
# Returns True if users with invalid pathes to home directories exist
def invalid_homes_exist():
# get all users from /etc/passwd
pw = pwd.getpwall()
for line in pw:
if line.pw_dir.startswith('/var/cagefs/'):
return True
return False
def print_log(log, *messages):
for msg in messages:
print(msg, end=' ')
print(msg, end=' ', file=log)
print("")
print("", file=log)
# Function sets home directory for specified user
# Returns True if error has occured
def usermod(user, home_dir, log):
try:
ret = subprocess.call([USERMOD, "-d", home_dir, user])
if ret != 0:
print_log(log, "Error:", USERMOD, "-d", home_dir, user, "failed")
return True
except OSError:
print_log(log, 'Error: failed to run', USERMOD, "-d", home_dir, user)
return True
return False
# Run a subprocess with list of options. Log output of subprocess.
# Return True if error has occured
def run_subprocess(log, command_line_list):
error = False
try:
# run the command and suppress it's output
p = subprocess.Popen(command_line_list,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True)
(stdoutdata, stderrdata) = p.communicate()
if stdoutdata != None:
print_log(log, stdoutdata)
if stderrdata != None:
print_log(log, stderrdata)
# check return code of the child
if p.returncode != 0:
error = True
except OSError:
print_log(log, 'Error: failed to run', command_line_list)
error = True
return error
def repair_homes(ask = True):
log = open(LOGFILE, 'a+')
if not invalid_homes_exist():
print_log(log, 'Users with invalid pathes to home directories do NOT exist')
log.close()
return
print("Users that have invalid path to home directory are found")
print("(users that have path to home directory starting with /var/cagefs).")
print('This script will move home directories to "'+BASE_HOME_DIR+'" and change pathes to home directories')
print("in /etc/passwd, /var/cpanel/userdata and /usr/local/apache/conf/httpd.conf")
print("Log of all operations will be written to", LOGFILE)
print("Backups will be created automatically.")
print("")
if ask:
confirm("Do you want to continue (yes/no)? ")
# Print current date and time
cur_time = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime())
print_log(log, "*** Repair started", cur_time)
# Make backups
shutil.copyfile(PASSWD, PASSWD+'.repair.bak')
print_log(log, "Created backup", PASSWD+'.repair.bak')
shutil.copyfile(HTTPD_CONF, HTTPD_CONF+'.repair.bak')
print_log(log, "Created backup", HTTPD_CONF+'.repair.bak')
# Read httpd.conf
print_log(log, 'Reading', HTTPD_CONF, '...')
_file = open(HTTPD_CONF, "r")
httpd_conf = _file.readlines()
_file.close()
# get all users from /etc/passwd
pw = pwd.getpwall()
for line in pw:
if line.pw_dir.startswith('/var/cagefs/'):
# pw_dir is like /var/cagefs/[prefix]/[parent-user]/home/[parent-user]/[invalid-user]
# or
# like /var/cagefs/[prefix]/[parent-user]/var/cagefs/[prefix2]/[parent-user2]/home/[parent-user2]/[parent-user]/[invalid-user]
# etc...
print_log(log, "Repairing user", line.pw_name, "...")
# Get name of invalid user
invalid_user = os.path.basename(line.pw_dir)
if invalid_user != line.pw_name:
print_log(log, 'Error: Cannot repair home path', line.pw_dir, 'for user', line.pw_name)
continue
# Get /var/cagefs/[prefix]/[parent-user]/home/[parent-user]
# or /var/cagefs/[prefix]/[parent-user]/var/cagefs/[prefix2]/[parent-user2]/home/[parent-user2]/[parent-user]
var_cagefs_home_of_parent = os.path.dirname(line.pw_dir)
# Get name of "parent" of invalid user
parent = os.path.basename( var_cagefs_home_of_parent )
# Get passwd info for parent
try:
pw_line = pwd.getpwnam(parent)
except Exception as e:
print_log(log, 'Cannot repair home path', line.pw_dir, 'for user', line.pw_name)
print_log(log, 'Error: "Parent" user', parent, 'does NOT exist')
print_log(log, str(e))
continue
# Get home of parent
parent_home = pw_line.pw_dir
# if not var_cagefs_home_of_parent.endswith(parent_home):
# print_log(log, 'Cannot repair home path', line.pw_dir, 'for user', line.pw_name)
# print_log(log, 'Error: Path to home directory in /var/cagefs for parent user is invalid')
# print_log(log, 'Parent user:', parent)
# print_log(log, 'Home directory of parent user:', parent_home)
# print_log(log, 'Home directory of parent user in /var/cagefs:', var_cagefs_home_of_parent)
# continue
# Get home base (parent) directory (commonly "/home")
if parent_home.startswith('/var/cagefs/'):
# Cyclic error. Parent user should be repaired already. So home of parent should be "/home"
base_home = BASE_HOME_DIR
else:
base_home = os.path.dirname(parent_home)
print_log(log, 'Base home directory:', base_home)
# Incorrect (invalid) location of home dir of invalid user
src = base_home+'/'+parent+'/'+invalid_user
# Correct location of home dir of invalid user
dest = base_home+'/'+invalid_user
# Check that home dir of invalid user exists in home dir of "parent" user
# (Check that /home/[parent-user]/[invalid-user] exists)
if not os.path.isdir(src):
# Check that home dir of invalid user is in proper location already
if not os.path.isdir(dest):
print_log(log, 'Error: home directory of user', invalid_user, 'is NOT found')
print_log(log, 'Searched locations:', src, "and", dest)
continue
else:
# Home dir is NOT moved yet
# Disable and unmount user
disable_user(invalid_user)
# if src is symlink, move dir which is pointed by that symlink
src = os.path.realpath(src)
# remove dest if it is symlink
if os.path.islink(dest):
try:
os.unlink(dest)
except (OSError, IOError):
pass
# Check that "correct" home directory of invalid_user does NOT exist yet
if not os.path.exists(dest):
# Do "mv /home/[parent-user]/[invalid-user] /home/[invalid-user]"
try:
os.rename(src, dest)
except (OSError, IOError):
print_log(log, 'Error while moving', src, 'to', dest)
enable_user(invalid_user)
continue
# Check that moving directory was successfull
if (not os.path.isdir(dest)) or os.path.exists(src):
print_log(log, 'Error: moving', src, 'to', dest, 'was NOT successfull')
enable_user(invalid_user)
continue
else:
# Destination (correct) home directory already exists
print_log(log, 'Warning: home directory', dest, 'of user', invalid_user, 'already exists')
print_log(log, 'Warning: home directory of user', invalid_user, 'is NOT moved')
# Home dir is moved already
# Change path to home dir in /etc/passwd
if usermod(invalid_user, dest, log):
enable_user(invalid_user)
continue
# Change path to home dir in httpd.conf
for ind in range(len(httpd_conf)):
if httpd_conf[ind].find('/var/cagefs/') != -1:
temp = httpd_conf[ind].replace(line.pw_dir, dest)
# Invalid path is repaired correctly ?
# line.pw_dir is NOT a part of another more long invalid path (as a result of cyclic error) ?
if temp.find('/var/cagefs/') == -1:
httpd_conf[ind] = temp
# Change path to home dir in all TEXT files in /var/cpanel/userdata
for next_file in os.listdir(os.path.join(USERDATA, invalid_user)):
file_path = os.path.join(USERDATA, invalid_user) + '/' + next_file
if is_text_file(file_path):
modified = False
userdata_file = open(file_path, 'r')
userdata = userdata_file.readlines()
userdata_file.close()
for ind in range(len(userdata)):
if userdata[ind].find('/var/cagefs/') != -1:
temp = userdata[ind].replace(line.pw_dir, dest)
# Invalid path is repaired correctly ?
# line.pw_dir is NOT a part of another more long invalid path (as a result of cyclic error) ?
if temp.find('/var/cagefs/') == -1:
userdata[ind] = temp
modified = True
else:
print_log(log, 'Error: cannot repair', file_path)
modified = False
break
if modified:
shutil.copyfile(file_path, file_path+'.repair.bak')
print_log(log, "Created backup", file_path+'.repair.bak')
userdata_file = open(file_path, 'w')
for next_line in userdata:
userdata_file.write(next_line)
userdata_file.close()
enable_user(invalid_user)
print_log(log, 'User', invalid_user, "has been repaired SUCCESSFULLY!")
# Write httpd.conf
print_log(log, 'Writting', HTTPD_CONF, '...')
_file = open(HTTPD_CONF, "w")
for line in httpd_conf:
_file.write(line)
_file.close()
# Rebuild /var/cpanel/userdata
print_log(log, 'Rebuilding /var/cpanel/userdata...')
run_subprocess(log, [USERDATA_UPDATE])
log.close()
def print_warning():
print('Please, rename or remove /var/cagefs directory of old version of CageFS')
print('in order to correct operation of new version of CageFS.')
print('New /var/cagefs directory will be created automatically.')
def rename_var_cagefs():
if os.path.exists('/var/cagefs'):
# Users with invalid pathes to home directories do NOT exist ?
if not invalid_homes_exist():
try:
if os.path.isdir('/var/cagefs.old') and (not os.path.islink('/var/cagefs.old')):
shutil.rmtree('/var/cagefs.old', True)
else:
os.unlink('/var/cagefs.old')
except (OSError, IOError):
pass
try:
os.rename('/var/cagefs', '/var/cagefs.old')
print('/var/cagefs has been renamed to /var/cagefs.old')
except (OSError, IOError):
print_error('failed to rename /var/cagefs to /var/cagefs.old')
print_warning()
else:
print_warning()
def main():
try:
opts, _ = getopt.getopt(sys.argv[1:], "f", ["do-not-ask", "rename-var-cagefs", "uninstall_cagefs_etc",\
"add-syslog-socket", "remove-syslog-socket"])
except getopt.GetoptError:
print('Usage error')
sys.exit(1)
if (os.geteuid()!=0):
print_error('root privileges required. Abort.')
sys.exit(1)
for o, _ in opts:
if o in ('-f', '--do-not-ask'):
repair_homes(False)
sys.exit(0)
elif o in ('--rename-var-cagefs',):
rename_var_cagefs()
sys.exit(0)
elif o in ('--uninstall_cagefs_etc',):
uninstall_cagefs_etc()
sys.exit(0)
elif o in ('--add-syslog-socket',):
print ("Invalid option.\nUse /usr/share/cagefs-plugins/install"
"-cagefs-plugin.py --add-syslog-socket")
sys.exit(0)
elif o in ('--remove-syslog-socket',):
print ("Invalid option.\nUse /usr/share/cagefs-plugins/install"
"-cagefs-plugin.py --remove-syslog-socket")
sys.exit(0)
repair_homes(True)
# mv /home/mistersc/zthzmnvp /home/zthzmnvp
# mkdir -p /var/cagefs/sc/mistersc/home/mistersc
# ln -s /home/zthzmnvp /var/cagefs/sc/mistersc/home/mistersc/zthzmnvp
# mv /home/lakzqyaa/zcktakme /home/zcktakme
# mkdir -p /var/cagefs/aa/lakzqyaa/var/cagefs/sc/mistersc/home/mistersc/lakzqyaa
# ln -s /home/zcktakme /var/cagefs/aa/lakzqyaa/var/cagefs/sc/mistersc/home/mistersc/lakzqyaa/zcktakme
# OLD function (NOT USED)
def repair_homes_old():
print("This script repairs home directories of users that have invalid pathes to home directories")
print("(users that have path to home directory starting with /var/cagefs)")
print("This script will move home directories to proper location and create")
print("appropriate symlink in /var/cagefs")
print("")
confirm("Do you want to continue (yes/no)? ")
log = open(LOGFILE, 'a+')
# Print current date and time
cur_time = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime())
print_log(log, "*** Repair started", cur_time)
if not invalid_homes_exist():
print_log(log, 'Users with invalid pathes to home directories do NOT exist')
log.close()
return
# get all users from /etc/passwd
pw = pwd.getpwall()
for line in pw:
if line.pw_dir.startswith('/var/cagefs/'):
# pw_dir is like /var/cagefs/[prefix]/[parent-user]/home/[parent-user]/[invalid-user]
# or
# like /var/cagefs/[prefix]/[parent-user]/var/cagefs/[prefix2]/[parent-user2]/home/[parent-user2]/[parent-user]/[invalid-user]
# etc...
if os.path.islink(line.pw_dir):
print_log(log, 'Home', line.pw_dir, 'for user', line.pw_name, 'is repaired already. Skipping...')
continue
print_log(log, "Repairing user", line.pw_name, "...")
# Get name of invalid user
invalid_user = os.path.basename(line.pw_dir)
if invalid_user != line.pw_name:
print_log(log, 'Error: Cannot repair home path', line.pw_dir, 'for user', line.pw_name)
continue
# Get /var/cagefs/[prefix]/[parent-user]/home/[parent-user]
# or /var/cagefs/[prefix]/[parent-user]/var/cagefs/[prefix2]/[parent-user2]/home/[parent-user2]/[parent-user]
var_cagefs_home_of_parent = os.path.dirname(line.pw_dir)
# Get name of "parent" of invalid user
parent = os.path.basename( var_cagefs_home_of_parent )
# Get passwd info for parent
try:
pw_line = pwd.getpwnam(parent)
except Exception as e:
print_log(log, 'Cannot repair home path', line.pw_dir, 'for user', line.pw_name)
print_log(log, 'Error: "Parent" user', parent, 'does NOT exist')
print_log(log, str(e))
continue
# Get home of parent
parent_home = pw_line.pw_dir
if not var_cagefs_home_of_parent.endswith(parent_home):
print_log(log, 'Cannot repair home path', line.pw_dir, 'for user', line.pw_name)
print_log(log, 'Error: Path to home directory in /var/cagefs for parent user is invalid')
print_log(log, 'Parent user:', parent)
print_log(log, 'Home directory of parent user:', parent_home)
print_log(log, 'Home directory of parent user in /var/cagefs:', var_cagefs_home_of_parent)
continue
# Get home base (parent) directory (commonly "/home")
if parent_home.startswith('/var/cagefs/'):
# Cyclic error. Parent user should be repaired already. So home of parent should be "/home"
base_home = BASE_HOME_DIR
else:
base_home = os.path.dirname(parent_home)
# Check that home dir of invalid user exists in home dir of "parent" user
# (Check that /home/[parent-user]/[invalid-user] exists)
src = base_home+'/'+parent+'/'+invalid_user
if not os.path.isdir(src):
print_log(log, 'Error: home directory of user', invalid_user, 'is NOT found in', base_home+'/'+parent)
continue
# Check that "correct" home directory of invalid_user does NOT exist yet
dest = base_home+'/'+invalid_user
if os.path.exists(dest):
print_log(log, 'Error: home directory', dest, 'of user', invalid_user, 'already exists')
continue
# Do "mv /home/[parent-user]/[invalid-user] /home/[invalid-user]"
try:
os.rename(src, dest)
except (OSError, IOError):
print_log(log, 'Error while moving', src, 'to', dest)
continue
# Check that moving directory was successfull
if (not os.path.isdir(dest)) or os.path.exists(src):
print_log(log, 'Error: moving', src, 'to', dest, 'was NOT successfull')
continue
# Do "mkdir -p /var/cagefs/[prefix]/[parent-user]/home/[parent-user]"
# or "mkdir -p /var/cagefs/[prefix]/[parent-user]/var/cagefs/[prefix2]/[parent-user2]/home/[parent-user2]/[parent-user]"
# (ensure that directory exists)
try:
os.makedirs(var_cagefs_home_of_parent)
except (OSError, IOError):
pass
# Do "ln -s /home/[invalid-user] /var/cagefs/[prefix]/[parent-user]/home/[parent-user]/[invalid-user]"
# or "ln -s /home/[invalid-user] /var/cagefs/[prefix]/[parent-user]/var/cagefs/[prefix2]/[parent-user2]/home/[parent-user2]/[parent-user]/[invalid-user]"
try:
os.symlink(dest, line.pw_dir)
except (OSError, IOError):
print_log(log, 'Error while creatimg symlink', line.pw_dir, 'to', dest)
continue
print_log(log, 'User', invalid_user, "has been repaired SUCCESSFULLY!")
log.close()
if __name__ == "__main__":
main()
Zerion Mini Shell 1.0