Skip to content
View in the app

A better way to browse. Learn more.

AlphaGNU

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.

Fixing False Positive CWP Security Audit Alerts on AlmaLinux 9.x

Featured Replies

After recent CWP updates, some AlmaLinux 9.x servers may report false security alerts when running:

sh /scripts/cwp_security_audit

A typical false positive looks like this:

------------------------------------------------------
[INFO] Auditing cwpsrv (PID: 767572)
[OK] cwpsrv looks clean.
------------------------------------------------------
[INFO] Auditing php-fpm-cwp (PID: 710)
[SECURITY ALERT] Unknown/Untrusted file: /usr/lib64/gconv/gconv-modules.cache
Error:Can't add notification![SECURITY ALERT] Unauthorized port: php-fpm
Error:Can't add notification!------------------------------------------------------
[INFO] Auditing apache (PID: 768077)
[OK] apache looks clean.
------------------------------------------------------
[DONE] Security audit finished.

In this case the warning is misleading. On AlmaLinux 9.x, the file:

/usr/lib64/gconv/gconv-modules.cache

is a normal system file used by the GNU C Library character conversion system. The original CWP audit script does not include /usr/lib64/gconv/ in the allowed library paths, so it incorrectly reports this file as unknown or untrusted.

There is also a second parsing issue in the port audit section. The original script extracts listening ports using a simple awk -F':' expression against generic lsof output. In some cases this can incorrectly parse process-related text and produce an alert such as:

[SECURITY ALERT] Unauthorized port: php-fpm

Obviously, php-fpm is not a port number.

What needs to be fixed

There are two small changes that solve the false positives.

First, add this path to ALLOWED_LIB_PATHS:

"/usr/lib64/gconv/"

Second, replace the port audit line with a more precise lsof command that only checks TCP listening sockets:

local CURRENT_PORTS=$(lsof -Pan -p $PID -iTCP -sTCP:LISTEN 2>/dev/null | awk 'NR>1 {split($9,a,":"); print a[length(a)]}')

This avoids parsing unrelated lsof lines and prevents values like php-fpm from being treated as ports.


Patched version of /scripts/cwp_security_audit

Below is the corrected version. It keeps the original logic but fixes the AlmaLinux 9.x false positives.

#!/bin/bash

# --- CONFIGURATION ---
ALLOWED_LIB_PATHS=(
    "/usr/lib64/lib"
    "/usr/lib64/ld-"
    "/usr/local/ioncube/"
    "/usr/lib/locale/"
    "/usr/local/cwp/"
    "/usr/local/apache/modules/"
    "/usr/local/lib/"
    "/usr/lib64/gconv/"
)

ALLOWED_BINARIES=(
    "/usr/local/cwpsrv/bin/cwpsrv"
    "/usr/local/cwp/php71/sbin/php-fpm"
    "/usr/local/apache/bin/httpd"
)

ALLOWED_PORTS=("2030" "2031" "2082" "2083" "2086" "2087" "2095" "2096" "9000" "2302" "2304" "8181" "8443" "80" "443")

# --- INITIALIZATION ---
if ! command -v lsof &> /dev/null; then
    yum install -y lsof
fi

# --- FUNCTIONS ---

check_process() {
    local PROC_NAME=$1
    local SEARCH_PATTERN=$2
    local PID=$(ps aux | grep "$SEARCH_PATTERN" | grep -v grep | awk '{print $2}' | head -n 1)

    if [ -z "$PID" ]; then
        echo "[SKIP] Process '$PROC_NAME' not found."
        return
    fi

    echo "------------------------------------------------------"
    echo "[INFO] Auditing $PROC_NAME (PID: $PID)"
    local GLOBAL_ERROR=0

    # 1. Detect GHOST Files (DELETED or missing via stat)
    local GHOST_DATA=$(lsof -p $PID -n | grep -E "DEL|\(stat:" | grep -v "/dev/zero")
    if [ ! -z "$GHOST_DATA" ]; then
        echo "[!!! CRITICAL ALERT !!!] Ghost files (deleted but running) found:"
        echo "$GHOST_DATA"
        /usr/local/cwp/php71/bin/php /usr/local/cwpsrv/htdocs/resources/admin/include/libs/notifications/cli.php --level="danger" --subject="CWP Security Audit - Ghost files (deleted but running)" --message="[!!! CRITICAL ALERT !!!] Ghost files (deleted but running) found, for more info run:  sh /scripts/cwp_security_audit"
        GLOBAL_ERROR=1
    fi

    # 2. Deep Memory Audit (Path + RPM Package Check)
    local CURRENT_MEM=$(lsof -p $PID -n | grep "mem" | awk '{for(i=9;i<=NF;i++) printf "%s ", $i; print ""}' | sed 's/(stat:.*//' | xargs)
    
    for FILE in $CURRENT_MEM; do
        [[ -z "$FILE" || "$FILE" == "REG" || "$FILE" == "mem" || "$FILE" == "/" ]] && continue
        
        local MATCH=0

        for ALLOWED in "${ALLOWED_LIB_PATHS[@]}"; do
            if [[ "$FILE" == "$ALLOWED"* ]]; then MATCH=1; break; fi
        done

        for ALLOWED in "${ALLOWED_BINARIES[@]}"; do
            if [[ "$FILE" == "$ALLOWED" ]]; then MATCH=1; break; fi
        done

        if [ $MATCH -eq 0 ]; then
            echo "[SECURITY ALERT] Unknown/Untrusted file: $FILE"
            /usr/local/cwp/php71/bin/php /usr/local/cwpsrv/htdocs/resources/admin/include/libs/notifications/cli.php --level="danger" --subject="CWP Security Audit - Unknown/Untrusted file" --message="[SECURITY ALERT] Unknown/Untrusted file: $FILE"
            GLOBAL_ERROR=1
        else
            if [[ "$FILE" == "/usr/lib64/"* ]]; then
                if ! rpm -qf "$FILE" &>/dev/null; then
                    echo "[!!! DANGER !!!] File in system path but NOT owned by any package: $FILE"
                    /usr/local/cwp/php71/bin/php /usr/local/cwpsrv/htdocs/resources/admin/include/libs/notifications/cli.php --level="danger" --subject="CWP Security Audit - File in system path" --message="[!!! DANGER !!!] File in system path but NOT owned by any package, for more info run:  sh /scripts/cwp_security_audit"
                    GLOBAL_ERROR=1
                fi
            fi
        fi
    done

    # 3. Port Audit
    local CURRENT_PORTS=$(lsof -Pan -p $PID -iTCP -sTCP:LISTEN 2>/dev/null | awk 'NR>1 {split($9,a,":"); print a[length(a)]}')
    for PORT in $CURRENT_PORTS; do
        local PORT_MATCH=0

        for ALLOWED in "${ALLOWED_PORTS[@]}"; do
            if [ "$PORT" == "$ALLOWED" ]; then PORT_MATCH=1; break; fi
        done

        if [ $PORT_MATCH -eq 0 ]; then
            echo "[SECURITY ALERT] Unauthorized port: $PORT"
            /usr/local/cwp/php71/bin/php /usr/local/cwpsrv/htdocs/resources/admin/include/libs/notifications/cli.php --level="danger" --subject="CWP Security Audit - Unauthorized port: $PORT" --message="[SECURITY ALERT] Unauthorized port, for more info run:  sh /scripts/cwp_security_audit"
            GLOBAL_ERROR=1
        fi
    done

    [ $GLOBAL_ERROR -eq 0 ] && echo "[OK] $PROC_NAME looks clean."
}

# --- EXECUTION ---
check_process "cwpsrv" "cwpsrv: master process"
check_process "php-fpm-cwp" "php-fpm: master process .*cwpsrv.conf"
check_process "apache" "/usr/local/apache/bin/httpd"

echo "------------------------------------------------------"
echo "[DONE] Security audit finished."

Problem: CWP updates may overwrite the fix

CWP updates may overwrite /scripts/cwp_security_audit, so manually patching the file once is not always enough.

One practical solution is to keep a local fixed copy and automatically restore it if CWP replaces the file during an update.

The following installer creates:

/root/cwp-overrides/cwp_security_audit.fixed
/root/cwp-overrides/repair-cwp-security-audit.sh
/etc/systemd/system/cwp-security-audit-override.service
/etc/systemd/system/cwp-security-audit-override.path
/etc/cron.d/cwp-security-audit-override

The systemd.path unit watches /scripts/cwp_security_audit. If the file changes, the repair script compares it to the fixed version and restores the patched file if needed. A daily cron fallback is also added in case the file watch misses an event.


Installer script

Save this as:

install-cwp-security-audit-override.sh

Then run it as root:

chmod +x install-cwp-security-audit-override.sh
./install-cwp-security-audit-override.sh
#!/bin/bash
set -euo pipefail

# ============================================================
# CWP security audit override installer
# Restores the locally fixed /scripts/cwp_security_audit
# if CWP updates overwrite it.
# ============================================================

if [ "$(id -u)" -ne 0 ]; then
    echo "ERROR: This installer must be run as root."
    exit 1
fi

OVERRIDE_DIR="/root/cwp-overrides"
BACKUP_DIR="${OVERRIDE_DIR}/backups"
FIXED_FILE="${OVERRIDE_DIR}/cwp_security_audit.fixed"
REPAIR_SCRIPT="${OVERRIDE_DIR}/repair-cwp-security-audit.sh"
TARGET="/scripts/cwp_security_audit"
SERVICE_FILE="/etc/systemd/system/cwp-security-audit-override.service"
PATH_FILE="/etc/systemd/system/cwp-security-audit-override.path"
CRON_FILE="/etc/cron.d/cwp-security-audit-override"
LOG_FILE="/var/log/cwp-security-audit-override.log"

echo "------------------------------------------------------"
echo "[INFO] Installing CWP security audit override"
echo "------------------------------------------------------"

mkdir -p "$OVERRIDE_DIR" "$BACKUP_DIR"
chmod 700 "$OVERRIDE_DIR"
chmod 700 "$BACKUP_DIR"

if ! command -v lsof >/dev/null 2>&1; then
    echo "[INFO] lsof not found. Installing..."

    if command -v dnf >/dev/null 2>&1; then
        dnf install -y lsof
    elif command -v yum >/dev/null 2>&1; then
        yum install -y lsof
    else
        echo "WARNING: Neither dnf nor yum found. Please install lsof manually."
    fi
fi

if [ -f "$TARGET" ]; then
    INITIAL_BACKUP="${BACKUP_DIR}/cwp_security_audit.initial.$(date '+%Y%m%d-%H%M%S').bak"
    cp -a "$TARGET" "$INITIAL_BACKUP"
    echo "[INFO] Current target backed up to: $INITIAL_BACKUP"
else
    echo "[WARNING] Target file does not exist yet: $TARGET"
fi

cat > "$FIXED_FILE" <<'CWP_FIXED_SCRIPT'
#!/bin/bash

# --- CONFIGURATION ---
ALLOWED_LIB_PATHS=(
    "/usr/lib64/lib"
    "/usr/lib64/ld-"
    "/usr/local/ioncube/"
    "/usr/lib/locale/"
    "/usr/local/cwp/"
    "/usr/local/apache/modules/"
    "/usr/local/lib/"
    "/usr/lib64/gconv/"
)

ALLOWED_BINARIES=(
    "/usr/local/cwpsrv/bin/cwpsrv"
    "/usr/local/cwp/php71/sbin/php-fpm"
    "/usr/local/apache/bin/httpd"
)

ALLOWED_PORTS=("2030" "2031" "2082" "2083" "2086" "2087" "2095" "2096" "9000" "2302" "2304" "8181" "8443" "80" "443")

if ! command -v lsof &> /dev/null; then
    yum install -y lsof
fi

check_process() {
    local PROC_NAME=$1
    local SEARCH_PATTERN=$2
    local PID=$(ps aux | grep "$SEARCH_PATTERN" | grep -v grep | awk '{print $2}' | head -n 1)

    if [ -z "$PID" ]; then
        echo "[SKIP] Process '$PROC_NAME' not found."
        return
    fi

    echo "------------------------------------------------------"
    echo "[INFO] Auditing $PROC_NAME (PID: $PID)"
    local GLOBAL_ERROR=0

    local GHOST_DATA=$(lsof -p $PID -n | grep -E "DEL|\(stat:" | grep -v "/dev/zero")
    if [ ! -z "$GHOST_DATA" ]; then
        echo "[!!! CRITICAL ALERT !!!] Ghost files (deleted but running) found:"
        echo "$GHOST_DATA"
        /usr/local/cwp/php71/bin/php /usr/local/cwpsrv/htdocs/resources/admin/include/libs/notifications/cli.php --level="danger" --subject="CWP Security Audit - Ghost files (deleted but running)" --message="[!!! CRITICAL ALERT !!!] Ghost files (deleted but running) found, for more info run:  sh /scripts/cwp_security_audit"
        GLOBAL_ERROR=1
    fi

    local CURRENT_MEM=$(lsof -p $PID -n | grep "mem" | awk '{for(i=9;i<=NF;i++) printf "%s ", $i; print ""}' | sed 's/(stat:.*//' | xargs)
    
    for FILE in $CURRENT_MEM; do
        [[ -z "$FILE" || "$FILE" == "REG" || "$FILE" == "mem" || "$FILE" == "/" ]] && continue
        
        local MATCH=0

        for ALLOWED in "${ALLOWED_LIB_PATHS[@]}"; do
            if [[ "$FILE" == "$ALLOWED"* ]]; then MATCH=1; break; fi
        done

        for ALLOWED in "${ALLOWED_BINARIES[@]}"; do
            if [[ "$FILE" == "$ALLOWED" ]]; then MATCH=1; break; fi
        done

        if [ $MATCH -eq 0 ]; then
            echo "[SECURITY ALERT] Unknown/Untrusted file: $FILE"
            /usr/local/cwp/php71/bin/php /usr/local/cwpsrv/htdocs/resources/admin/include/libs/notifications/cli.php --level="danger" --subject="CWP Security Audit - Unknown/Untrusted file" --message="[SECURITY ALERT] Unknown/Untrusted file: $FILE"
            GLOBAL_ERROR=1
        else
            if [[ "$FILE" == "/usr/lib64/"* ]]; then
                if ! rpm -qf "$FILE" &>/dev/null; then
                    echo "[!!! DANGER !!!] File in system path but NOT owned by any package: $FILE"
                    /usr/local/cwp/php71/bin/php /usr/local/cwpsrv/htdocs/resources/admin/include/libs/notifications/cli.php --level="danger" --subject="CWP Security Audit - File in system path" --message="[!!! DANGER !!!] File in system path but NOT owned by any package, for more info run:  sh /scripts/cwp_security_audit"
                    GLOBAL_ERROR=1
                fi
            fi
        fi
    done

    local CURRENT_PORTS=$(lsof -Pan -p $PID -iTCP -sTCP:LISTEN 2>/dev/null | awk 'NR>1 {split($9,a,":"); print a[length(a)]}')
    for PORT in $CURRENT_PORTS; do
        local PORT_MATCH=0

        for ALLOWED in "${ALLOWED_PORTS[@]}"; do
            if [ "$PORT" == "$ALLOWED" ]; then PORT_MATCH=1; break; fi
        done

        if [ $PORT_MATCH -eq 0 ]; then
            echo "[SECURITY ALERT] Unauthorized port: $PORT"
            /usr/local/cwp/php71/bin/php /usr/local/cwpsrv/htdocs/resources/admin/include/libs/notifications/cli.php --level="danger" --subject="CWP Security Audit - Unauthorized port: $PORT" --message="[SECURITY ALERT] Unauthorized port, for more info run:  sh /scripts/cwp_security_audit"
            GLOBAL_ERROR=1
        fi
    done

    [ $GLOBAL_ERROR -eq 0 ] && echo "[OK] $PROC_NAME looks clean."
}

check_process "cwpsrv" "cwpsrv: master process"
check_process "php-fpm-cwp" "php-fpm: master process .*cwpsrv.conf"
check_process "apache" "/usr/local/apache/bin/httpd"

echo "------------------------------------------------------"
echo "[DONE] Security audit finished."
CWP_FIXED_SCRIPT

chmod 600 "$FIXED_FILE"

cat > "$REPAIR_SCRIPT" <<'REPAIR_SCRIPT'
#!/bin/bash
set -euo pipefail

TARGET="/scripts/cwp_security_audit"
FIXED="/root/cwp-overrides/cwp_security_audit.fixed"
BACKUP_DIR="/root/cwp-overrides/backups"
LOG="/var/log/cwp-security-audit-override.log"

mkdir -p "$BACKUP_DIR"

timestamp="$(date '+%Y-%m-%d %H:%M:%S')"

notify_cwp() {
    local level="$1"
    local subject="$2"
    local message="$3"

    if [ -x /usr/local/cwp/php71/bin/php ] && [ -f /usr/local/cwpsrv/htdocs/resources/admin/include/libs/notifications/cli.php ]; then
        /usr/local/cwp/php71/bin/php \
            /usr/local/cwpsrv/htdocs/resources/admin/include/libs/notifications/cli.php \
            --level="$level" \
            --subject="$subject" \
            --message="$message" \
            >/dev/null 2>&1 || true
    fi
}

if [ ! -f "$FIXED" ]; then
    echo "[$timestamp] ERROR: fixed file not found: $FIXED" >> "$LOG"
    notify_cwp "danger" \
        "CWP override error" \
        "Fixed CWP security audit file not found: $FIXED"
    exit 1
fi

if [ ! -f "$TARGET" ]; then
    echo "[$timestamp] WARNING: target file missing, restoring: $TARGET" >> "$LOG"
    install -m 755 "$FIXED" "$TARGET"

    notify_cwp "warning" \
        "CWP security audit restored" \
        "Target file was missing and has been restored: $TARGET"

    exit 0
fi

target_hash="$(sha256sum "$TARGET" | awk '{print $1}')"
fixed_hash="$(sha256sum "$FIXED" | awk '{print $1}')"

if [ "$target_hash" != "$fixed_hash" ]; then
    backup="$BACKUP_DIR/cwp_security_audit.$(date '+%Y%m%d-%H%M%S').bak"

    cp -a "$TARGET" "$backup"
    install -m 755 "$FIXED" "$TARGET"

    echo "[$timestamp] RESTORED: $TARGET was changed. Backup saved to: $backup" >> "$LOG"

    notify_cwp "warning" \
        "CWP override restored cwp_security_audit" \
        "CWP update changed /scripts/cwp_security_audit. The local fixed version was restored. Backup: $backup"
else
    echo "[$timestamp] OK: no change detected." >> "$LOG"
fi
REPAIR_SCRIPT

chmod 700 "$REPAIR_SCRIPT"

cat > "$SERVICE_FILE" <<'SERVICE_UNIT'
[Unit]
Description=Restore local fixed CWP security audit script if overwritten

[Service]
Type=oneshot
ExecStart=/root/cwp-overrides/repair-cwp-security-audit.sh
SERVICE_UNIT

chmod 644 "$SERVICE_FILE"

cat > "$PATH_FILE" <<'PATH_UNIT'
[Unit]
Description=Watch CWP security audit script for changes

[Path]
PathChanged=/scripts/cwp_security_audit
PathModified=/scripts/cwp_security_audit
Unit=cwp-security-audit-override.service

[Install]
WantedBy=multi-user.target
PATH_UNIT

chmod 644 "$PATH_FILE"

cat > "$CRON_FILE" <<'CRON_FALLBACK'
# CWP security audit override fallback check
# Runs daily in case systemd.path missed a file change.
17 3 * * * root /root/cwp-overrides/repair-cwp-security-audit.sh >/dev/null 2>&1
CRON_FALLBACK

chmod 644 "$CRON_FILE"

systemctl daemon-reload
systemctl enable --now cwp-security-audit-override.path

echo "[INFO] Running first repair/check..."
"$REPAIR_SCRIPT"

echo "------------------------------------------------------"
echo "[OK] Installation finished."
echo
echo "Status:"
systemctl --no-pager status cwp-security-audit-override.path || true
echo
echo "Last log entries:"
tail -n 10 "$LOG_FILE" 2>/dev/null || true
echo "------------------------------------------------------"

Verification

After installation, run:

systemctl status cwp-security-audit-override.path
tail -n 30 /var/log/cwp-security-audit-override.log
sha256sum /scripts/cwp_security_audit /root/cwp-overrides/cwp_security_audit.fixed

The two sha256sum values should be identical.

Then run the CWP audit again:

sh /scripts/cwp_security_audit

On a clean AlmaLinux 9.x server, the previous false alerts for:

/usr/lib64/gconv/gconv-modules.cache

and:

Unauthorized port: php-fpm

should be gone.


Notes

This does not disable the CWP security audit. It only fixes two false-positive conditions:

  1. missing allowed path for /usr/lib64/gconv/,

  2. unsafe parsing of listening ports from generic lsof output.

The script also keeps backups of any CWP-provided version that gets overwritten, so you can later compare what changed after an update:

ls -lah /root/cwp-overrides/backups/

This approach is safer than using:

chattr +i /scripts/cwp_security_audit

because CWP updates are not blocked. The update can complete normally, and the local fixed version is restored afterwards.

Create an account or sign in to comment

Account

Navigation

Search

Search

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.