Mini Shell
#!/opt/imh-python/bin/python3
"""Check for bugged addon domains which cpanel adds as parked"""
from argparse import ArgumentParser
from pathlib import Path
from logging import Logger
import traceback
import sys
import rads
import yaml
def check_serveraliases(log: Logger, noop: bool):
"""Iterate primary domain configs and check serveraliases on each"""
for cpuser_entry in Path('/var/cpanel/users').iterdir():
user = cpuser_entry.name
if not rads.is_cpuser(user):
continue
try:
userdata = rads.UserData(user)
except Exception as exc:
log.warning('%s - %s: %s', user, type(exc).__name__, exc)
continue
primary = userdata.primary.domain
userdata_path = Path('/var/cpanel/userdata', user)
addons = {x.domain for x in userdata.addons}
if not addons:
continue
addons.update(f"www.{x.domain}" for x in userdata.addons)
check_primary_file(log, noop, userdata_path / primary, addons)
check_primary_file(log, noop, userdata_path / f"{primary}_SSL", addons)
def check_primary_file(log: Logger, noop: bool, path: Path, addons: list[str]):
"""Check a primary domain's config file in userdata to see if an addon
domain was erroneously added to the "serveralias" line"""
try:
with open(path, encoding='utf-8') as file:
data = yaml.load(file, Loader=yaml.SafeLoader)
except (OSError, ValueError):
return
old = data['serveralias'].split()
if not old:
# there should have been at least a www alias for the main
# domain, so if we hit here, the file is mangled or the
# file format changed. Don't touch it
return
new = [x for x in old if x not in addons]
if old == new:
return # No changes
if not new:
return # Mangled file
log.info("old %s serveraliases: %s", path, ' '.join(old))
log.info("new %s serveraliases: %s", path, ' '.join(new))
data['serveralias'] = ' '.join(new)
if noop:
return
save_conf(path, data)
def save_conf(path: Path, data: dict):
"""Save config changes by writing to a temp file and then moving it into
place. Saving in this method avoids race conditions from cPanel trying
to read it before we're done flushing contents to disk"""
tmp_path = path.parent / f".{path.name}.tmp"
with open(tmp_path, 'w', encoding='utf-8') as file:
yaml.dump(data, file, default_flow_style=False)
tmp_path.rename(path)
def main():
"""Setup logging and run check_serveraliases"""
# blank argparse adds -h/--help
parser = ArgumentParser(description=__doc__)
parser.add_argument('--noop', action='store_true', help='test mode')
noop: bool = parser.parse_args().noop
if noop:
fmt = '%(asctime)s %(levelname)s NOOP: %(message)s'
else:
fmt = '%(asctime)s %(levelname)s %(message)s'
log = rads.setup_logging(
path='/var/log/maint/addon_serveraliases.log',
name='addon_serveraliases',
fmt=fmt,
loglevel='DEBUG',
print_out='stdout',
chown=(0, 0),
chmod=0o700,
)
try:
check_serveraliases(log, noop)
except KeyboardInterrupt:
log.warning("Canceling run on ^C")
sys.exit(1)
except Exception:
log.critical(traceback.format_exc())
sys.exit(1)
if __name__ == '__main__':
main()
Zerion Mini Shell 1.0