#!/opt/imh-python/bin/python3
"""Script to fix common security issues on shared servers"""
from stat import S_IWGRP, S_IWOTH, S_IMODE
from subprocess import run, DEVNULL
users = rads.all_cpusers(owners=False)
print("Checking /home...")
set_perms(Path('/home'), 0o711, (0, 0), 'root:root')
print("Removing shell access for unauthorized users...")
print("Checking user home folder permissions...")
nobody_gid = grp.getgrnam('nobody').gr_gid
fix_user_perms(user, nobody_gid)
def check_shells(users: list[str]):
"""Remove shell access for users that shouldn't have it"""
if distro.id() == 'cloudlinux':
print("CloudLinux server detected. Allowing /bin/bash shells")
main_shell = '/usr/local/cpanel/bin/jailshell'
allowed_shells = (main_shell, '/usr/local/cpanel/bin/noshell')
if user in rads.SYS_USERS:
user_shell = pwd.getpwnam(user).pw_shell
if user_shell in allowed_shells:
'Changing shell of %s from %s to %s',
chsh = ['chsh', '-s', main_shell, user]
run(chsh, stdout=DEVNULL, check=False)
'sed', '-i', f"s@{user_shell}@{main_shell}@g",
f'/var/spool/cron/{user}',
run(sed, stdout=DEVNULL, check=False)
run(['service', 'crond', 'reload'], stdout=DEVNULL, check=False)
path: Path, new_mode: int, uid_gid: tuple[int, int], usr_grp: str
cur_mode = S_IMODE(stat.st_mode)
'%s --> Changed mode from %s to %s',
if uid_gid != (stat.st_uid, stat.st_gid):
chown(path, uid_gid[0], uid_gid[1])
LOGGER.info('%s --> Changed owner:group to %s', path, usr_grp)
def fix_user_perms(user: str, nobody_gid: int):
info = pwd.getpwnam(user)
except rads.CpuserError as exc:
homedir = Path(info.pw_dir)
or not info.pw_dir.startswith('/home')
if info.pw_uid == 0: # malicious cpmove restores can do this
subject='SECURITY: cPanel user with UID of 0',
body=f"secureperms found {user}@{platform.node()} has a UID of 0",
(info.pw_uid, info.pw_gid),
(info.pw_uid, nobody_gid),
(info.pw_uid, info.pw_gid),
# check if homedir/logs is writable by group or world
logs_mode = (homedir / 'logs').stat().st_mode
if logs_mode & S_IWOTH or logs_mode & S_IWGRP:
(info.pw_uid, info.pw_gid),
userdata = rads.UserData(user)
for docroot in map(Path, userdata.all_roots):
(info.pw_uid, nobody_gid),
if __name__ == '__main__':
# cron config appends stdout/err to /var/log/maint/secureperms.log
LOGGER = rads.setup_logging(
path=None, name='secureperms', print_out='stdout', loglevel='INFO'
IS_SUPHP = Path('/etc/apache2/conf.modules.d/90-suphp.conf').is_file()