Mini Shell

Direktori : /opt/sharedrads/
Upload File :
Current File : //opt/sharedrads/modsec_disable.py

#!/opt/imh-python/bin/python3

"""Written by Quentin H
"""


import subprocess
import argparse
from pathlib import Path
import sys
from rads.color import red, green

# Set the lock path to /tmp as a placeholder
lock_file = Path("/tmp/modsec_disable.lock")
platformI = Path("/etc/ansible/wordpress-ultrastack")

# Ensure that only one instance of the script runs at a time.
def get_lock():
    if lock_file.exists():
        print(red("Another instance of the script is currently running. Exiting."))
        sys.exit(1)
    lock_file.touch()  # Create a lock file to indicate the script is running

# Release the lock after the script has completed.
def release_lock():
    lock_file.unlink(missing_ok=True)

# Check if Nginx exists
def check_nginx():
    nginx_dir = Path("/var/log/nginx/")
    return nginx_dir.exists()

# Get Apache version
def get_apache_version():
    try:
        result = subprocess.run(["apachectl", "-v"], capture_output=True, text=True, check=True)
    except FileNotFoundError:
        print(red("apachectl not found. Please ensure Apache is installed."))
        exit(1)
    except subprocess.CalledProcessError:
        print(red("Failed to get Apache version."))
        exit(1)
    if "Server version" in result.stdout:
        version_line = result.stdout.split('\n')[0]
        version = version_line.split()[2]
        return version.split('/')[1]
    print(red("Unable to parse Apache version."))
    exit(1)

# Update modsec.conf file based on provided arguments.
def update_modsec_configuration(modsec_file, rule_ids=None, disable_all=False, reset=False, remove_rule_id=None):
    modsec_file.parent.mkdir(parents=True, exist_ok=True)  # Ensure the directory exists
    # Ensure the modsec.conf file exists before modifying it
    if not modsec_file.exists() or reset:
        modsec_file.write_text("")  # Create or reset the file if necessary

    if disable_all:
        modsec_file.write_text("SecRuleEngine Off\n")
    elif rule_ids:
        for rule_id in rule_ids:
            disable_rule(modsec_file, rule_id)
    elif remove_rule_id:
        for rule_id in remove_rule_id:
            remove_rule(modsec_file, rule_id)

# Add rules to modsec.conf, checks if the rule exists and skips it if it does
def disable_rule(modsec_file, rule_id):
    rule_text = f"SecRuleRemoveById {rule_id}"
    contents = ""
    if modsec_file.exists():
        with modsec_file.open("r+") as file:
            contents = file.read()
    if rule_text in contents:
        print(red(f"Rule {rule_id} already disabled, not adding."))
        return
    with modsec_file.open("a") as file:
        file.write(f"{rule_text}\n")
    print(green(f"Rule {rule_id} added to file: {modsec_file}"))

# Remove a rule from the modsec file.
def remove_rule(modsec_file, rule_id):
    rule_text = f"SecRuleRemoveById {rule_id}"
    if modsec_file.exists():
        with modsec_file.open("r+") as file:
            contents = file.readlines()
            file.seek(0)
            file.truncate()
            for line in contents:
                if rule_text not in line:
                    file.write(line)
            print(green(f"Rule {rule_id} removed from file: {modsec_file}"))

# Remove all SecRuleRemoveById and SecRuleEngine entries from the modsec file.
def reset_modsec_configuration(modsec_file):
    if modsec_file.exists():
        with modsec_file.open("r+") as file:
            contents = file.readlines()
            file.seek(0)
            file.truncate()
            for line in contents:
                if not (line.startswith("SecRuleRemoveById") or line.startswith("SecRuleEngine Off")):
                    file.write(line)
            print(green(f"All SecRuleRemoveById and SecRuleEngine entries removed from file: {modsec_file}"))

# Rebuild and restart Apache.
def cpanel_restart_apache():
    subprocess.run(["/scripts/rebuildhttpdconf"])
    subprocess.run(["/sbin/apachectl", "graceful"])
    print(green("Apache configuration rebuilt and service restarted gracefully."))

def platformI_restart_apache():
    try:
        subprocess.run(["apachectl", "-t"])
    except subprocess.CalledProcessError:
        print(red("Syntax failed apache check"))
        exit(1)
    try:
        subprocess.run(["systemctl", "restart", "httpd"])
        print(green("Apache restarted correctly."))
    except subprocess.CalledProcessError:
        print(red("Apache failed to restart"))
        exit(1)

# Run ngxconf if Nginx exists
def cpanel_run_ngxconf(user):
    subprocess.run(["ngxconf", "-u", user, "-rd"])

# Function to handle platformI check and set the modsec file path
def platformI_modsec(args):
    modsec_file_path = Path("/etc/httpd/modsecurity.d/activated_rules/z-mycustom.conf")
    update_modsec_configuration(
        modsec_file=modsec_file_path,
        rule_ids=args.rule if args.rule else None,
        disable_all=args.off,
        reset=args.reset,
        remove_rule_id=args.drop
    )
    if args.reset:
        reset_modsec_configuration(modsec_file_path)
    platformI_restart_apache()

# Cpanel function that sets the modsec.conf file based on the flags/args passed to the script 
def cpanel_modsec(args):
    if not args.user:
        print(red("The --user (-u) flag is required for CPanel operations."))
        sys.exit(1)
    
    try:
        apache_version = get_apache_version()
        if apache_version.startswith("2.4"):
            std_apache_dir = Path("/usr/local/apache/conf/userdata/std/2_4")
            ssl_apache_dir = Path("/usr/local/apache/conf/userdata/ssl/2_4")
        elif apache_version.startswith("2."):
            std_apache_dir = Path("/usr/local/apache/conf/userdata/std/2")
            ssl_apache_dir = Path("/usr/local/apache/conf/userdata/ssl/2")
        else:
            print(red(f"Unsupported Apache version {apache_version}."))
            exit(1)
        
        # Determine the correct modsec_file path based on the presence of the domain flag
        if args.domain:
            modsec_file_std = std_apache_dir / args.user / args.domain / "modsec.conf"
            modsec_file_ssl = ssl_apache_dir / args.user / args.domain / "modsec.conf"
        else:
            modsec_file_std = std_apache_dir / args.user / "modsec.conf"
            modsec_file_ssl = ssl_apache_dir / args.user / "modsec.conf"
        
        # Ensure the directory and files exist
        modsec_file_std.parent.mkdir(parents=True, exist_ok=True)
        modsec_file_ssl.parent.mkdir(parents=True, exist_ok=True)
        modsec_file_std.touch(exist_ok=True)
        modsec_file_ssl.touch(exist_ok=True)
        
        if args.rule:
            for rule_id in args.rule:
                disable_rule(modsec_file_std, rule_id)
                disable_rule(modsec_file_ssl, rule_id)
        
        if args.drop:
            for rule_id in args.drop:
                remove_rule(modsec_file_std, rule_id)
                remove_rule(modsec_file_ssl, rule_id)

        if args.reset:
            if args.domain:
                reset_modsec_configuration(modsec_file_std)
                reset_modsec_configuration(modsec_file_ssl)
            else:
                reset_modsec_configuration(std_apache_dir / args.user / "modsec.conf")
                reset_modsec_configuration(ssl_apache_dir / args.user / "modsec.conf")
        
        if args.off:
            disable_all_rules(modsec_file_std)
            disable_all_rules(modsec_file_ssl)
        
    finally:
        if check_nginx():
            print(green("Nginx detected."))
            cpanel_run_ngxconf(args.user)
        cpanel_restart_apache()

# Function to disable all rules in modsec.conf
def disable_all_rules(modsec_file):
    if modsec_file.exists():
        with modsec_file.open("a") as file:
            file.write("SecRuleEngine Off\n")
        print(green(f"All rules disabled in file: {modsec_file}"))

# Main function to parse arguments and run needed functions based on server type  
def main():
    get_lock()  # Get the lock before proceeding
    try:
        parser = argparse.ArgumentParser(description="Disable modsec rules for Cpanel or PlatformI users and domains.")
        
        if platformI.exists():
            parser.add_argument("-r", "--rule", type=int, nargs='+', help="Specific rule IDs to disable")  
            parser.add_argument("-o", "--off", action="store_true", help="Disable all rules")   
            parser.add_argument("-R", "--reset", action="store_true", help="Reset the modsec configuration")  
            parser.add_argument("-D", "--drop", nargs='+', type=int, help="Drop/Remove a specific rule ID from the modsec configuration")  
        else:
            parser.add_argument("-u", "--user", type=str, required=True, help="CPanel user this is for")  
            parser.add_argument("-d", "--domain", type=str, help="The domain to modify rules for")  
            parser.add_argument("-r", "--rule", type=int, nargs='+', help="Specific rule IDs to disable")  
            parser.add_argument("-o", "--off", action="store_true", help="Disable all rules for the domain")  
            parser.add_argument("-R", "--reset", action="store_true", help="Reset the modsec configuration")  
            parser.add_argument("-a", "--all", action="store_true", help="Apply changes to all domains for the user")
            parser.add_argument("-D", "--drop", nargs='+', type=int, help="Drop/Remove a specific rule ID from the modsec configuration")   

        args = parser.parse_args()

        if platformI.exists():
            platformI_modsec(args)
        else:
            cpanel_modsec(args)

    finally:
        release_lock()  # Release the lock after the script completes

if __name__ == "__main__":
    main()

Zerion Mini Shell 1.0