Last active
January 28, 2025 18:52
-
-
Save cPFence/041ef1ba10a41d62bd2a4d6cee2dae41 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# | |
# Written by: cPFence Team / https://cpfence.app/ | |
# | |
# | |
# Description: | |
# This script is designed to work with the Enhance Control Panel to ensure | |
# that custom OpenLiteSpeed (OLS) configurations persist across vhost rebuilds. | |
# Using a simple freeze/unfreeze mechanism, it allows administrators to lock | |
# their custom configurations and prevent them from being overwritten by the | |
# default settings applied during vhost creation or updates. | |
# MD5 checksum validation is added to detect changes and prevent redundant updates. | |
# | |
# [WARNING] !!! | |
# If the configuration is frozen, you **must unfreeze** before making changes | |
# to cPFence WAF settings (e.g., turning WAF on or off). Failure to unfreeze | |
# will cause the cron job to enter a loop and continuously restart OpenLiteSpeed. | |
# Always unfreeze before making changes, and freeze again afterward. | |
# | |
# Usage: | |
# 1) Make your edits in the OpenLiteSpeed admin panel. https://your.domain.com:7080/ | |
# 2) Run: `./cPFence_ols_freeze.sh freeze` to freeze and save your changes. | |
# 3) If you need to make changes through the admin panel again: | |
# - Run: `./cPFence_ols_freeze.sh unfreeze` | |
# - Stop the cron job to avoid overwriting configurations during edits. | |
# 4) After completing your edits, run: `./cPFence_ols_freeze.sh freeze` again. | |
# 5) Set up a cron job to run the script with no arguments: | |
# - Cron Command: `* * * * * /path/to/cPFence_ols_freeze.sh` | |
# | |
# Example Workflow: | |
# - Make edits in the OLS admin panel → Freeze the configuration. | |
# - Unfreeze before making further changes → Stop the cron temporarily. | |
# - Freeze again after updates → Resume the cron job. | |
# | |
# Disclaimer: | |
# This script is provided "as is" without any warranties of any kind, express or implied. | |
# It is recommended to thoroughly test this script in a non-production environment prior to | |
# deployment on any live or critical systems. cPFence and Linkers Gate LLC are not liable for | |
# any damage or data loss resulting from the use of this script. | |
# | |
################################################################################ | |
# USER CONFIGURABLE SECTION # | |
################################################################################ | |
# Using Enhance v12 option (set to 'on' when Enhance v12 is released) | |
Using_Enhance_v12="off" # "on" or "off" | |
CONTAINER_NAME="openlitespeed" | |
# Path to the main OLS config | |
CONFIG_PATH="/usr/local/lsws/conf/httpd_config.conf" | |
# Backup directory for storing the full config before changes | |
BACKUP_DIR="/usr/local/lsws/conf/" | |
# Directory where we store freeze-related files | |
FREEZE_DIR="/usr/local/src" | |
# The file storing the ols settings that we freeze | |
FROZEN_TOP_FILE="$FREEZE_DIR/ols_top_config_frozen.conf" | |
# The MD5 we store for the entire config at freeze time | |
FROZEN_MD5_FILE="$FREEZE_DIR/ols_config_frozen.md5" | |
# The "marker" file indicating the config is currently frozen | |
FREEZE_MARKER="$FREEZE_DIR/.ols_frozen" | |
# Retention period for old backups (in days) | |
RETENTION_DAYS=7 | |
################################################################################ | |
# DO NOT EDIT BELOW THIS LINE # | |
################################################################################ | |
# Decide Docker vs local | |
if [ "$Using_Enhance_v12" = "on" ]; then | |
docker_cmd="" | |
else | |
docker_cmd="docker exec $CONTAINER_NAME " | |
fi | |
MODE="$1" # freeze, unfreeze, or blank | |
# welcome message | |
display_welcome() | |
{ | |
echo "**********************************************************************************************" | |
echo "* cPFence Web Security *" | |
echo "* cPFence OpenLiteSpeed Freeze *" | |
echo "* Copyright (C) 2023 - 2025 Linkers Gate LLC. *" | |
echo "**********************************************************************************************" | |
} | |
display_welcome | |
############################################################################### | |
# Helper: Check if config file exists | |
############################################################################### | |
check_config_exists() { | |
if ! $docker_cmd test -f "$CONFIG_PATH" >/dev/null 2>&1; then | |
echo "ERROR: OLS config file not found at: $CONFIG_PATH" | |
exit 1 | |
fi | |
} | |
############################################################################### | |
# Cleanup: Remove old backup files | |
############################################################################### | |
cleanup_old_backups() { | |
echo "Cleaning up backup files older than $RETENTION_DAYS days in $BACKUP_DIR..." | |
local deleted_files | |
deleted_files=$($docker_cmd find "$BACKUP_DIR" -name "httpd_config_backup-*.conf" -type f -mtime +$RETENTION_DAYS -print -exec rm -f {} \;) | |
if [ -n "$deleted_files" ]; then | |
echo "Cleanup complete. The following old backup files were removed:" | |
echo "$deleted_files" | |
else | |
echo "No old backup files found for cleanup." | |
fi | |
} | |
############################################################################### | |
# 1) freeze_config | |
# - Backup entire config | |
# - Store current OLS settings in a file | |
# - Compute MD5 of entire config, store it | |
# - Touch marker | |
############################################################################### | |
freeze_config() { | |
check_config_exists | |
# (1) Cleanup old backups | |
cleanup_old_backups | |
# (2) Backup entire config | |
local TIMESTAMP | |
TIMESTAMP="$(date +'%d%m%y-%H%M%S')" | |
local BACKUP_FILE="httpd_config_backup-${TIMESTAMP}.conf" | |
echo "Backing up current config to: ${BACKUP_DIR}${BACKUP_FILE}" | |
$docker_cmd cp -a "$CONFIG_PATH" "${BACKUP_DIR}${BACKUP_FILE}" | |
# (3) Extract current OLS settings | |
local TMP_TOP="/tmp/ols_top_portion.$$" | |
# We'll stop printing as soon as we see "virtualhost Example {" | |
$docker_cmd sh -c "awk ' | |
/virtualhost[[:space:]]+Example[[:space:]]*{/ { | |
exit | |
} | |
{ print \$0 } | |
' \"$CONFIG_PATH\" > \"$TMP_TOP\"" | |
# Copy current OLS settings from container to host if needed | |
if [ "$Using_Enhance_v12" = "on" ]; then | |
cp "$TMP_TOP" "$FROZEN_TOP_FILE" | |
else | |
docker cp "$CONTAINER_NAME:$TMP_TOP" "$FROZEN_TOP_FILE" | |
$docker_cmd rm -f "$TMP_TOP" >/dev/null 2>&1 | |
fi | |
echo "Stored current OLS settings in: $FROZEN_TOP_FILE" | |
# (4) Compute MD5 of entire config | |
local CURRENT_MD5 | |
if [ "$Using_Enhance_v12" = "on" ]; then | |
CURRENT_MD5="$(md5sum "$CONFIG_PATH" | awk '{print $1}')" | |
else | |
CURRENT_MD5="$($docker_cmd md5sum "$CONFIG_PATH" | awk '{print $1}')" | |
fi | |
echo "$CURRENT_MD5" > "$FROZEN_MD5_FILE" | |
echo "Stored config MD5 in: $FROZEN_MD5_FILE" | |
# (5) Touch marker | |
touch "$FREEZE_MARKER" | |
echo "CONFIG FROZEN. Future runs will enforce your current OLS settings." | |
# (6) Display warning after successful freeze | |
echo "[WARNING] You must unfreeze the configuration before making changes to OLS settings or cPFence WAF settings (e.g., turning WAF on or off)." | |
echo "[WARNING] Failure to do so will cause the cron job to undo your changes and enter a loop, causing cPFence to keep restarting OLS every 5 minutes!" | |
exit 0 | |
} | |
############################################################################### | |
# 2) unfreeze_config | |
# - Remove freeze marker & stored files so changes won't be reverted | |
############################################################################### | |
unfreeze_config() { | |
if [ -f "$FREEZE_MARKER" ]; then | |
rm -f "$FREEZE_MARKER" 2>/dev/null | |
rm -f "$FROZEN_TOP_FILE" 2>/dev/null | |
rm -f "$FROZEN_MD5_FILE" 2>/dev/null | |
echo "Unfrozen. Future runs won't revert any changes." | |
else | |
echo "Already unfrozen (no freeze marker found)." | |
fi | |
exit 0 | |
} | |
############################################################################### | |
# 3) enforce_freeze_if_needed | |
# - If not frozen, do nothing. | |
# - If frozen: | |
# * Compare entire config MD5 to stored MD5. | |
# * If any difference is found, it backs up the file, then replaces only the settings from your frozen copy | |
# preserving the rest (the vhost portion). Finally, it restarts OLS. | |
############################################################################### | |
enforce_freeze_if_needed() { | |
# If not frozen, exit | |
if [ ! -f "$FREEZE_MARKER" ]; then | |
echo "Not frozen. No action taken." | |
exit 0 | |
fi | |
check_config_exists | |
# Ensure we have a frozen settings and MD5 | |
if [ ! -f "$FROZEN_TOP_FILE" ] || [ ! -f "$FROZEN_MD5_FILE" ]; then | |
echo "ERROR: Freeze marker exists, but missing frozen settings or MD5 file!" | |
exit 1 | |
fi | |
# Compare entire config's MD5 | |
local CURRENT_MD5 | |
if [ "$Using_Enhance_v12" = "on" ]; then | |
CURRENT_MD5="$(md5sum "$CONFIG_PATH" | awk '{print $1}')" | |
else | |
CURRENT_MD5="$($docker_cmd md5sum "$CONFIG_PATH" | awk '{print $1}')" | |
fi | |
local STORED_MD5 | |
STORED_MD5="$(cat "$FROZEN_MD5_FILE" 2>/dev/null || echo "")" | |
if [ "$CURRENT_MD5" = "$STORED_MD5" ]; then | |
echo "No changes detected (MD5 match). Exiting." | |
exit 0 | |
fi | |
echo "Changes detected! Reverting stored OLS settings..." | |
# Cleanup old backups | |
cleanup_old_backups | |
# (1) Backup entire config | |
local TIMESTAMP | |
TIMESTAMP="$(date +'%d%m%y-%H%M%S')" | |
local BACKUP_FILE="httpd_config_backup-${TIMESTAMP}.conf" | |
echo "Backing up changed config to: ${BACKUP_DIR}${BACKUP_FILE}" | |
$docker_cmd cp -a "$CONFIG_PATH" "${BACKUP_DIR}${BACKUP_FILE}" | |
# (2) Extract vhost from the current config | |
local TMP_SECOND_HALF="/tmp/ols_second_half.$$" | |
$docker_cmd sh -c "awk ' | |
/virtualhost[[:space:]]+Example[[:space:]]*{/ { | |
found=1 | |
} | |
found==1 { | |
print \$0 | |
} | |
' \"$CONFIG_PATH\" > \"$TMP_SECOND_HALF\"" | |
# Copy the vhost portion to host if needed | |
local SECOND_HALF_HOST="/tmp/ols_second_half_host.$$" | |
if [ "$Using_Enhance_v12" = "on" ]; then | |
cp "$TMP_SECOND_HALF" "$SECOND_HALF_HOST" | |
else | |
docker cp "$CONTAINER_NAME:$TMP_SECOND_HALF" "$SECOND_HALF_HOST" | |
$docker_cmd rm -f "$TMP_SECOND_HALF" >/dev/null 2>&1 | |
fi | |
# (3) Combine the stored ols settings + the vhost portion | |
local TMP_NEW_CONF="/tmp/ols_new_conf.$$" | |
cat "$FROZEN_TOP_FILE" "$SECOND_HALF_HOST" > "$TMP_NEW_CONF" | |
# (4) Overwrite the config in the container or host | |
if [ "$Using_Enhance_v12" = "on" ]; then | |
# Copy and preserve permissions and ownership in one step | |
install -m 0750 -o lsadm -g webserver "$TMP_NEW_CONF" "$CONFIG_PATH" | |
else | |
docker cp "$TMP_NEW_CONF" "$CONTAINER_NAME:$CONFIG_PATH" | |
# Ensure permissions and ownership are correct inside the container | |
docker exec "$CONTAINER_NAME" chown lsadm:webserver "$CONFIG_PATH" | |
docker exec "$CONTAINER_NAME" chmod 0750 "$CONFIG_PATH" | |
fi | |
# Cleanup | |
rm -f "$SECOND_HALF_HOST" "$TMP_NEW_CONF" 2>/dev/null | |
# (5) Restart OLS | |
${docker_cmd}/usr/local/lsws/bin/lswsctrl restart | |
echo "OpenLiteSpeed restarted with the reverted (frozen) settings." | |
# (6) Compute new MD5 of entire config and update the value | |
local CURRENT_MD5 | |
if [ "$Using_Enhance_v12" = "on" ]; then | |
CURRENT_MD5="$(md5sum "$CONFIG_PATH" | awk '{print $1}')" | |
else | |
CURRENT_MD5="$($docker_cmd md5sum "$CONFIG_PATH" | awk '{print $1}')" | |
fi | |
echo "$CURRENT_MD5" > "$FROZEN_MD5_FILE" | |
echo "Updated the new MD5 in: $FROZEN_MD5_FILE" | |
} | |
############################################################################### | |
# MAIN | |
############################################################################### | |
case "$MODE" in | |
freeze) | |
freeze_config | |
;; | |
unfreeze) | |
unfreeze_config | |
;; | |
*) | |
enforce_freeze_if_needed | |
;; | |
esac |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment