#!/usr/local/cpanel/3rdparty/bin/perl
#WHMADDON:addonupdates:ConfigServer ModSec Control
#ACLS:configserver
###############################################################################
# Copyright (C) 2006-2025 Jonathan Michaelson
#
# https://github.com/waytotheweb/scripts
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, see .
###############################################################################
## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
use strict;
use CGI::Carp qw(fatalsToBrowser);
use File::Basename;
use File::Path;
use File::Copy;
use File::Find;
use Fcntl qw(:DEFAULT :flock);
use IPC::Open3;
use lib '/usr/local/cpanel';
require Cpanel::Form;
require Cpanel::Config;
require Cpanel::Version::Tiny;
require Whostmgr::ACLS;
require Cpanel::Rlimit;
require Cpanel::Template;
###############################################################################
# start main
our ($images, $myv, $script, $versionfile, %FORM, $downloadserver);
%FORM = Cpanel::Form::parseform();
Whostmgr::ACLS::init_acls();
if (!Whostmgr::ACLS::hasroot()) {
print "Content-type: text/html\r\n\r\n";
print "You do not have access to ConfigServer ModSecurity Control.\n";
exit();
}
Cpanel::Rlimit::set_rlimit_to_infinity();
$script = "cmc.cgi";
$images = "cmc";
$versionfile = "/usr/local/cpanel/whostmgr/docroot/cgi/configserver/cmc/cmcversion.txt";
local $| = 1;
$downloadserver = &getdownloadserver;
my $thisapp = "cmc";
my $reregister;
my $modalstyle;
if ($Cpanel::Version::Tiny::major_version >= 65) {
if (-e "/usr/local/cpanel/whostmgr/docroot/cgi/configserver/${thisapp}/${thisapp}.conf") {
sysopen (my $CONF, "/usr/local/cpanel/whostmgr/docroot/cgi/configserver/${thisapp}/${thisapp}.conf", O_RDWR | O_CREAT);
flock ($CONF, LOCK_EX);
my @confdata = <$CONF>;
chomp @confdata;
for (0..scalar(@confdata)) {
if ($confdata[$_] =~ /^target=mainFrame/) {
$confdata[$_] = "target=_self";
$reregister = 1;
}
}
if ($reregister) {
seek ($CONF, 0, 0);
truncate ($CONF, 0);
foreach (@confdata) {
print $CONF "$_\n";
}
&printcmd("/usr/local/cpanel/bin/register_appconfig","/usr/local/cpanel/whostmgr/docroot/cgi/configserver/${thisapp}/${thisapp}.conf");
$reregister = "
Updated application. The next time you login to WHM this will open within the native WHM main window instead of launching a separate window
\n";
}
close ($CONF);
}
}
print "Content-type: text/html\r\n\r\n";
#if ($Cpanel::Version::Tiny::major_version < 65) {$modalstyle = "style='top:120px'"}
our (@files);
open (my $IN, "<", $versionfile) or die $!;
flock ($IN, LOCK_SH);
$myv = <$IN>;
close ($IN);
chomp $myv;
my $bootstrapcss = "";
my $jqueryjs = "";
my $bootstrapjs = "";
my $templatehtml;
my $SCRIPTOUT;
unless ($FORM{action} eq "help") {
open ($SCRIPTOUT, '>', \$templatehtml);
select $SCRIPTOUT;
print <
$jqueryjs
$bootstrapjs
EOF
} else {
print <
$bootstrapcss
$jqueryjs
$bootstrapjs
EOF
}
print <
ConfigServer ModSecurity Control - cmc v$myv
EOF
if ($reregister ne "") {print $reregister}
print "
This script creates and rewrites modsec2.whitelist.conf and userdata modsec.conf files
\n";
print "
Do not use cmc if you have made manual modifications to these files as they will be removed by cmc
\n";
my $is_ea4 = 0;
my $apachepath = "/usr/local/apache/conf";
my $modsecpath = "/usr/local/apache/conf";
my $apachebin = "/usr/local/apache/bin/httpd";
my $apachectl = "/usr/local/apache/bin/apachectl";
my $apachelogs = "/usr/local/apache/logs";
if (-e "/usr/local/cpanel/version" and -e "/etc/cpanel/ea4/is_ea4" and -e "/etc/cpanel/ea4/paths.conf") {
$is_ea4 = 1;
$apachepath = "/etc/apache2/conf.d";
$apachebin = "/usr/sbin/httpd";
$apachectl = "/usr/sbin/apachectl";
$apachelogs = "/etc/apache2/logs";
open (my $IN, "<", "/etc/cpanel/ea4/paths.conf");
flock ($IN, LOCK_SH);
my @file = <$IN>;
close ($IN);
chomp @file;
foreach my $line (@file) {
if ($line =~ /^(\s|\#|$)/) {next}
if ($line !~ /=/) {next}
my ($name,$value) = split (/=/,$line,2);
$value =~ s/^\s+//g;
$value =~ s/\s+$//g;
if ($name eq "dir_conf") {$apachepath = $value}
if ($name eq "bin_httpd") {$apachebin = $value}
if ($name eq "bin_apachectl") {$apachectl = $value}
if ($name eq "dir_logs") {$apachelogs = $value}
}
$modsecpath = $apachepath."/modsec";
}
my $httpv = "2";
my $mypid;
my ($childin, $childout);
$mypid = open3($childin, $childout, $childout, "$apachebin","-v");
my @version = <$childout>;
waitpid ($mypid, 0);
chomp @version;
$version[0] =~ /Apache\/(\d+)\.(\d+)\.(\d+)/;
my $mas = $1;
my $maj = $2;
my $min = $3;
$httpv = "$mas.$maj";
my $stdpath = "$apachepath/userdata/std/2";
my $sslpath = "$apachepath/userdata/ssl/2";
my $oldstdpath;
my $oldsslpath;
if ($httpv eq "2.2") {
$oldstdpath = $stdpath;
$oldsslpath = $sslpath;
$stdpath = "$apachepath/userdata/std/2_2";
$sslpath = "$apachepath/userdata/ssl/2_2";
}
if ($httpv eq "2.4") {
$oldstdpath = $stdpath;
$oldsslpath = $sslpath;
$stdpath = "$apachepath/userdata/std/2_4";
$sslpath = "$apachepath/userdata/ssl/2_4";
}
my $truefile;
if ($FORM{template} ne "") {
my ($tfile, $tdir) = fileparse("$apachepath/$FORM{template}");
$truefile = "$tdir$tfile";
}
if (($FORM{template} ne "") and ($truefile !~ m[^$apachepath/])) {
print "[$FORM{template}] is not a valid file";
}
elsif (($FORM{domain} ne "") and ($FORM{domain} !~ /^[a-zA-Z0-9\-\_\.]+$/)) {
print "[$FORM{domain}] is not a valid domain";
}
elsif (($FORM{user} ne "") and ($FORM{user} !~ /^[a-zA-Z0-9\-\_\.\@\%\+]+$/)) {
print "[$FORM{user}] is not a valid user";
}
elsif ($FORM{action} eq "upgrade") {
print "Retrieving new cmc package...\n";
print "
Disable ModSecurity rules that have unique ID numbers on a global, per cPanel user or per hosted domain level.
Disable ModSecurity entirely, also on a global, per cPanel user or per hosted domain level.
Edit files containing ModSecurity configuration settings in $apachepath
View the latest ModSecurity log entries
The requirements for this utility are:
Apache v2+
ModSecurity v2.5+ installed via Easyapache
A set of ModSecurity rules each of which uses a unique ID
ModSecurity logging that uses "SecAuditLogParts A...Z"
ModSecurity logs will be detected in the following order, the last found being the one that will be used. If the wrong logs are being shown the other logs should be removed:
$apachelogs/audit_log
$apachelogs/modsec_audit.log
$apachelogs/modsec_audit/ (used under mod_ruid2 and mpm_itk)
This utility uses concepts explained in this section of the cPanel documentation.
EOH
print "
\n";
print "
\n";
}
else {
my @modsecfiles;
my @modsecdirfiles;
my %ids;
my @alt;
my $off = 0;
if (-e "$apachepath/modsec2.whitelist.conf") {
open (my $FH, "<", "$apachepath/modsec2.whitelist.conf");
flock ($FH, LOCK_SH);
my @data = <$FH>;
close ($FH);
chomp @data;
my $start = 0;
foreach my $line (@data) {
if ($line =~ //) {push @alt,$1; $start = 1}
if (!$start and $line =~ /SecRuleRemoveById\s+(\d*)/) {$ids{$1} = 1}
if (!$start and $line =~ /SecRuleEngine\s+Off/) {$off = 1}
if ($line =~ /^\s*(<\/DirectoryMatch>)/) {$start = 0}
}
} else {
open (my $FH,">","$apachepath/modsec2.whitelist.conf");
flock ($FH, LOCK_SH);
print $FH "\# ConfigServer ModSecurity whitelist file\n";
close ($FH);
}
sysopen (my $FH, "$modsecpath/modsec2.user.conf", O_RDWR | O_CREAT);
flock ($FH, LOCK_EX);
my @data = <$FH>;
chomp @data;
if ($is_ea4) {
if (grep {$_ =~ /^\s*Include\s+$apachepath\/modsec2\.whitelist\.conf/} @data) {
seek ($FH, 0, 0);
truncate ($FH, 0);
foreach my $line (@data) {
if ($line =~ /^\s*Include\s+$apachepath\/modsec2\.whitelist\.conf/) {next}
if ($line =~ /^\# ConfigServer ModSecurity whitelist file/) {next}
print $FH "$line\n";
}
print "
Removing modsec2.whitelist.conf in modsec2.user.conf (not needed in EA4) and gracefully restarting Apache...";
&printcmd("$apachectl","graceful");
print "Done