Mini Shell
#!/usr/bin/bash
if [ "$IM360_DEBUG_SHELL" = "1" ]; then
echo "IM360_ARGV: <$0 [$@]>"
set -x
else
:
fi
# For backwards compatibility with CentOS 6 only.
WEBSHIELD='imunify360-webshield'
JOBFILE=/etc/cron.d/imunify360-webshield-check
STATEFILE="/usr/share/imunify360-webshield/.webshieldctl.status"
VIRTSERVER_CONF="/etc/imunify360-webshield/virtserver.conf"
WEBSHIELD_ANTIBOT_CONF="/etc/imunify360-webshield/splashscreen-antibot.conf"
MODE='undefined'
SSL_UNIT='imunify360-webshield-ssl-cache'
WAFD_UNIT='imunify360-wafd'
MAIN_UNIT='undefined'
OUR_MODULES_DIR=/usr/share/imunify360-webshield/modules
NGINX_MODULE_CONF=/etc/nginx/modules-enabled/40-imunify360-access-checker.conf
NGINX_CHECKER_CONF=/etc/nginx/conf.d/imunify360-access-checker.conf
PREVIOUS_MODE_STATE_PATH=/usr/share/imunify360-webshield/.previous_mode
STATE_PATH=/usr/share/imunify360-webshield/modularity_mode
readonly COMMON_CONF=/etc/sysconfig/imunify360/imunify360-merged.config
# Returns code 0 if webshield is enabled and code 1 otherwise.
# On any errors we suppose that webshield is enabled, which is the default value.
# Refer to: defence360:config_schema/firewall.py.
# WARN: According to the YAML standard, all maps must contains unique keys only.
# But Python implementation silently overwrites the values with the same keys.
# This parser behaves exactly the same - it returns the last value as the result.
is_enabled_in_config() {
[ -s "$COMMON_CONF" ] || return 0
local value=$(
awk -F: '
/^WEBSHIELD:$/ {
section_found=1;
next;
};
section_found == 1 && /^[A-Z][A-Z0-9_-]*:$/ {
section_over=1;
next;
};
section_found == 1 && section_over != 1 && /^[[:space:]]+enable:/ {
gsub(/[[:space:]]/, "", $2);
print $2;
}' "$COMMON_CONF"
)
[ "$value" = 'false' ] && return 1 || return 0
}
is_cloudways() {
hostname -f 2>/dev/null | grep -qE '^.+\.(cloudwaysapps|cloudwaysstagingapps)\.com$' && return 0
[ -x /usr/local/sbin/apm ] && /usr/local/sbin/apm info | grep -qi cloudways
}
detect_mode() {
local mode='standalone'
# TODO: At the moment, module-based nginx is available for CloudWays only.
if is_cloudways; then
mode='nginx'
fi
if [ -s "$STATE_PATH" ] && [ "$1" = 'skip-on-update' ]; then
# Do not override config on packages update.
mode=$(<"$STATE_PATH")
echo "Module-based config left untouched with mode: '$mode'."
else
echo $mode >| $STATE_PATH
echo "Module-based mode set to: '$mode'."
fi
}
load_mode() {
local mode='undefined'
local unit='undefined'
local value=''
[ -s "$STATE_PATH" ] && value=$(<"$STATE_PATH")
case "$value" in
nginx)
mode="$value"
unit="$value"
;;
apache)
mode="$value"
unit=$(get_unit)
;;
*)
mode='standalone'
unit='imunify360-webshield'
;;
esac
export MODE="$mode"
export MAIN_UNIT="$unit"
}
is_cpanel(){
if [ -x /usr/local/cpanel/cpanel ];then
return 0
fi
return 1
}
is_redhat(){
if [ -e /etc/redhat-release ];then
return 0
fi
return 1
}
has_apache(){
if which apachectl &>/dev/null; then
return 0
fi
return 1
}
is_supported(){
if has_apache && is_cpanel && [ ! -d /usr/local/lsws ]; then
[ "$1" = "out" ] && echo yes
return 0
else
[ "$1" = "out" ] && echo no
return 1
fi
}
get_apache_root(){
echo $(apachectl -V | awk -F= '/HTTPD_ROOT/{gsub("\042", "", $2);print $2}')
}
put_apache_conf(){
if is_supported; then
local root=$(get_apache_root)
if is_redhat;then
if [ -d $root/conf.d ];then
cp /usr/share/imunify360-webshield/access_checker.conf $root/conf.d
fi
else
# By default debian-based apache has modules configs in ROOT/mods-available folder
# and enables the modules with placing a symlink to the config file into ROOT/mods-enabled.
# Ubuntu-based cpanel keeps configs in ROOT/conf.d
if [ -d $root/conf.d ];then
cp /usr/share/imunify360-webshield/access_checker.conf $root/conf.d
elif [ -d $root/mods-available ] && [ -d $root/mods-enabled ];then
cp /usr/share/imunify360-webshield/access_checker.conf $root/mods-available
ln -s $root/mods-available/access_checker.conf $root/mods-enabled
# But we've seen different debian apache setups
elif [ -d $root/mods-enabled ];then
cp /usr/share/imunify360-webshield/access_checker.conf $root/mods-enabled
fi
fi
fi
}
remove_apache_conf(){
local root=$(get_apache_root)
if is_redhat;then
rm -f $root/conf.d/access_checker.conf
else
if [ -d $root/conf.d ];then
rm -f $root/conf.d/access_checker.conf
else
rm -f $root/mods-enabled/access_checker.conf
fi
fi
}
check_apache_config(){
apachectl configtest
}
get_unit(){
if is_redhat;then
# Both ea-apache and httpd packages supply the same unit: httpd
echo httpd
else
# Ubuntu-based cPanel runs httpd unit. However, it returns 0 even on missing unit file
# So we check output '0/1 unit files listed'
if [ $(systemctl list-unit-files httpd.service 2>&1 | awk '/unit files listed/{print $1}') = '1' ];then
echo httpd
else
echo apache2
fi
fi
}
# the webshield is running as a separate entity
is_standalone() {
[ "$MODE" = 'standalone' ]
}
# We supplied our module for a hoster's NGINX. No webshield is running
is_nginx(){
[ "$MODE" = nginx ]
}
# We supplied our module for a hoster's Apache.
# Webshield is running as well and serves non-HTTP/HTTPS ports
is_apache(){
[ "$MODE" = apache ]
}
cleanup_configs() {
case "$MODE" in
nginx)
rm -vf "$NGINX_MODULE_CONF" "$NGINX_CHECKER_CONF"
;;
apache)
remove_apache_conf
;;
esac
}
prepare_nginx() {
local version=$(nginx -v 2>&1 | grep -oP '(\d+[.]){2}\d+')
local mod_path="$OUR_MODULES_DIR"/"ngx_http_access_checker_module_${version}.so"
if ! [ -s "$mod_path" ]; then
echo "ERROR: Unable to find nginx module for version '$version'." >&2
return 1
fi
# WARN: Explicit check that the path is exist and it is a directory
# cause simple 'echo' to a file will raise error with an ambiguous message
# "No such file or directory" like you're trying to *READ* from the file.
# "dirname" does not check type of its argument and just cut everything
# following the last slash.
local path=''
for path in "$NGINX_MODULE_CONF" "$NGINX_CHECKER_CONF"; do
if ! [ -d $(dirname "$path") ]; then
echo "ERROR: Unable to find directory for config '$path'." >&2
return 1
fi
done
if ! nginx -t ; then
echo "ERROR: Invalid nginx config before installing module." >&2
return 1
fi
echo "load_module ${mod_path};" >| "$NGINX_MODULE_CONF" || return 1
echo 'access_checker unix:/var/run/imunify360/libiplists-daemon.sock;' >| "$NGINX_CHECKER_CONF" || return 1
if ! nginx -t ; then
echo "ERROR: Invalid nginx config after installing module." >&2
return 1
fi
}
prepare_configs() {
is_enabled_in_config || return 0
case "$MODE" in
nginx)
prepare_nginx
;;
apache)
put_apache_conf
;;
esac
}
check_nginx() {
[ -s "$NGINX_MODULE_CONF" ] || return 1
[ -s "$NGINX_CHECKER_CONF" ] || return 1
nginx -T 2> /dev/null | grep -qE '^access_checker\s+unix:/[^.]+[.]sock;$' || return 1
local pid=''
pid=$(pgrep -f 'nginx:\s+master') || return 1
# pid=1 is acquired by init.
[[ "$pid" =~ ^([2-9]|[1-9][0-9]+)$ ]] || return 1
grep -qP '/ngx_http_access_checker_module_(\d+[.]){2}\d+[.]so$' /proc/"$pid"/maps
}
check_configs() {
case "$MODE" in
nginx)
check_nginx
;;
apache)
check_apache_config
;;
esac
}
has_hosting_panel(){
local checks=(
/usr/local/cpanel/cpanel
/usr/sbin/plesk
/usr/local/directadmin/custombuild/build
)
for i in ${checks[@]};do
[ -e $i ] && return 0
done
return 1
}
count_processes(){
local count=$(ps aux | grep -c '[i]m360:\|webshield-[s]sl-cache')
echo $count
}
check_running(){
# for hosts with hosting panels we expect 5 processes to be running.
# otherwise 4 ones (ssl-cache is not expected to be run on no-panel hosts)
local num=$(count_processes)
local expected
if has_hosting_panel; then expected=3; else expected=2; fi
[ $num -ge $expected ] && return 0
return 1
}
check_stopped(){
local num=$(count_processes)
[ $num -eq 0 ] && return 0
return 1
}
enable_for_systemd(){
systemctl enable $WAFD_UNIT || return $?
systemctl enable $SSL_UNIT || return $?
# Webshield should be enabled and running in standalone mode to serve all ports
# and in Apache mode to serve non-standard HTTP/HTTPS ports like cPanel ports.
is_nginx || systemctl enable $WEBSHIELD
}
safe_reload() {
# WARN: In some cases 'reload-or-restart' action is not applicable
# and need to be done directly.
if systemctl --quiet is-active $1 ; then
systemctl reload $1
else
systemctl restart $1
fi
}
start_for_systemd(){
systemctl start $WAFD_UNIT || return $?
if ! systemctl start $SSL_UNIT; then
# WARN: Ignore error cause ssl-cache will exit if there is no panel.
has_hosting_panel && return 1 || :
fi
if is_nginx;then
prepare_configs && systemctl reload $MAIN_UNIT && return 0
cleanup_configs
return 1
elif is_apache;then
# In apache mode we expect both apache and webshield are running: apache
# for HTTP/HTTPS ports and webshield for the rest of them (like 2082/2083)
# WARN: Sometimes Apache unit can not be reloaded and must to be restarted.
prepare_configs && safe_reload $MAIN_UNIT && systemctl start $WEBSHIELD && return 0
cleanup_configs
return 1
elif is_standalone;then
systemctl start $MAIN_UNIT
else
echo "Configuration error: unknown mode: $MODE"
return 1
fi
}
activate_for_systemd(){
local RV
enable_for_systemd
RV=$?
[ $RV -ne 0 ] && return $RV
start_for_systemd
}
disable_for_systemd(){
local failed=0
# we don't disable wafd because it's used
# for ip-list functionality
systemctl disable $SSL_UNIT || failed=1
if is_standalone; then
systemctl disable $MAIN_UNIT || failed=1
else
cleanup_configs || failed=1
systemctl reload $MAIN_UNIT || failed=1
if is_apache; then
# Webshield is enabled in Apache mode to serve
# non-standard HTTP/HTTPS ports like cPanel ports.
systemctl disable $WEBSHIELD || failed=1
fi
fi
return $failed
}
stop_for_systemd(){
local failed=0
local wafd_force=$1
# we stop wafd only in 'restart' action, i.e stop with
# subsequent start
if [ "x$wafd_force" = xforce ];then
systemctl stop $WAFD_UNIT || failed=1
fi
systemctl stop $SSL_UNIT || failed=1
if is_nginx;then
cleanup_configs || failed=1
systemctl reload $MAIN_UNIT || failed=1
elif is_apache;then
# stopping in apache mode means that we unload access_checker
# module from apache server and stop webshield
cleanup_configs || failed=1
systemctl reload $MAIN_UNIT || failed=1
systemctl stop $WEBSHIELD || failed=1
elif is_standalone;then
systemctl stop $MAIN_UNIT || failed=1
fi
return $failed
}
deactivate_for_systemd(){
local RV
stop_for_systemd
RV=$?
[ $RV -ne 0 ] && return $RV
disable_for_systemd
}
do_terminate(){
stop_for_systemd force
local RV0=$?
disable_for_systemd
local RV=$?
# We run both command but return success only if both of them succeded
if [ $RV -ne 0 ]; then
return $RV
fi
return $RV0
}
do_enable(){
enable_for_systemd
local rv=$?
if [ $rv == 0 ]; then
echo "enabled" > $STATEFILE
fi
return $rv
}
do_disable(){
disable_for_systemd
local rv=$?
if [ $rv == 0 ]; then
echo "disabled" > $STATEFILE
fi
return $rv
}
do_start(){
start_for_systemd
local rv=$?
if [ $rv == 0 ]; then
echo "started" > $STATEFILE
fi
return $rv
}
do_stop(){
local force=$1
stop_for_systemd $force
local rv=$?
if [ $rv == 0 ]; then
echo "stopped" > $STATEFILE
fi
return $rv
}
do_activate(){
activate_for_systemd
local rv=$?
if [ $rv == 0 ]; then
echo "activated" > $STATEFILE
fi
return $rv
}
do_deactivate(){
deactivate_for_systemd
local rv=$?
if [ $rv == 0 ]; then
echo "deactivated" > $STATEFILE
fi
return $rv
}
is_enabled(){
# We don't care if ssl-cache unit is enabled. Webshield will start it
# by 'Wants' dependency before its own start. If it's not required for
# given environment (no known hosting panel) it'll exit then.
local failed=0
if systemctl -q is-enabled $WAFD_UNIT; then
echo "Unit '$WAFD_UNIT' is enabled."
else
echo "ERROR: Unit '$WAFD_UNIT' is NOT enabled." >&2
failed=1
fi
# $MAIN_UNIT is either imunify360-webshield (in standalone mode) or a host
# webserver. It seems that there's no sense to check if host webserver is
# enabled as we don't have control over it at any rate.
if is_standalone; then
if systemctl -q is-enabled $MAIN_UNIT; then
echo "Unit '$MAIN_UNIT' is enabled."
else
echo "Unit '$MAIN_UNIT' is NOT enabled." >&2
failed=1
fi
else
# WARN: Webshield is always disabled for Cloudways, so its state must not be used as the result.
is_cloudways && check_nginx || systemctl -q is-enabled $WEBSHIELD
failed=$?
fi
return $failed
}
is_active(){
if is_standalone; then
if check_running; then
echo "$MAIN_UNIT is running"
return 0
else
echo "$MAIN_UNIT is not running"
return 1
fi
fi
local name='undefined'
local state='undefined'
local units=($MAIN_UNIT $WAFD_UNIT $SSL_UNIT)
local failed=0
for name in ${units[@]}; do
state="$(systemctl is-active $name 2>&1)"
if [ "$state" = "active" ]; then
echo "Unit '$name' is active."
else
# WARN: ssl-cache must be processed in its very special way, see below.
if ! [ "$name" = $SSL_UNIT ]; then
echo "ERROR: Unit '$name' is NOT active, result='$state'." >&2
failed=1
continue
fi
if has_hosting_panel; then
echo "ERROR: Unit '$SSL_UNIT' is NOT active, result '$state'." >&2
failed=1
else
echo "WARNING: Unit '$SSL_UNIT' is NOT active, result '$state' (ignored)." >&2
fi
fi
done
if [ $failed -eq 0 ]; then
if ! check_configs; then
echo "ERROR: Web server is not configured properly." >&2
failed=1
fi
fi
return $failed
}
do_restart(){
do_stop force
do_start
}
do_enable_splashscreen(){
is_standalone || return 0
local ss_state=$(awk '$1 == "splashscreen_antibot" {gsub(";","",$2);print $2}' $WEBSHIELD_ANTIBOT_CONF)
if [ "$ss_state" = on ];then
echo "splashscreen is already enabled"
return 0
fi
sed -i -e "/splashscreen_antibot/ {s/off/on/}" $WEBSHIELD_ANTIBOT_CONF
do_restart
}
do_disable_splashscreen(){
is_standalone || return 0
local ss_state=$(awk '$1 == "splashscreen_antibot" {gsub(";","",$2);print $2}' $WEBSHIELD_ANTIBOT_CONF)
if [ "$ss_state" = off ];then
echo "splashscreen is already disabled"
return 0
fi
sed -i -e "/splashscreen_antibot/ {s/on/off/}" $WEBSHIELD_ANTIBOT_CONF
do_restart
}
do_enable_cpanelprotection(){
is_standalone || return 0
local cp_state=$(awk '$2 == "$cpanel_protection" {gsub(";","",$3);print $3}' $VIRTSERVER_CONF)
if [ "$cp_state" = 1 ];then
echo "cpanel_protection is already enabled"
return 0
fi
sed -i -e '/$cpanel_protection/ {s/0/1/}' $VIRTSERVER_CONF
do_restart
}
do_disable_cpanelprotection(){
is_standalone || return 0
local cp_state=$(awk '$2 == "$cpanel_protection" {gsub(";","",$3);print $3}' $VIRTSERVER_CONF)
if [ "$cp_state" = 0 ];then
echo "cpanel_protection is already disabled"
return 0
fi
sed -i -e '/$cpanel_protection/ {s/1/0/}' $VIRTSERVER_CONF
do_restart
}
do_reload(){
# WARN: Ignoring errors here is for the compatibility with sysvinit script
# which contains bug - on action "reload" it always exits with code 0.
# try-reload-or-restart is supported only from version 229,
# which is not the case for Centos7. The difference between try-reload-or-restart
# and reload-or-restart is that former does nothing unless the service is running.
if systemctl --quiet is-active $SSL_UNIT;then
systemctl reload-or-restart $SSL_UNIT || :
fi
if systemctl --quiet is-active $WAFD_UNIT;then
systemctl reload-or-restart $WAFD_UNIT || :
fi
if systemctl --quiet is-active $MAIN_UNIT;then
systemctl reload-or-restart $MAIN_UNIT || :
fi
}
# Enable standalone mode
set_standalone(){
if ! is_standalone;then
echo "$MODE" >| "$PREVIOUS_MODE_STATE_PATH"
echo standalone >| "$STATE_PATH"
do_stop force
load_mode
do_start
else
echo "Required mode is already applied. Nothing to do"
return 1
fi
}
# Returned to the previous state if we have state backup file
# otherwise switch to apache mode if supported
unset_standalone(){
if is_standalone; then
if [ -s "$PREVIOUS_MODE_STATE_PATH" ]; then
local prev_mode=$(<"$PREVIOUS_MODE_STATE_PATH")
if [ -n "$prev_mode" ] && [ "$prev_mode" != standalone ]; then
echo -n "$prev_mode" >| "$STATE_PATH" || return 1
fi
rm -f "$PREVIOUS_MODE_STATE_PATH"
elif is_supported; then
echo apache >| $STATE_PATH
fi
do_stop force
load_mode
do_start
else
echo "Required mode is already applied. Nothing to do"
return 1
fi
}
configure() {
deactivate_for_systemd || true
systemctl unmask imunify360-webshield || true
detect_mode 'skip-on-update'
load_mode
is_cloudways && systemctl mask imunify360-webshield || true
activate_for_systemd
}
print_help(){
echo "enable : enables webshield starting on boot (without actully starting it)"
echo "is-enabled : shows if the webshield is enabled to start on boot"
echo "is-active : shows if the webshield is running now"
echo "disable : disables webshield starting on boot (without actully stopping it)"
echo "start : starts webshield (without enabling its starting on boot)"
echo "stop : stops webshield (without disabling its starting on boot)"
echo "activate : enables webshield starting on boot and starts it right away"
echo "deactivate : stops webshield right away and disables its starting on boot"
echo "terminate : stops webshield and wafd right away and disables its starting on boot"
echo "enable-splashscreen : enables splashscreen functionality for webshield"
echo "disable-splashscreen : disables splashscreen functionality for webshield"
echo "enable-cpanelprotection : enables cpanelprotection functionality for webshield"
echo "disable-cpanelprotection : disables cpanelprotection functionality for webshield"
echo "set-standalone : forcibly use standalone mode"
echo "unset-standalone : switch from forcibly set standalone mode to the previous one (if exists)"
echo "mode : shows the current module"
echo "mode module : if possible switch from standalone to module-based mode"
echo "mode proxy : if possible switch from module-based to standalone mode"
echo "mode supported : prints if switching to module-based mode is supported for current system"
echo "reload : reload settings without restart"
}
load_mode
case "$1" in
enable)
do_enable
;;
disable)
do_disable
;;
is-enabled)
is_enabled
;;
is-active)
is_active
;;
start)
do_start
;;
stop)
do_stop
;;
activate)
do_activate
;;
deactivate)
do_deactivate
;;
terminate)
do_terminate
;;
enable-splashscreen)
do_enable_splashscreen
;;
disable-splashscreen)
do_disable_splashscreen
;;
enable-cpanelprotection)
do_enable_cpanelprotection
;;
disable-cpanelprotection)
do_disable_cpanelprotection
;;
reload)
do_reload
;;
mode)
if [ "$2" = module ]; then
unset_standalone
elif [ "$2" = proxy ]; then
set_standalone
elif [ "$2" = supported ]; then
is_supported 'out'
elif [ -z "$2" ]; then
if [ "$MODE" = standalone ]; then
echo proxy
else
echo "$MODE"
fi
else
echo "Unknown mode: $2. Exit"
exit
fi
;;
mode-proxy)
set_standalone
;;
mode-module)
unset_standalone
;;
mode-supported)
is_supported 'out'
;;
set-standalone)
set_standalone
;;
unset-standalone)
unset_standalone
;;
detect-mode)
detect_mode
;;
is-apache)
is_apache
;;
is-nginx)
is_nginx
;;
is-cloudways)
is_cloudways
;;
is-standalone)
is_standalone
;;
configure)
configure
;;
help)
print_help
;;
*)
echo "Usage: $0 {enable|disable|start|stop|activate|deactivate|is-enabled|is-active|enable-splashscreen|disable-splashscreen|enable-cpanelprotection|disable-cpanelprotection|reload|set_standalone|unset_standalone|mode|mode proxy|mode module|mode supported|detect-mode|is-apache|is-nginx|is-cloudways|is-standalone|help}"
exit 2
esac
Zerion Mini Shell 1.0