File: //usr/src/csf/ConfigServer/DisplayUI.pm
# #
# @app ConfigServer Security & Firewall (CSF)
# Login Failure Daemon (LFD)
# @website https://configserver.dev
# @docs https://docs.configserver.dev
# @download https://download.configserver.dev
# @repo https://github.com/Aetherinox/csf-firewall
# @copyright Copyright (C) 2025-2026 Aetherinox
# Copyright (C) 2006-2025 Jonathan Michaelson
# Copyright (C) 2006-2025 Way to the Web Ltd.
# @license GPLv3
# @updated 02.22.2026
#
# 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 <https://www.gnu.org/licenses>.
# #
## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen)
package ConfigServer::DisplayUI;
use strict;
use lib '/usr/local/csf/lib';
use Fcntl qw(:DEFAULT :flock);
use File::Basename;
use File::Copy;
use Net::CIDR::Lite;
use IPC::Open3;
use ConfigServer::Config;
use ConfigServer::CheckIP qw(checkip);
use ConfigServer::Ports;
use ConfigServer::JSON qw(encode_json decode_json);
use ConfigServer::URLGet;
use ConfigServer::Sanity qw(sanity);;
use ConfigServer::ServerCheck;
use ConfigServer::ServerStats;
use ConfigServer::Service;
use ConfigServer::RBLCheck;
use ConfigServer::GetEthDev;
use ConfigServer::Slurp qw(slurp);
use ConfigServer::Sanitize qw(html_safe html_escape strip_ansi);
use Exporter qw(import);
our $VERSION = 1.01;
our @ISA = qw(Exporter);
our @EXPORT_OK = qw();
umask(0177);
our ($chart, $ipscidr6, $ipv6reg, $ipv4reg, %config, %ips, $mobile,
$urlget, %FORM, $script, $script_da, $images, $myv);
my $slurpreg = ConfigServer::Slurp->slurpreg;
my $cleanreg = ConfigServer::Slurp->cleanreg;
# #
# Alias › Output
# #
sub __
{
if ( ref $_[0] eq 'GLOB' )
{
my $fh = shift;
print $fh @_;
}
else
{
print @_;
}
}
# #
# Main
# #
sub main
{
my $form_ref = shift;
%FORM = %{$form_ref};
$script = shift;
$script_da = shift;
$images = shift;
$myv = shift;
$config{THIS_UI} = shift;
$| = 1;
# #
# Date
# #
my @t = localtime;
my $year = $t[5] + 1900;
# #
# Load config file
# #
my $thisui = $config{THIS_UI};
my $config = ConfigServer::Config->loadconfig();
%config = $config->config;
$config{THIS_UI} = $thisui;
# #
# Get codename
# #
my $codename = ConfigServer::Config->getCodename();
# #
# Get IPs
# #
$ipscidr6 = Net::CIDR::Lite->new;
$ipv4reg = $config->ipv4reg;
$ipv6reg = $config->ipv6reg;
# #
# Cloudflare Services
# #
if ( $config{CF_ENABLE} )
{
require ConfigServer::CloudFlare;
import ConfigServer::CloudFlare;
}
$mobile = 0;
if ( $FORM{mobi} )
{
$mobile = 1
}
$chart = 1;
if ( $config{ST_ENABLE} )
{
if ( !defined ConfigServer::ServerStats::init( ) )
{
$chart = 0
}
}
$urlget = ConfigServer::URLGet->new( $config{URLGET}, "csf/$myv", $config{URLPROXY} );
unless ( defined $urlget )
{
$config{URLGET} = 1;
$urlget = ConfigServer::URLGet->new( $config{URLGET}, "csf/$myv", $config{URLPROXY} );
print "<p>*WARNING* URLGET set to use LWP but perl module is not installed, reverting to HTTP::Tiny<p>\n";
}
if ( $config{RESTRICT_UI} == 2 )
{
print "<table class='table table-bordered table-striped'>\n";
print "<tr><td><font color='red'>csf UI Disabled via the RESTRICT_UI option in /etc/csf/csf.conf</font></td></tr>\n";
print "</tr></table>\n";
exit;
}
if ( $FORM{ip} ne "" )
{
$FORM{ip} =~ s/(^\s+)|(\s+$)//g
}
if ( ( $FORM{ip} ne "" ) and ( $FORM{ip} ne "all" ) and ( !checkip(\$FORM{ip} ) ) )
{
print "[$FORM{ip}] is not a valid IP/CIDR";
}
elsif ( ( $FORM{ignorefile} ne "" ) and ( $FORM{ignorefile} =~ /[^\w\.]/ ) )
{
print "[$FORM{ignorefile}] is not a valid file";
}
elsif ( ( $FORM{template} ne "" ) and ( $FORM{template} =~ /[^\w\.]/ ) )
{
print "[$FORM{template}] is not a valid file";
}
elsif ( $FORM{action} eq "manualcheck" )
{
print "<div>\n";
my ( $upgrade, $response ) = &manualversion($myv);
print "<div>Gathering results</div>\n\n";
if ($upgrade)
{
print "<span style='color:#429654;'>New version of CSF (v$response) available. Upgrading will retain your settings.</span>\n";
print "You can view the list of changes <a href='https://$config{DOWNLOADSERVER}/csf/changelog.txt' target='_blank'>here</a>";
}
else
{
if ( $response ne "" )
{
print "An issue occurred; returned response $response\n";
}
else
{
print "You are running the latest version of CSF (v$myv). Nothing to do.\n";
}
}
print "</pre>\n</div>\n\n";
if ($upgrade)
{
print "<form action='$script' method='post'>\n";
print "<button name='action' value='upgrade' type='submit' class='btn btn-success'><i class='axs ax-box-open'></i> Update $myv › v$response</button>\n";
print "</form>\n";
}
else
{
if ( $response ne "" )
{
print "<div class='bs-callout bs-callout-danger'>$response</div>\n";
}
else
{
print "<div class='bs-callout bs-callout-info'>You are running the latest version of csf (v$myv). An Upgrade button will appear here if a new version becomes available</div>\n";
}
}
print "</div>\n";
&renderBtnReturn;
}
elsif ( $FORM{action} eq "lfdstatus" )
{
print "<div><p>Show lfd status...</p>\n<pre class='comment' style='white-space: pre-wrap;'>\n";
ConfigServer::Service::statuslfd();
print "</pre>\n<p>...<b>Done</b>.</div>\n";
&renderBtnReturn;
}
elsif ( $FORM{action} eq "ms_list" )
{
&modsec;
}
elsif ( $FORM{action} eq "chart" )
{
&chart;
}
elsif ( $FORM{action} eq "systemstats" )
{
&systemstats($FORM{graph});
}
elsif ( $FORM{action} eq "lfdstart" )
{
print "<div><p>Starting lfd...</p>\n<pre class='comment' style='white-space: pre-wrap;'>\n";
ConfigServer::Service::startlfd();
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
&renderBtnReturn;
}
elsif ( $FORM{action} eq "lfdrestart" )
{
if ( $config{THIS_UI} )
{
print "<div><p>Signal lfd to <i>restart</i>...</p>\n<pre class='comment' style='white-space: pre-wrap;'>\n";
sysopen (my $OUT, "/var/lib/csf/lfd.restart",, O_WRONLY | O_CREAT) or die "Unable to open file: $!";
close ($OUT);
}
else
{
print "<div><p>Restarting lfd...</p>\n<pre class='comment' style='white-space: pre-wrap;'>\n";
ConfigServer::Service::restartlfd();
}
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
&renderBtnReturn;
}
elsif ( $FORM{action} eq "lfdstop" )
{
print "<div><p>Stopping lfd...</p>\n<pre class='comment' style='white-space: pre-wrap;'>\n";
ConfigServer::Service::stoplfd();
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
&renderBtnReturn;
}
elsif ( $FORM{action} eq "status" )
{
&resize("top");
print "<pre class='comment' style='white-space: pre-wrap; height: 500px; overflow: auto; resize:both; clear:both' id='output'>\n";
&printcmd("/usr/sbin/csf","-l");
if ($config{IPV6})
{
print "\n\nip6tables:\n\n";
&printcmd("/usr/sbin/csf","-l6");
}
print "</pre>\n";
&resize("bot",1);
&renderBtnReturn;
}
elsif ( $FORM{action} eq "start" )
{
print "<div><p>Starting csf...</p>\n";
&resize("top");
print "<pre class='comment' style='white-space: pre-wrap; height: 500px; overflow: auto; resize:both; clear:both' id='output'>\n";
&printcmd("/usr/sbin/csf","-sf");
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
&resize("bot",1);
&renderBtnReturn;
}
elsif ($FORM{action} eq "restart")
{
print "<div><p>Restarting csf...</p>\n";
&resize("top");
print "<pre class='comment' style='white-space: pre-wrap; height: 500px; overflow: auto; resize:both; clear:both' id='output'>\n";
&printcmd("/usr/sbin/csf","-sf");
print "</pre>\n<p>...<b>Done</b>.</div>\n";
&resize("bot",1);
&renderBtnReturn;
}
elsif ($FORM{action} eq "restartq")
{
print "<div><p>Restarting csf via lfd...</p>\n<pre class='comment' style='white-space: pre-wrap;'>\n";
&printcmd("/usr/sbin/csf","-q");
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
&renderBtnReturn;
}
elsif ($FORM{action} eq "temp")
{
print "<table class='table table-bordered table-striped'>\n";
print "<thead><tr><th> </th><th>A/D</th><th>IP address</th><th>Port</th><th>Dir</th><th>Time To Live</th><th>Comment</th></tr></thead>\n";
my @deny;
if (! -z "/var/lib/csf/csf.tempban")
{
open (my $IN, "<", "/var/lib/csf/csf.tempban") or die $!;
flock ($IN, LOCK_SH);
@deny = <$IN>;
chomp @deny;
close ($IN);
}
foreach my $line ( reverse @deny )
{
if ($line eq "")
{
next
}
my ($time,$ip,$port,$inout,$timeout,$message) = split(/\|/,$line);
$time = $timeout - (time - $time);
if ($port eq "")
{
$port = "*"
}
if ($inout eq "")
{
$inout = " *"
}
if ($time < 1)
{
$time = "<1";
}
else
{
my $days = int($time/(24*60*60) );
my $hours = ($time/(60*60) )%24;
my $mins = ($time/60)%60;
my $secs = $time%60;
$days = $days < 1 ? '' : $days .'d ';
$hours = $hours < 1 ? '' : $hours .'h ';
$mins = $mins < 1 ? '' : $mins . 'm ';
$time = $days . $hours . $mins . $secs . 's';
}
print "<tr><td style='white-space: nowrap;'><a class='btn btn-success' href='$script?action=temprmd&ip=$ip' data-tooltip='tooltip' title='Unblock $ip'><span class='glyphicon glyphicon-ok-circle'></span></a> \n";
print "<a class='btn btn-danger' href='$script?action=temptoperm&ip=$ip' data-tooltip='tooltip' title='Permanently block $ip'><span class='glyphicon glyphicon-ban-circle'></span></a></td>\n";
print "<td>DENY</td><td>$ip</td><td>$port</td><td>$inout</td><td>$time</td><td>$message</td></tr>\n";
}
my @allow;
if (! -z "/var/lib/csf/csf.tempallow")
{
open (my $IN, "<", "/var/lib/csf/csf.tempallow") or die $!;
flock ($IN, LOCK_SH);
@allow = <$IN>;
chomp @allow;
close ($IN);
}
foreach my $line (@allow)
{
if ($line eq "")
{
next
}
my ($time,$ip,$port,$inout,$timeout,$message) = split(/\|/,$line);
$time = $timeout - (time - $time);
if ($port eq "")
{
$port = "*"
}
if ($inout eq "")
{
$inout = " *"
}
if ($time < 1)
{
$time = "<1";
}
else
{
my $days = int($time/(24*60*60) );
my $hours = ($time/(60*60) )%24;
my $mins = ($time/60)%60;
my $secs = $time%60;
$days = $days < 1 ? '' : $days .'d ';
$hours = $hours < 1 ? '' : $hours .'h ';
$mins = $mins < 1 ? '' : $mins . 'm ';
$time = $days . $hours . $mins . $secs . 's';
}
print "<tr><td style='white-space: nowrap;'><a class='btn btn-success' href='$script?action=temprma&ip=$ip' data-tooltip='tooltip' title='Remove $ip'><span class='glyphicon glyphicon-ok-circle'></span></a> \n";
print "<td>ALLOW</td><td>$ip</td><td>$port</td><td>$inout</td><td>$time</td><td>$message</td></tr>\n";
}
print "</table>\n";
if ( @deny or @allow )
{
print "<div><a class='btn btn-success' href='$script?action=temprm&ip=all'>Flush all temporary blocks</a></div>\n";
}
else
{
print "<div>There are no temporary IP entries</div>\n";
}
&renderBtnReturn;
}
elsif ( $FORM{action} eq "temprm" )
{
print "<div><p>Removing all temporary entries:</p>\n<pre class='comment' style='white-space: pre-wrap;'>\n";
if ( $FORM{ip} eq "all" )
{
&printcmd("/usr/sbin/csf","-tf");
}
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
print "<div><form action='$script' method='post'><input type='hidden' name='action' value='temp'><input type='submit' class='btn btn-default' value='Return'></form></div>\n";
}
elsif ( $FORM{action} eq "temprmd" )
{
print "<div><p>Removing temporary deny entry for $FORM{ip}:</p>\n<pre class='comment' style='white-space: pre-wrap;'>\n";
&printcmd("/usr/sbin/csf","-trd",$FORM{ip});
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
print "<div><form action='$script' method='post'><input type='hidden' name='action' value='temp'><input type='submit' class='btn btn-default' value='Return'></form></div>\n";
}
elsif ( $FORM{action} eq "temprma" )
{
print "<div><p>Removing temporary allow entry for $FORM{ip}:</p>\n<pre class='comment' style='white-space: pre-wrap;'>\n";
&printcmd("/usr/sbin/csf","-tra",$FORM{ip});
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
print "<div><form action='$script' method='post'><input type='hidden' name='action' value='temp'><input type='submit' class='btn btn-default' value='Return'></form></div>\n";
}
elsif ( $FORM{action} eq "temptoperm" )
{
print "<div><p>Permanent ban for $FORM{ip}:</p>\n<pre class='comment' style='white-space: pre-wrap;'>\n";
&printcmd("/usr/sbin/csf","-tr",$FORM{ip});
&printcmd("/usr/sbin/csf","-d",$FORM{ip});
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
print "<div><form action='$script' method='post'><input type='hidden' name='action' value='temp'><input type='submit' class='btn btn-default' value='Return'></form></div>\n";
}
elsif ( $FORM{action} eq "tempdeny" )
{
$FORM{timeout} =~ s/\D//g;
if ($FORM{dur} eq "minutes")
{
$FORM{timeout} = $FORM{timeout} * 60
}
if ($FORM{dur} eq "hours")
{
$FORM{timeout} = $FORM{timeout} * 60 * 60
}
if ($FORM{dur} eq "days")
{
$FORM{timeout} = $FORM{timeout} * 60 * 60 * 24
}
if ($FORM{ports} eq "")
{
$FORM{ports} = "*"
}
print "<div><p>Temporarily $FORM{do}ing $FORM{ip} for $FORM{timeout} seconds:</p>\n<pre class='comment' style='white-space: pre-wrap;'>\n";
if ($FORM{do} eq "block")
{
&printcmd("/usr/sbin/csf","-td",$FORM{ip},$FORM{timeout},"-p",$FORM{ports},$FORM{comment});
}
else
{
&printcmd("/usr/sbin/csf","-ta",$FORM{ip},$FORM{timeout},"-p",$FORM{ports},$FORM{comment});
}
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
&renderBtnReturn;
}
elsif ( $FORM{action} eq "stop" )
{
print "<div><p>Stopping csf...</p>\n";
&resize("top");
print "<pre class='comment' style='white-space: pre-wrap; height: 500px; overflow: auto; resize:both; clear:both' id='output'>\n";
&printcmd("/usr/sbin/csf","-f");
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
&resize("bot",1);
&renderBtnReturn;
}
elsif ( $FORM{action} eq "disable" )
{
print "<div><p>Disabling csf...</p>\n";
&resize("top");
print "<pre class='comment' style='white-space: pre-wrap; height: 500px; overflow: auto; resize:both; clear:both' id='output'>\n";
&printcmd("/usr/sbin/csf","-x");
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
&resize("bot",1);
&renderBtnReturn;
}
elsif ( $FORM{action} eq "enable" )
{
if ( $config{THIS_UI} )
{
print "<div><p>You must login to the root shell to enable csf using:\n<p><b>csf -e</b></p>\n";
}
else
{
print "<div><p>Enabling csf...</p>\n";
&resize("top");
print "<pre class='comment' style='white-space: pre-wrap; height: 500px; overflow: auto; resize:both; clear:both' id='output'>\n";
&printcmd("/usr/sbin/csf","-e");
print "</pre>";
&resize("bot",1);
}
print "<p>...<b>Done</b>.</p></div>\n";
&renderBtnReturn;
}
elsif ($FORM{action} eq "logtail")
{
$FORM{lines} =~ s/\D//g;
if ($FORM{lines} eq "" or $FORM{lines} == 0)
{
$FORM{lines} = 30
}
my $script_safe = $script;
my $csfFromBot = 120;
my $csfFromRight = 10;
if ( $config{DIRECTADMIN} )
{
$script = $script_da;
$csfFromBot = 400;
$csfFromRight = 150;
}
my @data = slurp("/etc/csf/csf.syslogs");
foreach my $line ( @data )
{
if ( $line =~ /^Include\s*(.*)$/ )
{
my @incfile = slurp($1);
push @data,@incfile;
}
}
@data = sort @data;
my $options = "<select id='csfLogNum' onchange='csfTimerRefresh()'>\n";
my $cnt = 0;
foreach my $file ( @data )
{
$file =~ s/$cleanreg//g;
if ($file eq "")
{
next
}
if ($file =~ /^\s*\#|Include/)
{
next
}
my @globfiles;
if ($file =~ /\*|\?|\[/)
{
foreach my $log (glob $file)
{
push @globfiles, $log
}
}
else
{
push @globfiles, $file
}
foreach my $globfile (@globfiles)
{
if (-f $globfile)
{
my $size = int( (stat($globfile) )[7]/1024);
$options .= "<option value='$cnt'";
if ($globfile eq "/var/log/lfd.log") {$options .= " selected"}
$options .= ">$globfile ($size kb)</option>\n";
$cnt++;
}
}
}
$options .= "</select>\n";
my $timer_duration = $config{UI_LOGS_REFRESH_TIME} || 7;
my $timer_pause = $config{UI_LOGS_START_PAUSED} || 0;
print "<script>\n";
# Inject Perl values first
print "var csfDuration = $timer_duration;\n";
print "var csfStartPaused = $timer_pause;\n";
print "var csfFromBot = $csfFromBot;\n";
print "var csfFromRight = $csfFromRight;\n";
print "var csfScript = '$script?action=logtailcmd';\n";
open (my $AJAX, "<", "/usr/local/csf/lib/csfajaxtail.js");
flock ($AJAX, LOCK_SH);
my @jsdata = <$AJAX>;
close ($AJAX);
print @jsdata;
print "</script>\n";
print <<EOF;
<div>
$options Lines: <input type='text' id="csfLines" value="100" size='4'>
<button class='btn btn-default' onclick="csfTimerRefresh()">Refresh Now</button>
</div>
<div>
Next Refresh <span id="csfTimer">0</span>
<button class='btn btn-default' id="csfPauseId" onclick="csfTimerPause()" style="width:80px;">Pause</button> <img src="$images/loader.gif" id="csfRefreshing" style="display:none" />
</div>
<div class='pull-right btn-group'>
<button class='btn btn-default' id='fontminus-btn'><strong>a</strong><span class='glyphicon glyphicon-arrow-down'></span></button>
<button class='btn btn-default' id='fontplus-btn'><strong>A</strong><span class='glyphicon glyphicon-arrow-up'></span></button>
</div>
<pre class='comment' id="csfAjax" style="overflow:auto; height:500px !important; resize:both; white-space: pre-wrap;clear:both"> </pre>
<script>
csfFromBot = $csfFromBot;
csfFromRight = $csfFromRight;
csfScript = '$script?action=logtailcmd';
csfTimerInitialize();
var myFont = 14;
\$( "#fontplus-btn" ).on( 'click', function ( )
{
myFont++;
if ( myFont > 20 )
{
myFont = 20
}
\$( '#csfAjax' ).css( "font-size", myFont + "px" );
});
\$( "#fontminus-btn" ).on( 'click', function ( )
{
myFont--;
if ( myFont < 10 )
{
myFont = 10
}
\$( '#csfAjax' ).css( "font-size", myFont + "px" );
});
</script>
EOF
if ($config{DIRECTADMIN})
{
$script = $script_safe
}
&renderBtnReturn;
}
elsif ($FORM{action} eq "logtailcmd")
{
$FORM{lines} =~ s/\D//g;
if ($FORM{lines} eq "" or $FORM{lines} == 0)
{
$FORM{lines} = 30
}
my @data = slurp("/etc/csf/csf.syslogs");
foreach my $line ( @data )
{
if ($line =~ /^Include\s*(.*)$/)
{
my @incfile = slurp($1);
push @data,@incfile;
}
}
@data = sort @data;
my $cnt = 0;
my $logfile = "/var/log/lfd.log";
my $hit = 0;
foreach my $file ( @data )
{
$file =~ s/$cleanreg//g;
if ($file eq "")
{
next
}
if ($file =~ /^\s*\#|Include/)
{
next
}
my @globfiles;
if ($file =~ /\*|\?|\[/)
{
foreach my $log ( glob $file )
{
push @globfiles, $log
}
}
else
{
push @globfiles, $file
}
foreach my $globfile ( @globfiles )
{
if ( -f $globfile )
{
if ( $FORM{lognum} == $cnt )
{
$logfile = $globfile;
$hit = 1;
last;
}
$cnt++;
}
}
if ( $hit )
{
last
}
}
if ( -z $logfile )
{
print "<---- $logfile is currently empty ---->";
}
else
{
if ( -x $config{TAIL} )
{
my $timeout = 30;
eval
{
local $SIG{__DIE__} = undef;
local $SIG{'ALRM'} = sub
{
die
};
alarm($timeout);
my ($childin, $childout);
my $pid = open3($childin, $childout, $childout,$config{TAIL},"-$FORM{lines}",$logfile);
while (<$childout>)
{
my $line = html_escape( $_, '&<>' );
print $line;
}
waitpid ($pid, 0);
alarm(0);
};
alarm(0);
if ($@) {print "TIMEOUT: tail command took too long. Timed out after $timeout seconds\n"}
}
else
{
print "Executable [$config{TAIL}] invalid";
}
}
}
elsif ($FORM{action} eq "loggrep")
{
$FORM{lines} =~ s/\D//g;
if ($FORM{lines} eq "" or $FORM{lines} == 0)
{
$FORM{lines} = 30
}
my $script_safe = $script;
my $csfFromBot = 120;
my $csfFromRight = 10;
if ($config{DIRECTADMIN})
{
$script = $script_da;
$csfFromBot = 400;
$csfFromRight = 150;
}
my @data = slurp( "/etc/csf/csf.syslogs" );
foreach my $line ( @data )
{
if ($line =~ /^Include\s*(.*)$/)
{
my @incfile = slurp($1);
push @data,@incfile;
}
}
@data = sort @data;
my $options = "<select id='csfLogNum'>\n";
my $cnt = 0;
foreach my $file ( @data )
{
$file =~ s/$cleanreg//g;
if ($file eq "")
{
next
}
if ($file =~ /^\s*\#|Include/)
{
next
}
my @globfiles;
if ($file =~ /\*|\?|\[/)
{
foreach my $log (glob $file)
{
push @globfiles, $log
}
}
else
{
push @globfiles, $file
}
foreach my $globfile ( @globfiles )
{
if ( -f $globfile )
{
my $size = int( (stat($globfile) )[7]/1024);
$options .= "<option value='$cnt'";
if ($globfile eq "/var/log/lfd.log") {$options .= " selected"}
$options .= ">$globfile ($size kb)</option>\n";
$cnt++;
}
}
}
$options .= "</select>\n";
open (my $AJAX, "<", "/usr/local/csf/lib/csfajaxtail.js");
flock ($AJAX, LOCK_SH);
my @jsdata = <$AJAX>;
close ($AJAX);
print "<script>\n";
print @jsdata;
print "</script>\n";
print <<EOF;
<div>Log: $options</div>
<div style='white-space: nowrap;'>Text: <input type='text' size="30" id="csfGrep" onClick="this.select()">
<input type="checkbox" id="CSFgrep_i" value="1">-i
<input type="checkbox" id="CSFgrep_E" value="1">-E
<input type="checkbox" id="CSFgrep_Z" value="1"> wildcard
<button class='btn btn-default' onClick="csfGrep()">Search</button>
<img src="$images/loader.gif" id="csfRefreshing" style="display:none" /></div>
<div class='pull-right btn-group'><button class='btn btn-default' id='fontminus-btn'><strong>a</strong><span class='glyphicon glyphicon-arrow-down'></span></button>
<button class='btn btn-default' id='fontplus-btn'><strong>A</strong><span class='glyphicon glyphicon-arrow-up'></span></button></div>
<pre class='comment' id="csfAjax" style="overflow:auto;height:500px;resize:both; white-space: pre-wrap;clear: both">
Please Note:
1. Searches use $config{GREP}/$config{ZGREP} if wildcard is used), so the search text/regex must be syntactically correct
2. Use the "-i" option to ignore case
3. Use the "-E" option to perform an extended regular expression search
4. Searching large log files can take a long time. This feature has a 30 second timeout
5. The searched for text will usually be <mark>highlighted</mark> but may not always be successful
6. Only log files listed in /etc/csf/csf.syslogs can be searched. You can add to this file
7. The wildcard option will use $config{ZGREP} and search logs with a wildcard suffix, e.g. /var/log/lfd.log*
</pre>
<script>
csfFromBot = $csfFromBot;
csfFromRight = $csfFromRight;
csfScript = '$script?action=loggrepcmd';
var myFont = 14;
\$( "#fontplus-btn" ).on( 'click', function ( )
{
myFont++;
if ( myFont > 20 )
{
myFont = 20
}
\$( '#csfAjax' ).css( "font-size", myFont + "px" );
});
\$( "#fontminus-btn" ).on( 'click', function ( )
{
myFont--;
if ( myFont < 10 )
{
myFont = 10
}
\$( '#csfAjax' ).css( "font-size", myFont + "px" );
});
</script>
EOF
if ($config{DIRECTADMIN})
{
$script = $script_safe
}
&renderBtnReturn;
}
elsif ($FORM{action} eq "loggrepcmd")
{
my @data = slurp("/etc/csf/csf.syslogs");
foreach my $line ( @data )
{
if ($line =~ /^Include\s*(.*)$/)
{
my @incfile = slurp($1);
push @data,@incfile;
}
}
@data = sort @data;
my $cnt = 0;
my $logfile = "/var/log/lfd.log";
my $hit = 0;
foreach my $file ( @data )
{
$file =~ s/$cleanreg//g;
if ($file eq "")
{
next
}
if ($file =~ /^\s*\#|Include/)
{
next
}
my @globfiles;
if ($file =~ /\*|\?|\[/)
{
foreach my $log ( glob $file)
{
push @globfiles, $log
}
}
else
{
push @globfiles, $file
}
foreach my $globfile ( @globfiles )
{
if ( -f $globfile )
{
if ( $FORM{lognum} == $cnt )
{
$logfile = $globfile;
$hit = 1;
last;
}
$cnt++;
}
}
if ( $hit )
{
last
}
}
my @cmd;
my $grepbin = $config{GREP};
if ($FORM{grepZ})
{
$grepbin = $config{ZGREP}
}
if ($FORM{grepi})
{
push @cmd, "-i"
}
if ($FORM{grepE})
{
push @cmd, "-E"
}
push @cmd, $FORM{grep};
if (-z $logfile)
{
print "<---- $logfile is currently empty ---->";
}
else
{
if (-x $grepbin)
{
my $timeout = 30;
eval
{
local $SIG{__DIE__} = undef;
local $SIG{'ALRM'} = sub
{
die
};
my $total;
if ($FORM{grepZ})
{
foreach my $file (glob $logfile."\*")
{
print "\nSearching $file:\n";
alarm($timeout);
my ($childin, $childout);
my $pid = open3($childin, $childout, $childout,$grepbin,@cmd,$file);
while (<$childout>)
{
my $line = html_escape( $_, '&<>' );
if ($FORM{grep} ne "")
{
eval
{
local $SIG{__DIE__} = undef;
if ($FORM{grepi})
{
$line =~ s/$FORM{grep}/<mark>$&<\/mark>/ig;
}
else
{
$line =~ s/$FORM{grep}/<mark>$&<\/mark>/g;
}
};
}
print $line;
$total += length $line;
}
waitpid ($pid, 0);
unless ($total) {print "<---- No matches found for \"$FORM{grep}\" in $file ---->\n"}
alarm(0);
}
}
else
{
alarm($timeout);
my ($childin, $childout);
my $pid = open3($childin, $childout, $childout,$grepbin,@cmd,$logfile);
while (<$childout>)
{
my $line = html_escape( $_, '&<>' );
if ($FORM{grep} ne "")
{
eval
{
local $SIG{__DIE__} = undef;
if ($FORM{grepi})
{
$line =~ s/$FORM{grep}/<mark>$&<\/mark>/ig;
}
else
{
$line =~ s/$FORM{grep}/<mark>$&<\/mark>/g;
}
};
}
print $line;
$total += length $line;
}
waitpid ($pid, 0);
unless ($total)
{
print "<---- No matches found for \"$FORM{grep}\" in $logfile ---->\n"
}
alarm(0);
}
};
alarm(0);
if ($@) {print "TIMEOUT: grep command took too long. Timed out after $timeout seconds\n"}
}
else
{
print "Executable [$grepbin] invalid";
}
}
}
elsif ($FORM{action} eq "readme")
{
&resize("top");
print "<pre id='output' class='comment' style='white-space: pre-wrap;height: 500px; overflow: auto; resize:both; clear:both'>\n";
open (my $IN, "<", "/etc/csf/readme.txt") or die $!;
flock ($IN, LOCK_SH);
my @readme = <$IN>;
close ($IN);
chomp @readme;
foreach my $line (@readme)
{
print html_escape( $line, '<>' )."\n";
}
print "</pre>\n";
&resize("bot",0);
&renderBtnReturn;
}
elsif ($FORM{action} eq "servercheck")
{
print ConfigServer::ServerCheck::report($FORM{verbose});
open (my $IN, "<", "/etc/cron.d/csf-cron");
flock ($IN, LOCK_SH);
my @data = <$IN>;
close ($IN);
chomp @data;
my $optionselected = "never";
my $email;
if (my @ls = grep {$_ =~ /csf \-m/} @data )
{
if ($ls[0] =~ /\@(\w+)\s+root\s+\/usr\/sbin\/csf \-m (.*)/)
{
$optionselected = $1; $email = $2
}
}
print "<br><div><form action='$script' method='post'><input type='hidden' name='action' value='serverchecksave'>\n";
print "Generate and email this report <select name='freq'>\n";
foreach my $option ("never","hourly","daily","weekly","monthly")
{
if ($option eq $optionselected) {print "<option selected>$option</option>\n"} else {print "<option>$option</option>\n"}
}
print "</select> to the email address <input type='text' name='email' value='$email'> <input type='submit' class='btn btn-default' value='Schedule'></form></div>\n";
print "<br><div><form action='$script' method='post'><input type='hidden' name='action' value='servercheck'><input type='submit' class='btn btn-default' value='Run Again'></form></div>\n";
print "<br><div><form action='$script' method='post'><input type='hidden' name='action' value='servercheck'><input type='hidden' name='verbose' value='1'><input type='submit' class='btn btn-default' value='Run Again and Display All Checks'></form></div>\n";
&renderBtnReturn;
}
elsif ($FORM{action} eq "serverchecksave")
{
my $extra = "";
my $freq = "daily";
my $email;
if ($FORM{email} ne "")
{
$email = "root"
}
if ($FORM{email} =~ /^[a-zA-Z0-9\-\_\.\@\+]+$/)
{
$email = $FORM{email}
}
foreach my $option ("never","hourly","daily","weekly","monthly")
{
if ($FORM{freq} eq $option)
{
$freq = $option
}
}
unless ($email) {$freq = "never"; $extra = "(no valid email address supplied)";}
sysopen (my $CRON, "/etc/cron.d/csf-cron", O_RDWR | O_CREAT) or die "Unable to open file: $!";
flock ($CRON, LOCK_EX);
my @data = <$CRON>;
chomp @data;
seek ($CRON, 0, 0);
truncate ($CRON, 0);
my $done = 0;
foreach my $line ( @data )
{
if ($line =~ /csf \-m/)
{
if ($freq and ($freq ne "never") and !$done)
{
print $CRON "\@$freq root /usr/sbin/csf -m $email\n";
$done = 1;
}
}
else
{
print $CRON "$line\n";
}
}
if (!$done and ($freq ne "never") )
{
print $CRON "\@$freq root /usr/sbin/csf -m $email\n";
}
close ($CRON);
if ($freq and $freq ne "never")
{
print "<div>Report scheduled to be emailed to $email $freq</div>\n";
}
else
{
print "<div>Report schedule cancelled $extra</div>\n";
}
print "<div><form action='$script' method='post'><input type='hidden' name='action' value='servercheck'><input type='submit' class='btn btn-default' value='Return'></form></div>\n";
}
elsif ($FORM{action} eq "rblcheck")
{
my ($status, undef) = ConfigServer::RBLCheck::report($FORM{verbose},$images,1);
print "<div style='padding-top: 3em;'><b>These options can take a long time to run</b> (several minutes) depending on the number of IP addresses to check and the response speed of the DNS requests:</div>\n";
print "<br><div><form action='$script' method='post'><input type='hidden' name='action' value='rblcheck'><input type='hidden' name='verbose' value='1'><input type='submit' class='btn btn-default' value='Update All Checks (standard)'> Generates the normal report showing exceptions only</form></div>\n";
print "<br><div><form action='$script' method='post'><input type='hidden' name='action' value='rblcheck'><input type='hidden' name='verbose' value='2'><input type='submit' class='btn btn-default' value='Update All Checks (verbose)'> Generates the normal report but shows successes and failures</form></div>\n";
print "<br><div><form action='$script' method='post'><input type='hidden' name='action' value='rblcheckedit'><input type='submit' class='btn btn-default' value='Edit RBL Options'> Edit csf.rblconf to enable and disable IPs and RBLs</form></div>\n";
open (my $IN, "<", "/etc/cron.d/csf-cron");
flock ($IN, LOCK_SH);
my @data = <$IN>;
close ($IN);
chomp @data;
my $optionselected = "never";
my $email;
if (my @ls = grep {$_ =~ /csf \-\-rbl/} @data )
{
if ($ls[0] =~ /\@(\w+)\s+root\s+\/usr\/sbin\/csf \-\-rbl (.*)/) {$optionselected = $1; $email = $2}
}
print "<br><div><form action='$script' method='post'><input type='hidden' name='action' value='rblchecksave'>\n";
print "Generate and email this report <select name='freq'>\n";
foreach my $option ("never","hourly","daily","weekly","monthly")
{
if ($option eq $optionselected) {print "<option selected>$option</option>\n"} else {print "<option>$option</option>\n"}
}
print "</select> to the email address <input type='text' name='email' value='$email'> <input type='submit' class='btn btn-default' value='Schedule'></form></div>\n";
&renderBtnReturn;
}
elsif ($FORM{action} eq "rblchecksave")
{
my $extra = "";
my $freq = "daily";
my $email;
if ($FORM{email} ne "")
{
$email = "root"
}
if ($FORM{email} =~ /^[a-zA-Z0-9\-\_\.\@\+]+$/)
{
$email = $FORM{email}
}
foreach my $option ("never","hourly","daily","weekly","monthly")
{
if ($FORM{freq} eq $option) {$freq = $option}
}
unless ($email)
{
$freq = "never"; $extra = "(no valid email address supplied)";
}
sysopen (my $CRON, "/etc/cron.d/csf-cron", O_RDWR | O_CREAT) or die "Unable to open file: $!";
flock ($CRON, LOCK_EX);
my @data = <$CRON>;
chomp @data;
seek ($CRON, 0, 0);
truncate ($CRON, 0);
my $done = 0;
foreach my $line ( @data )
{
if ($line =~ /csf \-\-rbl/)
{
if ($freq and ($freq ne "never") and !$done)
{
print $CRON "\@$freq root /usr/sbin/csf --rbl $email\n";
$done = 1;
}
}
else
{
print $CRON "$line\n";
}
}
if (!$done and ($freq ne "never") )
{
print $CRON "\@$freq root /usr/sbin/csf --rbl $email\n";
}
close ($CRON);
if ($freq and $freq ne "never")
{
print "<div>Report scheduled to be emailed to $email $freq</div>\n";
}
else
{
print "<div>Report schedule cancelled $extra</div>\n";
}
print "<div><form action='$script' method='post'><input type='hidden' name='action' value='rblcheck'><input type='submit' class='btn btn-default' value='Return'></form></div>\n";
}
elsif ($FORM{action} eq "rblcheckedit")
{
&editfile("/etc/csf/csf.rblconf","saverblcheckedit");
print "<div><form action='$script' method='post'><input type='hidden' name='action' value='rblcheck'><input type='submit' class='btn btn-default' value='Return'></form></div>\n";
}
elsif ($FORM{action} eq "saverblcheckedit")
{
&savefile("/etc/csf/csf.rblconf","");
print "<div><form action='$script' method='post'><input type='hidden' name='action' value='rblcheck'><input type='submit' class='btn btn-default' value='Return'></form></div>\n";
}
elsif ($FORM{action} eq "cloudflareedit")
{
&editfile("/etc/csf/csf.cloudflare","savecloudflareedit");
&renderBtnReturn;
}
elsif ($FORM{action} eq "savecloudflareedit")
{
&savefile("/etc/csf/csf.cloudflare","");
&renderBtnReturn;
}
elsif ($FORM{action} eq "restartboth")
{
print "<div><p>Restarting csf...</p>\n";
&resize("top");
print "<pre class='comment' style='white-space: pre-wrap; height: 500px; overflow: auto; resize:both; clear:both' id='output'>\n";
&printcmd("/usr/sbin/csf","-sf");
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
if ($config{THIS_UI})
{
print "<div><p>Signal lfd to <i>restart</i>...</p>\n<pre class='comment' style='white-space: pre-wrap;'>\n";
sysopen (my $OUT, "/var/lib/csf/lfd.restart",, O_WRONLY | O_CREAT) or die "Unable to open file: $!";
close ($OUT);
}
else
{
print "<div><p>Restarting lfd...</p>\n<pre class='comment' style='white-space: pre-wrap;'>\n";
ConfigServer::Service::restartlfd();
}
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
&resize("bot",1);
&renderBtnReturn;
}
elsif ($FORM{action} eq "remapf")
{
print "<div><p>Removing APF/BFD...</p>\n<pre class='comment' style='white-space: pre-wrap;'>\n";
&printcmd("sh","/usr/local/csf/bin/remove_apf_bfd.sh");
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
print "<div><p><b>Note: You should check the root cron and /etc/crontab to ensure that there are no apf or bfd related cron jobs remaining</b></p></div>\n";
&renderBtnReturn;
}
elsif ($FORM{action} eq "qallow")
{
print "<div><p>Allowing $FORM{ip}...</p>\n<pre class='comment' style='white-space: pre-wrap;'>\n";
&printcmd("/usr/sbin/csf","-a",$FORM{ip},$FORM{comment});
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
&renderBtnReturn;
}
elsif ($FORM{action} eq "qdeny")
{
print "<div><p>Blocking $FORM{ip}...</p>\n<pre class='comment' style='white-space: pre-wrap;'>\n";
&printcmd("/usr/sbin/csf","-d",$FORM{ip},$FORM{comment});
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
&renderBtnReturn;
}
elsif ($FORM{action} eq "qignore")
{
print "<div><p>Ignoring $FORM{ip}...\n";
open (my $OUT, ">>", "/etc/csf/csf.ignore");
flock ($OUT, LOCK_EX);
print $OUT "$FORM{ip}\n";
close ($OUT);
print "<b>Done</b>.</p></div>\n";
if ($config{THIS_UI})
{
print "<div><p>Signal lfd to <i>restart</i>...</p>\n<pre class='comment' style='white-space: pre-wrap;'>\n";
sysopen (my $OUT, "/var/lib/csf/lfd.restart",, O_WRONLY | O_CREAT) or die "Unable to open file: $!";
close ($OUT);
}
else
{
print "<div><p>Restarting lfd...</p>\n<pre class='comment' style='white-space: pre-wrap;'>\n";
ConfigServer::Service::restartlfd();
}
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
&renderBtnReturn;
}
elsif ($FORM{action} eq "kill")
{
print "<div><p>Unblock $FORM{ip}, trying permanent blocks...</p>\n<pre class='comment' style='white-space: pre-wrap;'>\n";
&printcmd("/usr/sbin/csf","-dr",$FORM{ip});
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
print "<div><p>Unblock $FORM{ip}, trying temporary blocks...</p>\n<pre class='comment' style='white-space: pre-wrap;'>\n";
&printcmd("/usr/sbin/csf","-trd",$FORM{ip});
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
&renderBtnReturn;
}
elsif ($FORM{action} eq "killallow")
{
print "<div><p>Unblock $FORM{ip}, trying permanent blocks...</p>\n<pre class='comment' style='white-space: pre-wrap;'>\n";
&printcmd("/usr/sbin/csf","-ar",$FORM{ip});
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
print "<div><p>Unblock $FORM{ip}, trying temporary blocks...</p>\n<pre class='comment' style='white-space: pre-wrap;'>\n";
&printcmd("/usr/sbin/csf","-tra",$FORM{ip});
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
&renderBtnReturn;
}
elsif ($FORM{action} eq "grep")
{
print "<div><p>Searching for $FORM{ip}...</p>\n";
&resize("top");
print "<pre class='comment' style='white-space: pre-wrap; height: 500px; overflow: auto; resize:both; clear:both' id='output'>\n";
my ($childin, $childout);
my $pid = open3($childin, $childout, $childout, "/usr/sbin/csf","-g",$FORM{ip});
my $unblock;
my $unallow;
while (<$childout>)
{
my $line = $_;
if ($line =~ /^csf.deny:\s(\S+)\s*/) {$unblock = 1}
if ($line =~ /^Temporary Blocks: IP:(\S+)\s*/) {$unblock = 1}
if ($line =~ /^csf.allow:\s(\S+)\s*/) {$unallow = 1}
if ($line =~ /^Temporary Allows: IP:(\S+)\s*/) {$unallow = 1}
print $_;
}
waitpid ($pid, 0);
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
&resize("bot",1);
if ($unblock) {print "<div><a class='btn btn-success' href='$script?action=kill&ip=$FORM{ip}'>Remove $FORM{ip} block</a></div>\n"}
if ($unallow) {print "<div><a class='btn btn-success' href='$script?action=killallow&ip=$FORM{ip}'>Remove $FORM{ip} allow</a></div>\n"}
&renderBtnReturn;
}
elsif ($FORM{action} eq "callow")
{
print "<div><p>Cluster Allow $FORM{ip}...</p>\n<pre class='comment' style='white-space: pre-wrap;'>\n";
&printcmd("/usr/sbin/csf","-ca",$FORM{ip},$FORM{comment});
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
&renderBtnReturn;
}
elsif ($FORM{action} eq "cignore")
{
print "<div><p>Cluster Ignore $FORM{ip}...</p>\n<pre class='comment' style='white-space: pre-wrap;'>\n";
&printcmd("/usr/sbin/csf","-ci",$FORM{ip},$FORM{comment});
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
&renderBtnReturn;
}
elsif ($FORM{action} eq "cirm")
{
print "<div><p>Cluster Remove ignore $FORM{ip}...</p>\n<pre class='comment' style='white-space: pre-wrap;'>\n";
&printcmd("/usr/sbin/csf","-cir",$FORM{ip});
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
&renderBtnReturn;
}
elsif ($FORM{action} eq "cloudflare")
{
&cloudflare;
}
elsif ($FORM{action} eq "cflist")
{
print "<div class='panel panel-info'><div class='panel-heading'>CloudFlare list $FORM{type} rules for user(s) $FORM{domains}:</div>\n";
print "<div class='panel-body'><pre class='comment' style='white-space: pre-wrap;'>";
&printcmd("/usr/sbin/csf","--cloudflare","list",$FORM{type},$FORM{domains});
print "</pre>\n</div></div>\n";
}
elsif ($FORM{action} eq "cftempdeny")
{
print "<div class='panel panel-info'><div class='panel-heading'>CloudFlare $FORM{do} $FORM{target} for user(s) $FORM{domains}:</div>\n";
print "<div class='panel-body'><pre class='comment' style='white-space: pre-wrap;'>\n";
&printcmd("/usr/sbin/csf","--cloudflare","tempadd",$FORM{do},$FORM{target},$FORM{domains});
print "</pre>\n</div></div>\n";
}
elsif ($FORM{action} eq "cfadd")
{
print "<div class='panel panel-info'><div class='panel-heading'>CloudFlare Add $FORM{type} $FORM{target} for user(s) $FORM{domains}:</div>\n";
print "<div class='panel-body'><pre class='comment' style='white-space: pre-wrap;'>";
&printcmd("/usr/sbin/csf","--cloudflare","add",$FORM{type},$FORM{target},$FORM{domains});
print "</pre>\n</div></div>\n";
}
elsif ($FORM{action} eq "cfremove")
{
print "<div class='panel panel-info'><div class='panel-heading'>CloudFlare Delete $FORM{type} $FORM{target} for user(s) $FORM{domains}:</div>\n";
print "<div class='panel-body'><pre class='comment' style='white-space: pre-wrap;'>";
&printcmd("/usr/sbin/csf","--cloudflare","del", $FORM{target},$FORM{domains});
print "</pre>\n</div></div>\n";
}
elsif ($FORM{action} eq "cdeny")
{
print "<div><p>Cluster Deny $FORM{ip}...</p>\n<pre class='comment' style='white-space: pre-wrap;'>\n";
&printcmd("/usr/sbin/csf","-cd",$FORM{ip},$FORM{comment});
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
&renderBtnReturn;
}
elsif ($FORM{action} eq "ctempdeny") {
$FORM{timeout} =~ s/\D//g;
if ($FORM{dur} eq "minutes") {$FORM{timeout} = $FORM{timeout} * 60}
if ($FORM{dur} eq "hours") {$FORM{timeout} = $FORM{timeout} * 60 * 60}
if ($FORM{dur} eq "days") {$FORM{timeout} = $FORM{timeout} * 60 * 60 * 24}
if ($FORM{ports} eq "") {$FORM{ports} = "*"}
print "<div><p>cluster Temporarily $FORM{do}ing $FORM{ip} for $FORM{timeout} seconds:</p>\n<pre class='comment' style='white-space: pre-wrap;'>\n";
if ($FORM{do} eq "block")
{
&printcmd("/usr/sbin/csf","-ctd",$FORM{ip},$FORM{timeout},"-p",$FORM{ports},$FORM{comment});
}
else
{
&printcmd("/usr/sbin/csf","-cta",$FORM{ip},$FORM{timeout},"-p",$FORM{ports},$FORM{comment});
}
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
&renderBtnReturn;
}
elsif ($FORM{action} eq "crm")
{
print "<div><p>Cluster Remove Deny $FORM{ip}...</p>\n<pre class='comment' style='white-space: pre-wrap;'>\n";
&printcmd("/usr/sbin/csf","-cr",$FORM{ip});
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
&renderBtnReturn;
}
elsif ($FORM{action} eq "carm")
{
print "<div><p>Cluster Remove Allow $FORM{ip}...</p>\n<pre class='comment' style='white-space: pre-wrap;'>\n";
&printcmd("/usr/sbin/csf","-car",$FORM{ip});
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
&renderBtnReturn;
}
elsif ($FORM{action} eq "cping")
{
print "<div><p>Cluster PING...</p>\n<pre class='comment' style='white-space: pre-wrap;'>\n";
&printcmd("/usr/sbin/csf","-cp");
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
&renderBtnReturn;
}
elsif ($FORM{action} eq "cgrep")
{
print "<div><p>Cluster GREP for $FORM{ip}...</p>\n";
print "<pre class='comment' style='white-space: pre-wrap;'>\n";
my ($childin, $childout);
my $pid = open3($childin, $childout, $childout, "/usr/sbin/csf","-cg",$FORM{ip});
my $unblock;
my $start = 0;
while (<$childout>)
{
my $line = $_;
if ($line =~ /^====/)
{
if ($start)
{
print "$line</pre><pre class='comment' style='white-space: pre-wrap;'>";
$start = 0;
}
else
{
print "</pre><pre class='comment' style='white-space: pre-wrap;background:#F4F4EA'>$line";
$start = 1;
}
}
else
{
print $line;
}
}
waitpid ($pid, 0);
print "...Done\n</pre></div>\n";
&renderBtnReturn;
}
elsif ($FORM{action} eq "cconfig")
{
$FORM{option} =~ s/\s*//g;
my %restricted;
if ($config{RESTRICT_UI})
{
sysopen (my $IN, "/usr/local/csf/lib/restricted.txt", O_RDWR | O_CREAT) or die "Unable to open file: $!";
flock ($IN, LOCK_SH);
while (my $entry = <$IN>)
{
chomp $entry;
$restricted{$entry} = 1;
}
close ($IN);
}
if ($restricted{$FORM{option}})
{
print "<div>Option $FORM{option} cannot be set with RESTRICT_UI enabled</div>\n";
exit;
}
print "<div><p>Cluster configuration option...</p>\n<pre class='comment' style='white-space: pre-wrap;'>\n";
&printcmd("/usr/sbin/csf","-cc",$FORM{option},$FORM{value});
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
&renderBtnReturn;
}
elsif ($FORM{action} eq "crestart")
{
print "<div><p>Cluster restart csf and lfd...</p>\n<pre class='comment' style='white-space: pre-wrap;'>\n";
&printcmd("/usr/sbin/csf --crestart");
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
&renderBtnReturn;
}
elsif ($FORM{action} eq "allow")
{
&editfile("/etc/csf/csf.allow","saveallow");
&renderBtnReturn;
}
elsif ($FORM{action} eq "saveallow")
{
&savefile("/etc/csf/csf.allow","both");
&renderBtnReturn;
}
elsif ($FORM{action} eq "redirect")
{
&editfile("/etc/csf/csf.redirect","saveredirect");
&renderBtnReturn;
}
elsif ($FORM{action} eq "saveredirect")
{
&savefile("/etc/csf/csf.redirect","both");
&renderBtnReturn;
}
elsif ($FORM{action} eq "smtpauth")
{
&editfile("/etc/csf/csf.smtpauth","savesmtpauth");
&renderBtnReturn;
}
elsif ($FORM{action} eq "savesmtpauth")
{
&savefile("/etc/csf/csf.smtpauth","both");
&renderBtnReturn;
}
elsif ($FORM{action} eq "reseller")
{
&editfile("/etc/csf/csf.resellers","savereseller");
&renderBtnReturn;
}
elsif ($FORM{action} eq "savereseller")
{
&savefile("/etc/csf/csf.resellers","");
&renderBtnReturn;
}
elsif ($FORM{action} eq "dirwatch")
{
&editfile("/etc/csf/csf.dirwatch","savedirwatch");
&renderBtnReturn;
}
elsif ($FORM{action} eq "savedirwatch")
{
&savefile("/etc/csf/csf.dirwatch","lfd");
&renderBtnReturn;
}
elsif ($FORM{action} eq "dyndns")
{
&editfile("/etc/csf/csf.dyndns","savedyndns");
&renderBtnReturn;
}
elsif ($FORM{action} eq "savedyndns")
{
&savefile("/etc/csf/csf.dyndns","lfd");
&renderBtnReturn;
}
elsif ($FORM{action} eq "blocklists")
{
&editfile("/etc/csf/csf.blocklists","saveblocklists");
&renderBtnReturn;
}
elsif ($FORM{action} eq "saveblocklists")
{
&savefile("/etc/csf/csf.blocklists","both");
&renderBtnReturn;
}
elsif ($FORM{action} eq "syslogusers")
{
&editfile("/etc/csf/csf.syslogusers","savesyslogusers");
&renderBtnReturn;
}
elsif ($FORM{action} eq "savesyslogusers")
{
&savefile("/etc/csf/csf.syslogusers","lfd");
&renderBtnReturn;
}
elsif ($FORM{action} eq "logfiles")
{
&editfile("/etc/csf/csf.logfiles","savelogfiles");
&renderBtnReturn;
}
elsif ($FORM{action} eq "savelogfiles")
{
&savefile("/etc/csf/csf.logfiles","lfd");
&renderBtnReturn;
}
elsif ($FORM{action} eq "deny")
{
&editfile("/etc/csf/csf.deny","savedeny");
&renderBtnReturn;
}
elsif ($FORM{action} eq "savedeny")
{
&savefile("/etc/csf/csf.deny","both");
&renderBtnReturn;
}
elsif ($FORM{action} eq "templates")
{
&editfile("/usr/local/csf/tpl/$FORM{template}","savetemplates","template");
&renderBtnReturn;
}
elsif ($FORM{action} eq "savetemplates")
{
&savefile("/usr/local/csf/tpl/$FORM{template}","",1);
&renderBtnReturn;
}
elsif ($FORM{action} eq "ignorefiles")
{
&editfile("/etc/csf/$FORM{ignorefile}","saveignorefiles","ignorefile");
&renderBtnReturn;
}
elsif ($FORM{action} eq "saveignorefiles")
{
&savefile("/etc/csf/$FORM{ignorefile}","lfd");
&renderBtnReturn;
}
elsif ($FORM{action} eq "repository")
{
}
elsif ( $FORM{action} =~ /^(conf|settings)$/ )
{
sysopen (my $IN, "/etc/csf/csf.conf", O_RDWR | O_CREAT) or die "Unable to open file: $!";
flock ($IN, LOCK_SH);
my @confdata = <$IN>;
close ($IN);
chomp @confdata;
my %restricted;
if ($config{RESTRICT_UI})
{
sysopen (my $IN, "/usr/local/csf/lib/restricted.txt", O_RDWR | O_CREAT) or die "Unable to open file: $!";
flock ($IN, LOCK_SH);
while (my $entry = <$IN>)
{
chomp $entry;
$restricted{$entry} = 1;
}
close ($IN);
}
print <<EOF;
<script type="text/javascript">
function CSFexpand(obj){
if (!obj.savesize) {obj.savesize=obj.size;}
var newsize = Math.max(obj.savesize,obj.value.length);
if (newsize > 120) {newsize = 120;}
obj.size = newsize;
}
</script>
EOF
print "<style>.hidepiece\{display:none\}</style>\n";
open (my $DIV, "<", "/usr/local/csf/lib/csf.div");
flock ($DIV, LOCK_SH);
my @divdata = <$DIV>;
close ($DIV);
print @divdata;
print "<div id='paginatediv2' class='text-center'></div>\n";
print "<form action='$script' method='post'>\n";
print "<input type='hidden' name='action' value='saveconf'>\n";
# #
# Process csf.conf config
# #
my $first = 1;
my @divnames;
my $comment = 0;
# #
# HEADER block parser
#
# Updated main functionality around 10/25 so that a new section was introduced
# HEADER:csf.conf
#
# This allows us to add information about a specific config file and show that
# to users when they view the "Firewall Configuration" page.
#
# The HEADER: block is completely optional, and we maintain reverse compatibility
# so that users can copy/paste old configs from previous versions.
# #
my $header_found = 0;
my $header_title = '';
my @header_lines = ();
# #
# Loop config data
# #
for ( my $i = 0; $i <= $#confdata; $i++ )
{
my $line = $confdata[$i];
# #
# Match lines like "# HEADER:csf.conf" (any number of # and spaces)
# #
if ( defined $line && $line =~ /^\s*#+\s*HEADER\s*:\s*(.*)/i )
{
$header_found = 1;
$header_title = $1;
$header_title =~ s/^\s+|\s+$//g;
# #
# Find start index including decorative lines
# #
my $start = $i;
while ( $start > 0 && defined $confdata[$start - 1] && $confdata[$start - 1] =~ /^\s*[#\s]+\s*$/ )
{
$start--;
}
# #
# Collect lines under header
# #
my $cur = $i + 1;
while ( $cur <= $#confdata && defined $confdata[$cur] && $confdata[$cur] =~ /^\s*\#/ )
{
my $raw = $confdata[$cur];
chomp $raw;
# #
# Blank comment line (just "#" or "# ") => push empty string
# #
if ( $raw =~ /^\s*#\s*$/ )
{
push @header_lines, '';
$cur++;
next;
}
# #
# Skip decorative lines like "# #", "### " after HEADER: tag
# #
if ( $raw =~ /^\s*#\s*#\s*$/ )
{
$cur++;
next;
}
# #
# HEADER: text is handled a bit differently than SECTION: tag.
# Strip leading # and space.
#
# @todo investigate later post v15.10
# #
$raw =~ s/^\s*#([ \t]*)//;
my $indent = $1 // '';
$indent =~ s/^[ \t]{0,3}//;
$raw = $indent . $raw;
# #
# Push
# #
push @header_lines, $raw;
$cur++;
}
# #
# Include trailing decorative lines
# #
my $last = $cur - 1;
while ( $last + 1 <= $#confdata && defined $confdata[$last + 1] && $confdata[$last + 1] =~ /^\s*[#\s]+\s*$/ )
{
$last++;
}
# #
# Remove header block
# #
if ( $last >= $start )
{
splice( @confdata, $start, $last - $start + 1 );
}
last;
}
}
# #
# Print header (if found)
# #
if ( $header_found )
{
print "<div class='header-block' style='padding-top: 1.6rem'>\n";
print "<div class='" . ( $codename eq 'webmin' ? 'section' : 'header' ) . "'>$header_title</div>\n";
print "<div class='" . ( $codename eq 'webmin' ? 'comment' : 'header-body' ) . "' style='padding-top:0px !important;margin-block-end: 3em !important;white-space: pre-wrap;font-family:monospace;line-height:1.2em;'>\n";
my $first_line = 1;
foreach my $hl ( @header_lines )
{
# #
# Count leading spaces/tabs
# #
my ($spaces) = $hl =~ /^([ \t]*)/;
# #
# Convert to if more than 4 spaces
# #
if ( $spaces && length( $spaces ) > 4 )
{
my $nbsp = ' ' x length( $spaces );
$hl =~ s/^[ \t]+/$nbsp/;
}
# #
# Skip printing \n only for the very first line
# #
print "${hl}\n";
}
print "</div> <!-- end section-body -->\n"; # close section-body
print "</div> <!-- end header-block -->\n"; # close header-block
}
# #
# Main configuration parsing loop
#
# Loop all setting descriptions
# #
my $first_line_comment = 1;
for ( my $i = 0; $i <= $#confdata; $i++ )
{
my $line = $confdata[$i];
# SECTION:Initial Settings
if ( ( $line !~ /^\#/ ) and ( $line =~ /=/ ) )
{
# TESTING = "0"
if ( $comment )
{
print "</div> <!-- end setting -->\n";
}
$comment = 0;
my ($start,$end) = split (/=/,$line,2); # Split at equal, TESTING = "0" => TESTING,"0"
my $name = $start; # TESTING_
my $cleanname = $start; # TESTING
$cleanname =~ s/\s//g;
$name =~ s/\s/\_/g; # TESTING_
if ( $end =~ /\"(.*)\"/ )
{
$end = $1;
}
my $escaped_end = html_escape( $end );
my $size = length( $end ) + 4;
my $class = "value-default";
my $tip = "";
my ($status,$range,$default) = sanity( $start, $end );
my $showrange = "";
my $showfrom;
my $showto;
if ( $range =~ /^(\d+)-(\d+)$/ )
{
$showfrom = $1;
$showto = $2;
}
# #
# Find modified settings with defined range
# #
if ( $default ne "" )
{
$showrange = " Default: $default [$range]";
if ( $end ne $default )
{
$class = "value-other";
$tip = "Modified";
}
}
# #
# Value › Bad Value
#
# $status = 0 Valid
# $status = 1 Invalid
# #
if ( $status )
{
$class = "value-warning";
$tip = "Warning. Invalid value specified.";
$showrange = " Recommended range: $range (Default: $default)";
}
# #
# Value › Restricted
#
# Setting cannot be changed using web interface; only csf.conf file.
# #
if ( $config{RESTRICT_UI} and ( $cleanname eq "CLUSTER_KEY" or $cleanname eq "UI_PASS" or $cleanname eq "UI_USER" ) )
{
print "<div class='" . ( $codename eq 'webmin' ? 'value-other' : 'value-restricted' ) . "' style='margin-block-end:1em !important;padding-left:10px !important'><b>$start</b> = <input type='text' value='********' size='14' disabled> (hidden restricted UI item)</div>\n";
}
# #
# Value › Disabled
#
# Setting disabled; cannot change.
# #
elsif ( $restricted{$cleanname} )
{
print "<div class='" . ( $codename eq 'webmin' ? 'value-other' : 'value-restricted' ) . "' style='margin-block-end:1em !important;padding-left:10px !important'><span class='glyphicon glyphicon-cog' style='font-size:1.3em;' data-tooltip='tooltip' title='Disabled. Protected by RESTRICT_UI'></span> <b>$start</b> = <input type='text' onFocus='CSFexpand(this);' onkeyup='CSFexpand(this);' value='$escaped_end' size='$size' disabled> (restricted UI item)</div>\n";
}
else
{
if ( $range eq "0-1" )
{
# #
# Value › Togglable (On/Off)
#
# Setting is toggable on/off with value of ether 0 or 1.
# AUTO_UPDATES
# LF_SPI
# IPV6
# #
my $switch_checked_0 = "";
my $switch_checked_1 = "";
my $switch_active_0 = "";
my $switch_active_1 = "";
if ($end == 0) {$switch_checked_0 = "checked"; $switch_active_0 = "active"}
if ($end == 1) {$switch_checked_1 = "checked"; $switch_active_1 = "active"}
print "<div class='$class' style='margin-block-end:1em !important;padding-left:10px !important'>"
. ($tip ? "<span class='glyphicon glyphicon-cog' style='font-size:1.3em;' data-tooltip='tooltip' title='$tip'></span> " : "")
. "<b>$start</b> = ";
print "<div class='btn-group' data-toggle='buttons'>\n";
print "<label class='btn btn-default btn-csf-config option-disabled $switch_active_0'>\n";
print "<input type='radio' name='${name}' value='0' $switch_checked_0> Off\n";
print "</label>\n";
print "<label class='btn btn-default btn-csf-config option-enabled $switch_active_1'>\n";
print "<input type='radio' name='${name}' value='1' $switch_checked_1> On\n";
print "</label>\n";
print "</div></div>\n";
}
elsif ( $range =~ /^(\d+)-(\d+)$/ and !( -e "/etc/csuibuttondisable" ) and ( $showto - $showfrom <= 20 ) and $end >= $showfrom and $end <= $showto )
{
# #
# Value › Custom Input Without Range
#
# Setting accepts custom input, but with no defaults.
# CC_LOOKUPS
# URLGET
# LF_CXS
# #
my $selected = "";
print "<div class='$class' style='margin-block-end:1em !important;padding-left:10px !important'>"
. ($tip ? "<span class='glyphicon glyphicon-cog' style='font-size:1.3em;' data-tooltip='tooltip' title='$tip'></span> " : "")
. "<b'>$start</b> = <select name='$name'>\n";
for ( $showfrom..$showto )
{
if ( $_ == $end )
{
$selected = "selected"
}
else
{
$selected = ""
}
print "<option $selected>$_</option>\n";
}
print "</select></div>\n";
}
else
{
# #
# Value › Custom Input With Range
#
# Setting with defined range [0-50], [1-2]
# Does NOT include toggable on/off settings [0-1]
# LF_CXS_PERM
# CC_SRC
# CC_DENY
# CC_ALLOW
# #
print "<div class='$class' style='margin-block-end:1em !important;padding-left:10px !important'>"
. ($tip ? "<span class='glyphicon glyphicon-cog' style='font-size:1.3em;' data-tooltip='tooltip' title='$tip'></span> " : "")
. "<b>$start</b> = <input type='text' onFocus='CSFexpand(this);' onkeyup='CSFexpand(this);' name='$name' value='$escaped_end' size='$size'>$showrange</div>\n";
}
}
}
else
{
# #
# Handles SECTION: parsing
# Skip decorative comments with "# #" and blank lines
# #
if ( $line =~ /^\#\s*\#/ ) { next }
if ( $line =~ /^\#\#/ ) { next }
if ( $line =~ /^\s*$/ ) { next }
# #
# Stylize SECTION tags
# #
if ( $line =~ /^\#\s*SECTION:(.*)/ )
{
my $section_name = $1;
# section anchors
my $section_id = lc $section_name;
$section_id =~ s/^\s+|\s+$//g; # trim
$section_id =~ s/[^a-z0-9]+/-/g; # replace crap with hyphen
$section_id =~ s/-{2,}/-/g; # collapse hyphens
$section_id =~ s/^-|-$//g; # trim hyphens
push @divnames, $section_name;
unless ( $first )
{
print "</div>\n"
}
print "<div class='virtualpage hidepiece'>\n";
# #
# Perform look-ahead for optional section description.
#
# If a comment block immediately follows after SECTION:
# by another comment block, treat as a description.
#
# Ensures users running CSF v10.08 or OLDER have backward
# compatibility.
# #
my @section_desc;
my $j = $i + 1;
# #
# Skip all the decorative "# #" lines after SECTION:
# #
while ( $j <= $#confdata && $confdata[$j] =~ /^\s*#\s*#\s*$/ )
{
$j++;
}
# #
# Collect description lines
# #
while ( $j <= $#confdata )
{
last if $confdata[$j] =~ /^\s*#\s*#\s*$/;
last if $confdata[$j] =~ /^\s*$/;
last unless $confdata[$j] =~ /^\#/;
push @section_desc, $confdata[$j];
$j++;
}
# #
# See what follows: skip all "# #" and blank lines, then see if
# the next meaningful line is a comment.
# #
my $k = $j;
while ( $k <= $#confdata && ( $confdata[$k] =~ /^\s*#\s*#\s*$/ || $confdata[$k] =~ /^\s*$/ ) )
{
$k++;
}
if ( @section_desc && $k <= $#confdata && $confdata[$k] =~ /^\#\s/ )
{
# #
# SECTION: has description
# #
print "<div id='$section_id' class='section-block' style='margin-block-start: 3em !important;'>\n";
print "<div class='section'>$section_name</div>\n";
print "<div class='" . ( $codename eq 'webmin' ? 'value-other' : 'section-body' ) . "' style='padding: 10px !important;white-space: pre-wrap;font-family:monospace;line-height:1.2em;margin-block-end:1em !important;'>";
my @desc_lines;
foreach my $dl ( @section_desc )
{
$dl =~ s/^\s*#+\s*//;
$dl = html_safe( $dl );
push @desc_lines, $dl;
}
print join("\n", @desc_lines);
print "</div>\n";
print "</div>\n";
$i = $j - 1;
}
# #
# SECTION: has no description; just show section name and move on
# #
else
{
print "<div id='$section_id' class='section-block'>\n";
print "<div class='section'>$section_name</div>\n";
print "</div>\n";
}
$first = 0;
next;
}
# #
# Handle regular comments
# #
if ( $line =~ /^\# / and $comment == 0 )
{
$comment = 1;
my $first_line_comment = 1;
print "<div class='comment' style='padding-top:0px !important;white-space: pre-wrap;font-family:monospace;line-height:1.2em;'>\n";
}
# #
# Capture indentation (spaces/tabs *after* #)
# #
my ( $spaces ) = $line =~ /^\s*#+([ \t]*)/;
my $nbsp = '';
# #
# Convert indentation to only if > 4 spaces
# #
if ( $spaces && length( $spaces ) > 4 )
{
$nbsp = ' ' x length( $spaces );
}
# #
# Remove leading hashes and one space (after capturing indent)
# #
$line =~ s/^\s*#+\s*//;
# #
# Sanitize line but allow only whitelisted tags
# #
$line = html_safe( $line );
# #
# Convert URLs to clickable links
# #
if ($line =~ m{^\s*https?://})
{
$line = "<a href=\"$line\" target=\"_blank\" rel=\"noopener noreferrer\">$line</a>";
}
else
{
$line = $nbsp . $line;
}
# #
# Prepend indentation (only if > 4 spaces)
# #
# $line = $nbsp . $line;
# #
# Replace newlines with <br>
# #
$line =~ s/\n/<br \/>\n/g;
if ( $comment && $first_line_comment )
{
print "$line<br />";
$first_line_comment = 0;
}
else
{
print "$line\n";
}
}
}
print "</div><br />\n";
print "<div id='paginatediv' class='text-center'>\n<a class='btn btn-default' href='javascript:pagecontent.showall()'>Show All</a> <a class='btn btn-default' href='#' rel='previous'>Prev</a> <select style='width: 250px'></select> <a class='btn btn-default' href='#' rel='next' >Next</a>\n</div>\n";
print <<EOD;
<script type="text/javascript">
var pagecontent=new virtualpaginate({
piececlass: "virtualpage", //class of container for each piece of content
piececontainer: "div", //container element type (ie: "div", "p" etc)
pieces_per_page: 1, //Pieces of content to show per page (1=1 piece, 2=2 pieces etc)
defaultpage: 0, //Default page selected (0=1st page, 1=2nd page etc). Persistence if enabled overrides this setting.
wraparound: false,
persist: false //Remember last viewed page and recall it when user returns within a browser session?
});
EOD
print "pagecontent.buildpagination(['paginatediv','paginatediv2'],[";
foreach my $line (@divnames)
{
print "'$line',"
}
print "''])\npagecontent.showall();\n</script>\n";
print "<br /><div class='text-center'><input type='submit' class='btn btn-default' value='Change'></div>\n";
print "</form>\n";
&renderBtnReturn;
}
elsif ($FORM{action} eq "saveconf")
{
sysopen (my $IN, "/etc/csf/csf.conf", O_RDWR | O_CREAT) or die "Unable to open file: $!";
flock ($IN, LOCK_SH);
my @confdata = <$IN>;
close ($IN);
chomp @confdata;
my %restricted;
if ($config{RESTRICT_UI})
{
sysopen (my $IN, "/usr/local/csf/lib/restricted.txt", O_RDWR | O_CREAT) or die "Unable to open file: $!";
flock ($IN, LOCK_SH);
while (my $entry = <$IN>)
{
chomp $entry;
$restricted{$entry} = 1;
}
close ($IN);
}
sysopen (my $OUT, "/etc/csf/csf.conf", O_WRONLY | O_CREAT) or die "Unable to open file: $!";
flock ($OUT, LOCK_EX);
seek ($OUT, 0, 0);
truncate ($OUT, 0);
for (my $x = 0; $x < @confdata;$x++)
{
if ( ($confdata[$x] !~ /^\#/) and ( $confdata[$x] =~ /=/) )
{
my ( $start,$end) = split (/=/,$confdata[$x],2);
if ( $end =~ /\"(.*)\"/) {$end = $1}
my $name = $start;
my $sanity_name = $start;
$name =~ s/\s/\_/g;
$sanity_name =~ s/\s//g;
if ( $restricted{$sanity_name})
{
print $OUT "$confdata[$x]\n";
}
else
{
print $OUT "$start= \"$FORM{$name}\"\n";
$end = $FORM{$name};
}
}
else
{
print $OUT "$confdata[$x]\n";
}
}
close ( $OUT);
ConfigServer::Config::resetconfig();
my $newconfig = ConfigServer::Config->loadconfig();
my %newconfig = $config->config;
foreach my $key (keys %newconfig)
{
my ( $insane,$range,$default) = sanity( $key,$newconfig{$key});
if ( $insane) {print "<br>WARNING: $key sanity check. $key = \"$newconfig{$key}\". Recommended range: $range (Default: $default)\n"}
}
print "<div>Changes saved. You should restart both csf and lfd.</div>\n";
print "<div><form action='$script' method='post'><input type='hidden' name='action' value='restartboth'><input type='submit' class='btn btn-default' value='Restart csf+lfd'></form></div>\n";
&renderBtnReturn;
}
elsif ( $FORM{action} eq "viewlogs" )
{
if (-e "/var/lib/csf/stats/iptables_log")
{
open (my $IN, "<", "/var/lib/csf/stats/iptables_log" ) or die "Unable to open file: $!";
flock ( $IN, LOCK_SH);
my @iptables = <$IN>;
close ( $IN);
chomp @iptables;
@iptables = reverse @iptables;
my $from;
my $to;
my $divcnt = 0;
my $expcnt = @iptables;
if ( $iptables[0] =~ /\|(\S+\s+\d+\s+\S+)/)
{
$from = $1
}
if ( $iptables[-1] =~ /\|(\S+\s+\d+\s+\S+)/)
{
$to = $1
}
print "<div class='pull-right'><button type='button' class='btn btn-primary glyphicon glyphicon-arrow-down' data-tooltip='tooltip' title='Expand All' onClick='\$(\".submenu\").show();'></button>\n";
print "<button type='button' class='btn btn-primary glyphicon glyphicon-arrow-up' data-tooltip='tooltip' title='Collapse All' onClick='\$(\".submenu\").hide();'></button></div>\n";
print "<h4>Last $config{ST_IPTABLES} iptables logs*, latest:<code>$from</code> oldest:<code>$to</code></h4><br />\n";
print "<table class='table table-bordered table-striped'>\n";
print "<thead><tr><th>Time</th><th width='50%'>From</th><th>Port</th><th>I/O</th><th width='50%'>To</th><th>Port</th><th>Proto</th></tr></thead>\n";
my $size = scalar @iptables;
if ( $size > $config{ST_IPTABLES})
{
$size = $config{ST_IPTABLES}
}
for (my $x = 0 ;$x < $size ;$x++)
{
my $line = $iptables[$x];
$divcnt++;
my ( $text,$log) = split(/\|/,$line);
my ( $time,$desc,$in,$out,$src,$dst,$spt,$dpt,$proto,$inout);
if ( $log =~ /IN=(\S+)/) {$in = $1}
if ( $log =~ /OUT=(\S+)/) {$out = $1}
if ( $log =~ /SRC=(\S+)/) {$src = $1}
if ( $log =~ /DST=(\S+)/) {$dst = $1}
if ( $log =~ /SPT=(\d+)/) {$spt = $1}
if ( $log =~ /DPT=(\d+)/) {$dpt = $1}
if ( $log =~ /PROTO=(\S+)/) {$proto = $1}
if ( $text ne "" )
{
$text =~ s/\(/\<br\>\(/g;
if ( $in and $src)
{
$src = $text ; $dst .= " <br>(server)"
}
elsif ( $out and $dst)
{
$dst = $text ; $src .= " <br>(server)"
}
}
if ( $log =~ /^(\S+\s+\d+\s+\S+)/)
{
$time = $1
}
$inout = "n/a";
if ( $in) {$inout = "in"}
elsif ( $out) {$inout = "out"}
print "<tr><td style='white-space: nowrap;'><button type='button' class='btn btn-primary glyphicon glyphicon-resize-vertical' data-tooltip='tooltip' title='Toggle Info' onClick='\$(\"#s$divcnt\").toggle()'></button> $time</td><td>$src</td><td>$spt</td><td>$inout</td><td>$dst</td><td>$dpt</td><td>$proto</td></tr>\n";
$log =~ s/\&/\&\;/g;
$log =~ s/>/\>\;/g;
$log =~ s/</\<\;/g;
print "<tr style='display:none' class='submenu' id='s$divcnt'><td colspan='7'><span>$log</span></td></tr>\n";
}
print "</table>\n";
print "<div class='bs-callout bs-callout-warning'>* These iptables logs taken from $config{IPTABLES_LOG} will not necessarily show all packets blocked by iptables. For example, ports listed in DROP_NOLOG or the settings for DROP_LOGGING/DROP_IP_LOGGING/DROP_ONLYRES/DROP_PF_LOGGING will affect what is logged. Additionally, there is rate limiting on all iptables log rules to prevent log file flooding</div>\n";
}
else
{
print "<div class='bs-callout bs-callout-info'> No logs entries found</div>\n";
}
&renderBtnReturn;
}
elsif ( $FORM{action} eq "sips")
{
sysopen (my $IN, "/etc/csf/csf.sips", O_RDWR | O_CREAT) or die "Unable to open file: $!";
flock ( $IN, LOCK_SH);
my @confdata = <$IN>;
close ( $IN);
chomp @confdata;
print "<form action='$script' method='post'><input type='hidden' name='action' value='sipsave'><br>\n";
print "<table class='table table-bordered table-striped'>\n";
print "<tr><td><b>IP Address</b></td><td><b>Deny All Access to IP</b></td></tr>\n";
my %sips;
open (my $SIPS, "<","/etc/csf/csf.sips");
flock ( $SIPS, LOCK_SH);
my @data = <$SIPS>;
close ( $SIPS);
chomp @data;
foreach my $line ( @data )
{
if ( $line =~ /^(\s|\#|$)/) {next}
$sips{$line} = 1;
}
my $ethdev = ConfigServer::GetEthDev->new();
my %g_ipv4 = $ethdev->ipv4;
my %g_ipv6 = $ethdev->ipv6;
foreach my $key (sort keys %g_ipv4)
{
my $ip = $key;
if ( $ip =~ /^127\.0\.0/)
{
next
}
my $chk = "ip_$ip";
$chk =~ s/\./\_/g;
my $checked = "";
if ( $sips{$ip})
{
$checked = "checked"
}
print "<tr><td>$ip</td><td><input type='checkbox' name='$chk' $checked></td></tr>\n";
}
foreach my $key (sort keys %g_ipv6)
{
my $ip = $key;
my $chk = "ip_$ip";
$chk =~ s/\./\_/g;
my $checked = "";
if ( $sips{$ip})
{
$checked = "checked"
}
print "<tr><td>$ip</td><td><input type='checkbox' name='$chk' $checked></td></tr>\n";
}
print "<tr><td colspan='2'><input type='submit' class='btn btn-default' value='Change'></td></tr>\n";
print "</table></form>\n";
&renderBtnReturn;
}
elsif ( $FORM{action} eq "sipsave" )
{
open (my $IN,"<","/etc/csf/csf.sips");
flock ( $IN, LOCK_SH);
my @data = <$IN>;
close ( $IN);
chomp @data;
open (my $OUT,">","/etc/csf/csf.sips");
flock ( $OUT, LOCK_EX);
foreach my $line ( @data )
{
if ( $line =~ /^\#/) {print $OUT "$line\n"} else {last}
}
foreach my $key (keys %FORM)
{
if ( $key =~ /^ip_(.*)/)
{
my $ip = $1;
$ip =~ s/\_/\./g;
print $OUT "$ip\n";
}
}
close( $OUT);
print "<div>Changes saved. You should restart csf.</div>\n";
print "<div><form action='$script' method='post'><input type='hidden' name='action' value='restart'><input type='submit' class='btn btn-default' value='Restart csf'></form></div>\n";
&renderBtnReturn;
}
elsif ( $FORM{action} eq "upgrade" )
{
if ( $config{THIS_UI})
{
print "<div>You cannot upgrade through the UI as restarting lfd will interrupt this session. You must login to the root shell to upgrade csf using:\n<p><b>csf -u</b></div>\n";
}
else
{
print "<div><p>Upgrading csf...</p>\n";
&resize("top");
print "<pre class='comment' style='white-space: pre-wrap; height: 500px; overflow: auto; resize:both; clear:both' id='output'>\n";
&printcmd("/usr/sbin/csf","-u");
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
&resize("bot",1);
open (my $IN, "<", "/etc/csf/version.txt") or die $!;
flock ( $IN, LOCK_SH);
$myv = <$IN>;
close ( $IN);
chomp $myv;
}
&renderBtnReturn;
}
elsif ( $FORM{action} eq "denyf" )
{
print "<div><p>Removing all entries from csf.deny...</p>\n";
&resize("top");
print "<pre class='comment' style='white-space: pre-wrap; height: 500px; overflow: auto; resize:both; clear:both' id='output'>\n";
&printcmd("/usr/sbin/csf","-df");
&printcmd("/usr/sbin/csf","-tf");
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
&resize("bot",1);
&renderBtnReturn;
}
elsif ( $FORM{action} eq "csftest" )
{
print "<div><p>Testing iptables...</p>\n<pre class='comment' style='white-space: pre-wrap;'>\n";
&printcmd("/usr/local/csf/bin/csftest.pl");
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
print "<div>You should restart csf after having run this test.</div>\n";
print "<div><form action='$script' method='post'><input type='hidden' name='action' value='restart'><input type='submit' class='btn btn-default' value='Restart csf'></form></div>\n";
&renderBtnReturn;
}
elsif ( $FORM{action} eq "profiles" )
{
my @profiles = sort glob("/usr/local/csf/profiles/*");
my @backups = reverse glob("/var/lib/csf/backup/*");
print "<form action='$script' method='post'><input type='hidden' name='action' value='profileapply'>\n";
print "<table class='table table-bordered table-striped'>\n";
print "<thead><tr><th>Preconfigured Profiles</th><th style='border-left:1px solid #990000'> </th></tr></thead>\n";
foreach my $profile (@profiles)
{
my ( $file, undef) = fileparse( $profile);
$file =~ s/\.conf$//;
my $text;
open (my $IN, "<", $profile);
flock ( $IN, LOCK_SH);
my @profiledata = <$IN>;
close ( $IN);
chomp @profiledata;
if ( $file eq "reset_to_defaults" )
{
$text = "This is the installation default profile and will reset all csf.conf settings, including enabling TESTING mode";
}
elsif ( $profiledata[0] =~ /^\# Profile:/)
{
foreach my $line ( @profiledata )
{
if ( $line =~ /^\# (.*)$/)
{
$text .= "$1 "
}
}
}
print "<tr><td><b>$file</b><br>\n$text</td><td style='border-left:1px solid #990000'><input type='radio' name='profile' value='$file'></td></tr>\n";
}
print "<tr><td>You can apply one or more of these profiles to csf.conf. Apart from reset_to_defaults, most of these profiles contain only a subset of settings. You can find out what will be changed by comparing the profile to the current configuration below. A backup of csf.conf will be created before any profile is applied.</td><td style='border-left:1px solid #990000'><input type='submit' class='btn btn-default' value='Apply Profile'></td></tr>\n";
print "</table>\n";
print "</form>\n";
print "<br><form action='$script' method='post'><input type='hidden' name='action' value='profilebackup'>\n";
print "<table class='table table-bordered table-striped'>\n";
print "<thead><tr><th>Backup csf.conf</th></tr></thead>\n";
print "<tr><td>Create a backup of csf.conf. You can use an optional name for the backup that should only contain alphanumerics. Other characters (including spaces) will be replaced with an underscore ( _ )</td></tr>\n";
print "<tr><td><input type='text' size='40' name='backup' placeholder='Optional name'> <input type='submit' class='btn btn-default' value='Create Backup'></td></tr>\n";
print "</table>\n";
print "</form>\n";
print "<br><form action='$script' method='post'><input type='hidden' name='action' value='profilerestore'>\n";
print "<table class='table table-bordered table-striped'>\n";
print "<thead><tr><th>Restore Backup Of csf.conf</th></tr></thead>\n";
print "<tr><td><select name='backup' size='10' style='min-width:400px'>\n";
foreach my $backup (@backups)
{
my ( $file, undef ) = fileparse( $backup );
my ( $stamp, undef ) = split(/_/,$file );
print "<optgroup label='".localtime( $stamp).":'><option>$file</option></optgroup>\n";
}
print "</select></td></tr>\n";
print "<tr><td><input type='submit' class='btn btn-default' value='Restore Backup'></td></tr>\n";
print "</table>\n";
print "</form>\n";
print "<br><form action='$script' method='post'><input type='hidden' name='action' value='profilediff'>\n";
print "<table class='table table-bordered table-striped'>\n";
print "<thead><tr><th>Compare Configurations</th></tr></thead>\n";
print "<tr><td>Select first configuration:<br>\n<select name='profile1' size='10' style='min-width:400px'>\n";
print "<optgroup label='Profiles:'>\n";
foreach my $profile (@profiles)
{
my ( $file, undef) = fileparse( $profile);
$file =~ s/\.conf$//;
print "<option>$file</option>\n";
}
print "</optgroup>\n";
foreach my $backup (@backups)
{
my ( $file, undef ) = fileparse( $backup );
my ( $stamp,undef ) = split(/_/, $file );
print "<optgroup label='".localtime( $stamp).":'><option>$file</option></optgroup>\n";
}
print "</select></td></tr>\n";
print "<tr><td style='border-top:1px dashed #990000'>Select second configuration:<br>\n<select name='profile2' size='10' style='min-width:400px'>\n";
print "<optgroup label='Current Configuration:'><option value='current' selected>/etc/csf/csf.conf</option></optgroup>\n";
print "<optgroup label='Profiles:'>\n";
foreach my $profile (@profiles)
{
my ( $file, undef) = fileparse( $profile);
$file =~ s/\.conf$//;
print "<option>$file</option>\n";
}
print "</optgroup>\n";
foreach my $backup (@backups)
{
my ( $file, undef ) = fileparse( $backup );
my ( $stamp, undef ) = split( /_/, $file );
print "<optgroup label='".localtime( $stamp).":'><option>$file</option></optgroup>\n";
}
print "</select></td></tr>\n";
print "<tr><td><input type='submit' class='btn btn-default' value='Compare Config/Backup/Profile Settings'></td></tr>\n";
print "</table>\n";
print "</form>\n";
&renderBtnReturn;
}
elsif ( $FORM{action} eq "profileapply" )
{
my $profile = $FORM{profile};
$profile =~ s/\W/_/g;
print "<div><p>Applying profile ( $profile)...</p>\n<pre class='comment' style='white-space: pre-wrap;'>\n";
&printcmd("/usr/sbin/csf","--profile","apply",$profile);
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
print "<div>You should restart both csf and lfd.</div>\n";
print "<div><form action='$script' method='post'><input type='hidden' name='action' value='restartboth'><input type='submit' class='btn btn-default' value='Restart csf+lfd'></form></div>\n";
&renderBtnReturn;
}
elsif ( $FORM{action} eq "profilebackup" )
{
my $profile = $FORM{backup};
$profile =~ s/\W/_/g;
print "<div><p>Creating backup...</p>\n<pre class='comment' style='white-space: pre-wrap;'>\n";
&printcmd("/usr/sbin/csf","--profile","backup",$profile);
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
&renderBtnReturn;
}
elsif ( $FORM{action} eq "profilerestore" )
{
my $profile = $FORM{backup};
$profile =~ s/\W/_/g;
print "<div><p>Restoring backup ( $profile)...</p>\n<pre class='comment' style='white-space: pre-wrap;'>\n";
&printcmd("/usr/sbin/csf","--profile","restore",$profile);
print "</pre>\n<p>...<b>Done</b>.</p></div>\n";
print "<div>You should restart both csf and lfd.</div>\n";
print "<div><form action='$script' method='post'><input type='hidden' name='action' value='restartboth'><input type='submit' class='btn btn-default' value='Restart csf+lfd'></form></div>\n";
&renderBtnReturn;
}
elsif ( $FORM{action} eq "profilediff" )
{
my $profile1 = $FORM{profile1};
my $profile2 = $FORM{profile2};
$profile2 =~ s/\W/_/g;
$profile2 =~ s/\W/_/g;
print "<table class='table table-bordered table-striped'>\n";
my ( $childin, $childout);
my $pid = open3( $childin, $childout, $childout, "/usr/sbin/csf","--profile","diff",$profile1,$profile2);
while (<$childout>)
{
$_ =~ s/\[|\]//g;
my ( $var,$p1,$p2) = split(/\s+/,$_);
if ( $var eq "" ) {
next;
}
elsif ( $var eq "SETTING" )
{
print "<tr><td><b>$var</b></td><td><b>$p1</b></td><td><b>$p2</b></td></tr>\n";
}
else
{
print "<tr><td>$var</td><td>$p1</td><td>$p2</td></tr>\n";
}
}
waitpid ( $pid, 0);
print "</table>\n";
&renderBtnReturn;
}
elsif ( $FORM{action} eq "viewports" )
{
print "<div><h4>Ports listening for external connections and the executables running behind them:</h4></div>\n";
print "<table class='table table-bordered table-striped'>\n";
print "<thead><tr><th>Port</th><th>Proto</th><th>Open</th><th>Conns</th><th>PID</th><th>User</th><th>Command Line</th><th>Executable</th></tr></thead>\n";
my %listen = ConfigServer::Ports->listening;
my %ports = ConfigServer::Ports->openports;
foreach my $protocol (sort keys %listen)
{
foreach my $port (sort {$a <=> $b} keys %{$listen{$protocol}})
{
foreach my $pid (sort {$a <=> $b} keys %{$listen{$protocol}{$port}})
{
my $fopen;
if ( $ports{$protocol}{$port})
{
$fopen = "4"
}
else
{
$fopen = "-"
}
if ( $config{IPV6} and $ports{$protocol."6"}{$port})
{
$fopen .= "/6"} else {$fopen .= "/-"
}
my $fcmd = ( $listen{$protocol}{$port}{$pid}{cmd});
$fcmd =~ s/\</\</g;
$fcmd =~ s/\&/\&/g;
my $fexe = $listen{$protocol}{$port}{$pid}{exe};
$fexe =~ s/\</\</g;
$fexe =~ s/\&/\&/g;
my $fconn = $listen{$protocol}{$port}{$pid}{conn};
print "<tr><td>$port</td><td>$protocol</td><td>$fopen</td><td>$fconn</td><td>$pid</td><td>$listen{$protocol}{$port}{$pid}{user}</td><td style='overflow: hidden;text-overflow: ellipsis; width:50%'>$fcmd</td><td style='overflow: hidden;text-overflow: ellipsis; width:50%'>$fexe</td></tr>\n";
}
}
}
print "</table>\n";
&renderBtnReturn;
}
elsif ( $mobile)
{
print "<table class='table table-bordered table-striped'>\n";
print "<tr><td><form action='$script' method='post'><input type='hidden' name='mobi' value='$mobile'><input type='hidden' name='action' value='qallow'><input type='submit' class='btn btn-default' value='Quick Allow'></td><td style='width:100%'><input type='text' name='ip' value='' size='18' style='background-color: #BDECB6'></form></td></tr>\n";
print "<tr><td><form action='$script' method='post'><input type='hidden' name='mobi' value='$mobile'><input type='hidden' name='action' value='qdeny'><input type='submit' class='btn btn-default' value='Quick Deny'></td><td style='width:100%'><input type='text' name='ip' value='' size='18' style='background-color: #FFD1DC'></form></td></tr>\n";
print "<tr><td><form action='$script' method='post'><input type='hidden' name='mobi' value='$mobile'><input type='hidden' name='action' value='qignore'><input type='submit' class='btn btn-default' value='Quick Ignore'></td><td style='width:100%'><input type='text' name='ip' value='' size='18' style='background-color: #D9EDF7'></form></td></tr>\n";
print "<tr><td><form action='$script' method='post'><input type='hidden' name='mobi' value='$mobile'><input type='hidden' name='action' value='kill'><input type='submit' class='btn btn-default' value='Quick Unblock'></td><td style='width:100%'><input type='text' name='ip' value='' size='18'></form></td></tr>\n";
print "</table>\n";
}
elsif ( $FORM{action} eq "fix" )
{
print "<div class='bs-callout bs-callout-warning'>These options should only be used as a last resort as most of them will reduce the effectiveness of csf and lfd to protect the server</div>\n";
print "<table class='table table-bordered table-striped'>\n";
print "<thead><tr><th colspan='2'>Fix Common Problems</th></tr></thead>";
if ( $config{LF_SPI} == 0 )
{
print "<tr><td><button class='btn btn-default' disabled>Disable SPI</button>\n";
}
else
{
print "<tr><td><button type='button' class='btn btn-default confirmButton' data-query='Are you sure you want to disable LF_SPI?' data-href='$script?action=fixspi' data-toggle='modal' data-target='#confirmmodal'>Disable SPI</button>\n";
}
print "</td><td style='width:100%'>If you find that ports listed in TCP_IN/UDP_IN are being blocked by iptables (e.g. port 80) as seen in /var/log/messages and users can only connect to the server if entered in csf.allow, then it could be that the kernel (usually on virtual servers) is broken and cannot perform connection tracking. In this case, disabling the Stateful Packet Inspection functionality of csf (LF_SPI) may help\n";
if ( $config{LF_SPI} == 0 )
{
print "<br><strong>Note: LF_SPI is already disabled</strong>";
}
print "</td></tr>\n";
if ( $config{TCP_IN} =~ /30000:35000/)
{
print "<tr><td><button class='btn btn-default' disabled>Open PASV FTP Hole</button>\n";
}
else
{
print "<tr><td><button type='button' class='btn btn-default confirmButton' data-query='Are you sure you want to open PASV FTP hole?' data-href='$script?action=fixpasvftp' data-toggle='modal' data-target='#confirmmodal'>Open PASV FTP Hole</button>\n";
}
print "</td><td style='width:100%'>If the kernel (usually on virtual servers) is broken and cannot perform ftp connection tracking, or if you are trying to use FTP over SSL, this option will open a hole in the firewall to allow PASV connections through\n";
if ( $config{TCP_IN} =~ /30000:35000/)
{
print "<br><strong>Note: The port range 30000 to 35000 is already open in csf</strong>\n";
}
print "</td></tr>\n";
if ( $config{PT_USERKILL} == 0 )
{
print "<tr><td><button class='btn btn-default' disabled>Disable PT_USERKILL</button>\n";
}
else
{
print "<tr><td><button type='button' class='btn btn-default confirmButton' data-query='Are you sure you want to disable PT_USERKILL?' data-href='$script?action=fixkill' data-toggle='modal' data-target='#confirmmodal'>Disable PT_USERKILL</button>\n";
}
print "</td><td style='width:100%'>If lfd is killing running processes and you have PT_USERKILL enabled, then we recommend that you disable this feature\n";
if ( $config{PT_USERKILL} == 0 )
{
print "<br><strong>Note: PT_USERKILL is already disabled</strong>";
}
print "</td></tr>\n";
if ( $config{SMTP_BLOCK} == 0 )
{
print "<tr><td><button class='btn btn-default' disabled>Disable SMTP_BLOCK</button>\n";
}
else
{
print "<tr><td><button type='button' class='btn btn-default confirmButton' data-query='Are you sure you want to disable SMTP_BLOCK?' data-href='$script?action=fixsmtp' data-toggle='modal' data-target='#confirmmodal'>Disable SMTP_BLOCK</button>\n";
}
print "</td><td style='width:100%'>If scripts on the server are unable to send out email via external SMTP connections and you have SMTP_BLOCK enabled then those scripts should be configured to send email either through /usr/sbin/sendmail or localhost on the server. If this is not possible then disabling SMTP_BLOCK can fix this\n";
if ( $config{SMTP_BLOCK} == 0 )
{
print "<br><strong>Note: SMTP_BLOCK is already disabled</strong>";
}
print "</td></tr>\n";
print "<tr><td><button type='button' class='btn btn-default confirmButton' data-query='Are you sure you want to disable all alerts?' data-href='$script?action=fixalerts' data-toggle='modal' data-target='#confirmmodal'>Disable All Alerts</button>\n";
print "</td><td style='width:100%'>If you really want to disable all alerts in lfd you can do so here. This is <strong>not</strong> recommended in any situation - you should go through the csf configuration and only disable those you do not want. As new features are added to csf you may find that you have to go into the csf configuration and disable them manually as this procedure only disables the ones that it is aware of when applied\n";
print "</td></tr>\n";
print "<tr><td><button type='button' class='btn btn-danger confirmButton' data-query='Are you sure you want to reinstall csf and lose all modifications?' data-href='$script?action=fixnuclear' data-toggle='modal' data-target='#confirmmodal'>Reinstall csf</button>\n";
print "</td><td style='width:100%'>If all else fails this option will <strong>completely</strong> uninstall csf and install it again with completely default options (including TESTING mode). The previous configuration will be lost including all modifications\n";
print "</td></tr>\n";
print "</table>\n";
&renderBtnReturn;
&confirmmodal;
}
elsif ( $FORM{action} eq "fixpasvftp" )
{
print "<div class='panel panel-default'>\n";
print "<div class='panel-heading panel-heading'>Enabling pure-ftpd PASV hole:</div>\n";
print "<div class='panel-body'>";
&resize("top");
print "<pre class='comment' style='white-space: pre-wrap; height: 500px; overflow: auto; resize:both; clear:both' id='output'>\n";
my $ftpdone = 0;
if (-e "/usr/local/cpanel/version")
{
require Cpanel::Config;
import Cpanel::Config;
my $cpconf = Cpanel::Config::loadcpconf();
if ( $cpconf->{ftpserver} eq "pure-ftpd" )
{
copy("/etc/pure-ftpd.conf","/etc/pure-ftpd.conf-".time."_prefixpasvftp");
sysopen (my $PUREFTP,"/etc/pure-ftpd.conf", O_RDWR | O_CREAT);
flock ( $PUREFTP, LOCK_EX);
my @ftp = <$PUREFTP>;
chomp @ftp;
seek ( $PUREFTP, 0, 0);
truncate ( $PUREFTP, 0);
my $hit = 0;
foreach my $line (@ftp)
{
if ( $line =~ /^#?\s*PassivePortRange/i)
{
if ( $hit)
{
next
}
$line = "PassivePortRange 30000 35000";
$hit = 1;
}
print $PUREFTP "$line\n";
}
unless ( $hit)
{
print $PUREFTP "PassivePortRange 30000 35000"
}
close ( $PUREFTP);
&printcmd( "/scripts/restartsrv_pureftpd" );
$ftpdone = 1;
}
}
if ( $config{TCP_IN} =~ /30000:35000/ )
{
print "PASV port range 30000:35000 already exists in TCP_IN/TCP6_IN\n";
}
else
{
$config{TCP_IN} .= ",30000:35000";
$config{TCP6_IN} .= ",30000:35000";
copy( "/etc/csf/csf.conf","/var/lib/csf/backup/".time."_prefixpasvftp" );
sysopen ( my $CSFCONF,"/etc/csf/csf.conf", O_RDWR | O_CREAT );
flock ( $CSFCONF, LOCK_EX );
my @csf = <$CSFCONF>;
chomp @csf;
seek ( $CSFCONF, 0, 0 );
truncate ( $CSFCONF, 0 );
foreach my $line ( @csf )
{
if ( $line =~ /^TCP6_IN/ )
{
print $CSFCONF "TCP6_IN = \"$config{TCP6_IN}\"\n";
print "*** PASV port range 30000:35000 added to the TCP6_IN port list\n";
}
elsif ( $line =~ /^TCP_IN/ )
{
print $CSFCONF "TCP_IN = \"$config{TCP_IN}\"\n";
print "*** PASV port range 30000:35000 added to the TCP_IN port list\n";
}
else
{
print $CSFCONF $line."\n";
}
}
close ( $CSFCONF );
}
print "</pre></div>\n";
&resize("bot",1);
print "<div class='panel-footer panel-footer'>Completed<br>\n";
unless ( $ftpdone )
{
print "<p><strong>You MUST now open the same port range hole (30000 to 35000) in your FTP Server configuration</strong></p>\n"
}
print "</div>\n";
print "</div>\n";
print "<div>You MUST now restart both csf and lfd:</div>\n";
print "<div><form action='$script' method='post'><input type='hidden' name='action' value='restartboth'><input type='submit' class='btn btn-default' value='Restart csf+lfd'></form></div>\n";
&renderBtnReturn;
}
elsif ( $FORM{action} eq "fixspi" )
{
print "<div class='panel panel-default'>\n";
print "<div class='panel-heading panel-heading'>Disabling LF_SPI:</div>\n";
print "<div class='panel-body'>";
copy( "/etc/csf/csf.conf","/var/lib/csf/backup/".time."_prefixspi" );
sysopen ( my $CSFCONF,"/etc/csf/csf.conf", O_RDWR | O_CREAT );
flock ( $CSFCONF, LOCK_EX );
my @csf = <$CSFCONF>;
chomp @csf;
seek ( $CSFCONF, 0, 0 );
truncate ( $CSFCONF, 0 );
foreach my $line ( @csf )
{
if ( $line =~ /^LF_SPI / )
{
print $CSFCONF "LF_SPI = \"0\"\n";
print "*** LF_SPI disabled ***\n";
}
else
{
print $CSFCONF $line."\n";
}
}
close ( $CSFCONF);
print "</div>\n";
print "<div class='panel-footer panel-footer'>Completed</div>\n";
print "</div>\n";
print "<div>You MUST now restart both csf and lfd:</div>\n";
print "<div><form action='$script' method='post'><input type='hidden' name='action' value='restartboth'><input type='submit' class='btn btn-default' value='Restart csf+lfd'></form></div>\n";
&renderBtnReturn;
}
elsif ( $FORM{action} eq "fixkill" )
{
print "<div class='panel panel-default'>\n";
print "<div class='panel-heading panel-heading'>Disabling PT_USERKILL:</div>\n";
print "<div class='panel-body'>";
copy( "/etc/csf/csf.conf","/var/lib/csf/backup/".time."_prefixkill" );
sysopen ( my $CSFCONF,"/etc/csf/csf.conf", O_RDWR | O_CREAT );
flock ( $CSFCONF, LOCK_EX);
my @csf = <$CSFCONF>;
chomp @csf;
seek ( $CSFCONF, 0, 0 );
truncate ( $CSFCONF, 0 );
foreach my $line ( @csf )
{
if ( $line =~ /^PT_USERKILL /)
{
print $CSFCONF "PT_USERKILL = \"0\"\n";
print "*** PT_USERKILL disabled ***\n";
} else {
print $CSFCONF $line."\n";
}
}
close ( $CSFCONF );
print "</div>\n";
print "<div class='panel-footer panel-footer'>Completed</div>\n";
print "</div>\n";
print "<div>You MUST now restart both csf and lfd:</div>\n";
print "<div><form action='$script' method='post'><input type='hidden' name='action' value='restartboth'><input type='submit' class='btn btn-default' value='Restart csf+lfd'></form></div>\n";
&renderBtnReturn;
}
elsif ( $FORM{action} eq "fixsmtp" )
{
print "<div class='panel panel-default'>\n";
print "<div class='panel-heading panel-heading'>Disabling SMTP_BLOCK:</div>\n";
print "<div class='panel-body'>";
copy( "/etc/csf/csf.conf","/var/lib/csf/backup/".time."_prefixsmtp" );
sysopen ( my $CSFCONF,"/etc/csf/csf.conf", O_RDWR | O_CREAT );
flock ( $CSFCONF, LOCK_EX );
my @csf = <$CSFCONF>;
chomp @csf;
seek ( $CSFCONF, 0, 0 );
truncate ( $CSFCONF, 0 );
foreach my $line ( @csf )
{
if ( $line =~ /^SMTP_BLOCK / )
{
print $CSFCONF "SMTP_BLOCK = \"0\"\n";
print "*** SMTP_BLOCK disabled ***\n";
}
else
{
print $CSFCONF $line."\n";
}
}
close ( $CSFCONF );
print "</div>\n";
print "<div class='panel-footer panel-footer'>Completed</div>\n";
print "</div>\n";
print "<div>You MUST now restart both csf and lfd:</div>\n";
print "<div><form action='$script' method='post'><input type='hidden' name='action' value='restartboth'><input type='submit' class='btn btn-default' value='Restart csf+lfd'></form></div>\n";
&renderBtnReturn;
}
elsif ( $FORM{action} eq "fixalerts" )
{
print "<div class='panel panel-default'>\n";
print "<div class='panel-heading panel-heading'>Disabling All Alerts:</div>\n";
print "<div class='panel-body'>";
&resize("top");
print "<pre class='comment' style='white-space: pre-wrap; height: 500px; overflow: auto; resize:both; clear:both' id='output'>\n";
copy("/etc/csf/csf.conf","/var/lib/csf/backup/".time."_prefixalerts");
&printcmd("/usr/sbin/csf","--profile","apply","disable_alerts");
print "</pre>\n";
&resize("bot",1);
print "</div>\n";
print "<div class='panel-footer panel-footer'>Completed</div>\n";
print "</div>\n";
print "<div>You MUST now restart both csf and lfd:</div>\n";
print "<div><form action='$script' method='post'><input type='hidden' name='action' value='restartboth'><input type='submit' class='btn btn-default' value='Restart csf+lfd'></form></div>\n";
&renderBtnReturn;
}
elsif ( $FORM{action} eq "fixnuclear" )
{
print "<div class='panel panel-default'>\n";
print "<div class='panel-heading panel-heading'>Nuclear Option:</div>\n";
print "<div class='panel-body'>";
my $time = time;
sysopen ( my $REINSTALL, "/usr/src/reinstall_$time.sh", O_WRONLY | O_CREAT | O_TRUNC );
flock ( $REINSTALL, LOCK_EX );
print $REINSTALL <<EOF;
#!/usr/bin/bash
bash /etc/csf/uninstall.sh
cd /usr/src
mv -fv csf.tgz csf.tgz.$time
mv -fv csf csf.$time
wget https://$config{DOWNLOADSERVER}/csf.tgz
tar -xzf csf.tgz
cd csf
sh install.sh
EOF
close ( $REINSTALL );
&resize( "top" );
print "<pre class='comment' style='white-space: pre-wrap; height: 500px; overflow: auto; resize:both; clear:both' id='output'>\n";
&printcmd( "bash","/usr/src/reinstall_$time.sh" );
unlink "/usr/src/reinstall_$time.sh";
print "</pre>\n";
&resize( "bot", 1 );
print "</div>\n";
print "<div class='panel-footer panel-footer'>Completed</div>\n";
print "</div>\n";
&renderBtnReturn;
}
else
{
if ( defined $ENV{WEBMIN_VAR} and defined $ENV{WEBMIN_CONFIG} and -e "module.info" )
{
my @data = slurp("module.info");
foreach my $line ( @data )
{
if ( $line =~ /^name=csf$/)
{
unless (-l "index.cgi")
{
unlink "index.cgi";
my $status = symlink ("/usr/local/csf/lib/webmin/csf/index.cgi","index.cgi");
if ( $status and -l "index.cgi" )
{
symlink ("/usr/local/csf/lib/webmin/csf/images","csfimages");
print "<p><b>csf updated to symlink webmin module to /usr/local/csf/lib/webmin/csf/. Click <a href='index.cgi'>here</a> to continue<p></b>\n";
exit;
}
else
{
print "<p>Failed to symlink to /usr/local/csf/lib/webmin/csf/<p>\n";
}
}
last;
}
}
}
&getethdev;
my ( $childin, $childout);
my $pid = open3( $childin, $childout, $childout, "$config{IPTABLES} $config{IPTABLESWAIT} -L LOCALINPUT -n");
my @iptstatus = <$childout>;
waitpid ( $pid, 0 );
chomp @iptstatus;
if ( $iptstatus[0] =~ /# Warning: iptables-legacy tables present/ )
{
shift @iptstatus
}
my $status = "<div class='bs-callout bs-callout-success text-center'><h4>Firewall Status: Enabled and Running</h4></div>";
if ( -e "/etc/csf/csf.disable" )
{
$status = "<div class='bs-callout bs-callout-danger text-center'><form action='$script' method='post'><h4>Firewall Status: Disabled and Stopped <input type='hidden' name='action' value='enable'><input type='submit' class='btn btn-default' value='Enable'></form></h4></div>\n"
}
elsif ( $config{TESTING} )
{
$status = "<div class='bs-callout bs-callout-warning text-center'><form action='$script' method='post'><h4>Firewall Status: Enabled but in Test Mode - Don't forget to disable TESTING in the Firewall Configuration</h4></div>";
}
elsif ( $iptstatus[0] !~ /^Chain LOCALINPUT/ )
{
$status = "<div class='bs-callout bs-callout-danger text-center'><form action='$script' method='post'><h4>Firewall Status: Enabled but Stopped <input type='hidden' name='action' value='start'><input type='submit' class='btn btn-default' value='Start'></form></h4></div>"
}
if ( -e "/var/lib/csf/lfd.restart" )
{
$status .= "<div class='bs-callout bs-callout-info text-center'><h4>lfd restart request pending</h4></div>"
}
unless ( $config{RESTRICT_SYSLOG} )
{
$status .= "<div class='bs-callout bs-callout-warning text-center'><h4>WARNING: RESTRICT_SYSLOG is disabled. See SECURITY WARNING in Firewall Configuration</h4></div>\n"
}
my $tempcnt = 0;
if (! -z "/var/lib/csf/csf.tempban")
{
sysopen ( my $IN, "/var/lib/csf/csf.tempban", O_RDWR );
flock ( $IN, LOCK_EX );
my @data = <$IN>;
close ( $IN );
chomp @data;
$tempcnt = scalar @data;
}
my $tempbans = "(Currently: <code>$tempcnt</code> temp IP bans, ";
$tempcnt = 0;
if (! -z "/var/lib/csf/csf.tempallow")
{
sysopen (my $IN, "/var/lib/csf/csf.tempallow", O_RDWR);
flock ( $IN, LOCK_EX);
my @data = <$IN>;
close ( $IN);
chomp @data;
$tempcnt = scalar @data;
}
$tempbans .= "<code>$tempcnt</code> temp IP allows)";
my $permcnt = 0;
if (! -z "/etc/csf/csf.deny")
{
sysopen (my $IN, "/etc/csf/csf.deny", O_RDWR);
flock ( $IN, LOCK_SH);
while (my $line = <$IN>)
{
chomp $line;
if ( $line =~ /^(\#|\n|\r)/) {next}
if ( $line =~ /$ipv4reg|$ipv6reg/) {$permcnt++}
}
close ( $IN);
}
my $permbans = "(Currently: <code>$permcnt</code> permanent IP bans)";
$permcnt = 0;
if (! -z "/etc/csf/csf.allow")
{
sysopen (my $IN, "/etc/csf/csf.allow", O_RDWR);
flock ( $IN, LOCK_SH);
while (my $line = <$IN>)
{
chomp $line;
if ( $line =~ /^(\#|\n|\r)/)
{
next
}
if ( $line =~ /$ipv4reg|$ipv6reg/)
{
$permcnt++
}
}
close ( $IN);
}
my $permallows = "(Currently: <code>$permcnt</code> permanent IP allows)";
print $status;
print "<div class='normalcontainer'>\n";
print "<div class='bs-callout bs-callout-info text-center collapse' id='upgradebs'><h4>A new version of csf is <a href='#upgradetable'>available</a></h4></div>";
print "<ul class='nav nav-tabs' id='myTabs' style='font-weight:bold'>\n";
print "<li class='active'><a data-toggle='tab' href='#' id='tabAll'>All</a></li>\n";
print "<li><a data-toggle='tab' href='#home'>Info</a></li>\n";
print "<li><a data-toggle='tab' href='#csf'>CSF</a></li>\n";
print "<li><a data-toggle='tab' href='#lfd'>LFD</a></li>\n";
if ( $config{CLUSTER_SENDTO} )
{
print "<li><a data-toggle='tab' href='#cluster'>Cluster</a></li>\n";
}
print "<li><a data-toggle='tab' href='#other'>Tools</a></li>\n";
print "<li><a data-toggle='tab' href='#resources'>Resources</a></li>\n";
print "<li><a data-toggle='tab' href='#insiders'>Insiders</a></li>\n";
print "</ul><br>\n";
# #
# Tab > Parent
# #
print "<div class='tab-content'>\n";
# #
# Tab > Home
# #
print "<div id='home' class='tab-pane active'>\n";
print "<form action='$script' method='post'>\n";
print "<table class='table table-bordered table-striped'>\n";
print "<thead><tr><th colspan='2'><span>Server Information</span></th></tr></thead>";
print "<tr><td><button name='action' value='servercheck' type='submit' class='btn btn-default' style='width: 200px;'>Check Server Security</button></td><td style='width:100%'>Perform a basic security, stability and settings check on the server</td></tr>\n";
print "<tr><td><button name='action' value='readme' type='submit' class='btn btn-default' style='width: 200px;'>Firewall Information</button></td><td style='width:100%'>View the csf+lfd readme.txt file</td></tr>\n";
print "<tr><td><button name='action' value='logtail' type='submit' class='btn btn-default' style='width: 200px;'>Watch System Logs</button></td><td style='width:100%'>Watch (tail) various system log files (listed in csf.syslogs)</td></tr>\n";
print "<tr><td><button name='action' value='loggrep' type='submit' class='btn btn-default' style='width: 200px;'>Search System Logs</button></td><td style='width:100%'>Search (grep) various system log files (listed in csf.syslogs)</td></tr>\n";
print "<tr><td><button name='action' value='viewports' type='submit' class='btn btn-default' style='width: 200px;'>View Listening Ports</button></td><td style='width:100%'>View ports on the server that have a running process behind them listening for external connections</td></tr>\n";
print "<tr><td><button name='action' value='rblcheck' type='submit' class='btn btn-default' style='width: 200px;'>Check for IPs in RBLs</button></td><td style='width:100%'>Check whether any of the servers IP addresses are listed in RBLs</td></tr>\n";
if ( $config{ST_ENABLE} )
{
# Button › Statistics › Iptable Logs
print "<tr><td><button name='action' value='viewlogs' type='submit' class='btn btn-default' style='width: 200px;'"
. ($config{ST_ENABLE} ? '' : ' disabled="disabled"')
. ">View iptables Log</button></td><td style='width:100%'>"
. ($config{ST_ENABLE} ? '' : "<span class='glyphicon glyphicon-cog' style='font-size:1.3em;' data-tooltip='tooltip' title='Requires ST_ENABLE=\"1\"'></span> ")
. "<span>View the last <code>$config{ST_IPTABLES}</code> iptables log lines</span></td></tr>\n";
if ( $chart )
{
# Button › View lfd Statistics
print "<tr><td><button name='action' value='chart' type='submit' class='btn btn-default' style='width: 200px;'"
. ($config{ST_ENABLE} && $chart ? '' : ' disabled="disabled"')
. ">View lfd Statistics</button></td><td style='width:100%'>"
. ($config{ST_ENABLE} && $chart ? '' : "<span class='glyphicon glyphicon-cog' style='font-size:1.3em;' data-tooltip='tooltip' title='Requires Package GD:Graph\nRequires ST_ENABLE=\"1\"'></span> ")
. "<span>View lfd blocking statistics</span></td></tr>\n";
if ($config{ST_SYSTEM})
{
# Button › View basic system statistics
print "<tr><td><button name='action' value='systemstats' type='submit' class='btn btn-default' style='width: 200px;'"
. ($config{ST_ENABLE} && $chart && $config{ST_SYSTEM} ? '' : ' disabled="disabled"')
. ">View System Statistics</button></td><td style='width:100%'>"
. (($config{ST_ENABLE} && $chart && $config{ST_SYSTEM})
? ''
: "<span class='glyphicon glyphicon-cog' style='font-size:1.3em;' data-tooltip='tooltip' title='Requires Package GD:Graph\nRequires ST_ENABLE=\"1\"\nRequires ST_SYSTEM=\"1\"'></span> ")
. "<span>View basic system statistics</span></td></tr>\n";
}
}
}
print "</table>\n";
print "</form>\n";
print "<form action='$script' method='post'>\n";
print "<table class='table table-bordered table-striped' id='upgradetable'>\n";
print "<thead><tr><th colspan='2'><span>Upgrade</span></th></tr></thead>";
# #
# upgrade (int) 0, 1
# response (str) upgrade status or version
# #
my ( $upgrade, $response ) = &csgetversion( "csf", $myv );
if ( $upgrade )
{
print "<tr><td><button name='action' value='upgrade' type='submit' class='btn btn-default' style='width: 200px;'>Upgrade csf</button></td><td style='width:100%'><b>A new version of csf (v$response) is available. Upgrading will retain your settings<br><a href='https://$config{DOWNLOADSERVER}/csf/changelog.txt' target='_blank'>View ChangeLog</a></b></td></tr>\n";
}
else
{
print "<tr><td style='width: 200px;'><button name='action' value='manualcheck' type='submit' class='btn btn-default' style='width: 200px;'>Manual Check</button></td><td>";
if ( $response ne "" )
{
print "(csget cron check) $response</td></tr>\n";
}
else
{
print "You are running the latest version of csf. An Upgrade button will appear here if a new version becomes available. New version checking is performed automatically by a daily cron job (csget)</td></tr>\n";
}
}
if ( !$config{INTERWORX} and ( -e "/etc/apf" or -e "/usr/local/bfd" ) )
{
print "<tr><td><button name='action' value='remapf' type='submit' class='btn btn-default' style='width: 200px;'>Remove APF/BFD</button></td><td style='width:100%'>Remove APF/BFD from the server. You must not run both APF or BFD with csf on the same server</td></tr>\n";
}
print "</table>\n";
print "</form>\n";
if ( $upgrade )
{
print "<script>\$('\#upgradebs').show();</script>\n"
}
print "</div>\n";
# #
# Tab > csf
# #
__ "<div id='csf' class='tab-pane active'>\n";
__ "<table class='table table-bordered table-striped'>\n";
__ "<thead><tr><th colspan='2'><span>CSF - Quick Actions</span></th></tr></thead>";
__ "<tr><td><button onClick='\$(\"#qallow\").submit();' class='btn btn-default' style='width: 200px;'>Quick Allow</button></td><td style='width:100%'><form action='$script' method='post' id='qallow'><input type='submit' class='hide'><input type='hidden' name='action' value='qallow'>Allow IP address <a href='javascript:fillfield(\"allowip\",\"$ENV{REMOTE_ADDR}\")'><span class='glyphicon glyphicon-cog' style='font-size:1.3em;' data-tooltip='tooltip' title='$ENV{REMOTE_ADDR}'></span></a> <input class='input-allow' type='text' name='ip' id='allowip' value='' size='18'> through the firewall and add to the allow file (csf.allow).<br>Comment for Allow: <input type='text' name='comment' value='' size='30'></form></td></tr>\n";
__ "<tr><td><button onClick='\$(\"#qdeny\").submit();' class='btn btn-default' style='width: 200px;'>Quick Deny</button></td><td style='width:100%'><form action='$script' method='post' id='qdeny'><input type='submit' class='hide'><input type='hidden' name='action' value='qdeny'>Block IP address <input class='input-deny' type='text' name='ip' value='' size='18'> in the firewall and add to the deny file (csf.deny).<br>Comment for Block: <input type='text' name='comment' value='' size='30'></form></td></tr>\n";
__ "<tr><td><button onClick='\$(\"#qignore\").submit();' class='btn btn-default' style='width: 200px;'>Quick Ignore</button></td><td style='width:100%'><form action='$script' method='post' id='qignore'><input type='submit' class='hide'><input type='hidden' name='action' value='qignore'>Ignore IP address <a href='javascript:fillfield(\"ignoreip\",\"$ENV{REMOTE_ADDR}\")'><span class='glyphicon glyphicon-cog' style='font-size:1.3em;' data-tooltip='tooltip' title='$ENV{REMOTE_ADDR}'></span></a> <input type='text' name='ip' id='ignoreip' value='' size='18'> in lfd, add to the ignore file (csf.ignore) and restart lfd</form></td></tr>\n";
__ "<tr><td><button onClick='\$(\"#kill\").submit();' class='btn btn-default' style='width: 200px;'>Quick Unblock</button></td><td style='width:100%'><form action='$script' method='post' id='kill'><input type='submit' class='hide'><input type='hidden' name='action' value='kill'>Remove IP address <input type='text' name='ip' value='' size='18'> from the firewall (temp and perm blocks)</form></td></tr>\n";
__ "</table>\n";
__ "<table class='table table-bordered table-striped'>\n";
__ "<thead><tr><th colspan='2'><span>CSF - ConfigServer Firewall</span></th></tr></thead>";
# #
# Webmin
#
# Numerous users have wanted the return of the "Firewall Configuration" button.
#
# Webmin authentik-dark js looks for element "conf", takes the url from it, and adds a settings button at the top, and
# then hides the original button.
#
# This allows that process to take place, without modifying Webmin's theme by adding a second button since we have
# zero control over the theme.
# #
if ( $codename eq 'webmin' && $config{UI_WEBMIN_SHOW_BUTTON_CONFIG} == 1 )
{
__ "<tr><td><form action='$script' method='post'><button name='action' value='settings' type='submit' class='btn btn-default' style='width: 200px;'>Firewall Configuration</button></form></td><td style='width:100%'>Edit the configuration file for the csf firewall and lfd</td></tr>\n";
__ "<tr style='display:none;'><td><form action='$script' method='post'><button name='action' value='conf' type='submit' class='btn btn-default' style='width: 200px;'>Firewall Configuration</button></form></td><td style='width:100%'>Edit the configuration file for the csf firewall and lfd</td></tr>\n";
}
else
{
__ "<tr><td><form action='$script' method='post'><button name='action' value='conf' type='submit' class='btn btn-default' style='width: 200px;'>Firewall Configuration</button></form></td><td style='width:100%'>Edit the configuration file for the csf firewall and lfd</td></tr>\n";
}
# __ "<tr><td><form action='$script' method='post'><button name='action' value='" . ( $codename eq 'webmin' ? 'settings' : 'conf' ) . "' type='submit' class='btn btn-default' style='width: 200px;'>Firewall Configuration</button></form></td><td style='width:100%'>Edit the configuration file for the csf firewall and lfd</td></tr>\n";
__ "<tr><td><form action='$script' method='post'><button name='action' value='profiles' type='submit' class='btn btn-default' style='width: 200px;'>Firewall Profiles</button></form></td><td style='width:100%'>Apply pre-configured csf.conf profiles and backup/restore csf.conf</td></tr>\n";
__ "<tr><td><form action='$script' method='post'><button name='action' value='status' type='submit' class='btn btn-default' style='width: 200px;'>View iptables Rules</button></form></td><td style='width:100%'>Display the active iptables rules</td></tr>\n";
__ "<tr><td><button onClick='\$(\"#grep\").submit();' class='btn btn-default' style='width: 200px;'>Search for IP</button></td><td style='width:100%'><form action='$script' method='post' id='grep'><input type='submit' class='hide'><input type='hidden' name='action' value='grep'>Search iptables for IP address <input type='text' name='ip' value='' size='18'></form></td></tr>\n";
__ "<tr><td><form action='$script' method='post'><button name='action' value='allow' type='submit' class='btn btn-default' style='width: 200px;'>Firewall Allow IPs</button></form></td><td style='width:100%'>Edit csf.allow, the IP address allow file $permallows</td></tr>\n";
__ "<tr><td><form action='$script' method='post'><button name='action' value='deny' type='submit' class='btn btn-default' style='width: 200px;'>Firewall Deny IPs</button></form></td><td style='width:100%'>Edit csf.deny, the IP address deny file $permbans</td></tr>\n";
__ "<tr><td><form action='$script' method='post'><button name='action' value='enable' type='submit' class='btn btn-default' style='width: 200px;'>Firewall Enable</button></form></td><td style='width:100%'>Enables csf and lfd if previously Disabled</td></tr>\n";
__ "<tr><td><form action='$script' method='post'><button name='action' value='disable' type='submit' class='btn btn-default' style='width: 200px;'>Firewall Disable</button></form></td><td style='width:100%'>Completely disables csf and lfd</td></tr>\n";
__ "<tr><td><form action='$script' method='post'><button name='action' value='restart' type='submit' class='btn btn-default' style='width: 200px;'>Firewall Restart</button></form></td><td style='width:100%'>Restart the csf iptables firewall</td></tr>\n";
__ "<tr><td><form action='$script' method='post'><button name='action' value='restartq' type='submit' class='btn btn-default' style='width: 200px;'>Firewall Quick Restart</button></form></td><td style='width:100%'>Have lfd restart the csf iptables firewall</td></tr>\n";
__ "<tr><td><button onClick='\$(\"#tempdeny\").submit();' class='btn btn-default' style='width: 200px;'>Temporary Allow/Deny</button></td><td style='width:100%'><form action='$script' method='post' id='tempdeny'><input type='submit' class='hide'><input type='hidden' name='action' value='tempdeny'>Temporarily <select name='do'><option>block</option><option>allow</option></select> IP address <input type='text' name='ip' value='' size='18'> to port(s) <input type='text' name='ports' value='*' size='5'> for <input type='text' name='timeout' value='' size='4'> <select name='dur'><option>seconds</option><option>minutes</option><option>hours</option><option>days</option></select>.<br>Comment: <input type='text' name='comment' value='' size='30'><br>\n(ports can be either * for all ports, a single port, or a comma separated list of ports)</form></td></tr>\n";
__ "<tr><td><form action='$script' method='post'><button name='action' value='temp' type='submit' class='btn btn-default' style='width: 200px;'>Temporary IP Entries</button></form></td><td style='width:100%'>View/Remove the <i>temporary</i> IP entries $tempbans</td></tr>\n";
__ "<tr><td><form action='$script' method='post'><button name='action' value='sips' type='submit' class='btn btn-default' style='width: 200px;'>Deny Server IPs</button></form></td><td style='width:100%'>Deny access to and from specific IP addresses configured on the server (csf.sips)</td></tr>\n";
__ "<tr><td><form action='$script' method='post'><button name='action' value='denyf' type='submit' class='btn btn-default' style='width: 200px;'>Flush all Blocks</button></form></td><td style='width:100%'>Removes and unblocks all entries in csf.deny (excluding those marked \"do not delete\") and all temporary IP entries (blocks <i>and</i> allows)</td></tr>\n";
__ "<tr><td><form action='$script' method='post'><button name='action' value='redirect' type='submit' class='btn btn-default' style='width: 200px;'>Firewall Redirect</button></form></td><td style='width:100%'>Redirect connections to this server to other ports/IP addresses</td></tr>\n";
__ "<tr><td><form action='$script' method='post'><button name='action' value='fix' type='submit' class='btn btn-default' style='width: 200px;'>Fix Common Problems</button></form></td><td style='width:100%'>Offers solutions to some common problems when using an SPI firewall</td></tr>\n";
__ "</table>\n";
__ "<script>function fillfield (myitem,myip) {document.getElementById(myitem).value = myip;}</script>\n";
__ "</div> <!-- end id='csf' -->\n"; # end TAB id='csf'
# #
# Tab > lfd
# #
__ "<div id='lfd' class='tab-pane active'>\n";
__ "<table class='table table-bordered table-striped'>\n";
__ "<thead><tr><th colspan='2'><span>LFD - Login Failure Daemon</span></th></tr></thead>";
__ "<tr><td><form action='$script' method='post'><input type='hidden' name='action' value='lfdstatus'><input type='submit' class='btn btn-default' style='width: 200px;' value='lfd Status'></form></td><td style='width:100%'>Display lfd status</td></tr>\n";
__ "<tr><td><form action='$script' method='post'><input type='hidden' name='action' value='lfdrestart'><input type='submit' class='btn btn-default' style='width: 200px;' value='lfd Restart'></form></td><td style='width:100%'>Restart lfd</td></tr>\n";
__ "<tr><td style='white-space: nowrap;'><form action='$script' method='post'><input type='hidden' name='action' value='ignorefiles'><select name='ignorefile'>\n";
__ "<option value='csf.ignore'>csf.ignore - IP Blocking</option>\n";
__ "<option value='csf.pignore'>csf.pignore, Process Tracking</option>\n";
__ "<option value='csf.fignore'>csf.fignore, Directory Watching</option>\n";
__ "<option value='csf.signore'>csf.signore, Script Alert</option>\n";
__ "<option value='csf.rignore'>csf.rignore, Reverse DNS lookup</option>\n";
__ "<option value='csf.suignore'>csf.suignore, Superuser check</option>\n";
__ "<option value='csf.mignore'>csf.mignore, RT_LOCALRELAY</option>\n";
__ "<option value='csf.logignore'>csf.logignore, Log Scanner</option>\n";
__ "<option value='csf.uidignore'>csf.uidignore, User ID Tracking</option>\n";
__ "</select> <input type='submit' class='btn btn-default' value='Edit'></form></td><td style='width:100%'>Edit lfd ignore file</td></tr>\n";
__ "<tr><td><form action='$script' method='post'><button name='action' value='dirwatch' type='submit' class='btn btn-default' style='width: 200px;'>lfd Directory File Watching</button></form></td><td style='width:100%'>Edit the Directory File Watching file (csf.dirwatch) - all listed files and directories will be watched for changes by lfd</td></tr>\n";
__ "<tr><td><form action='$script' method='post'><button name='action' value='dyndns' type='submit' class='btn btn-default' style='width: 200px;'>lfd Dynamic DNS</button></form></td><td style='width:100%'>Edit the Dynamic DNS file (csf.dyndns) - all listed domains will be resolved and allowed through the firewall</td></tr>\n";
__ "<tr><td><form action='$script' method='post'><select name='template'>\n";
foreach my $tmp ("alert.txt","tracking.txt","connectiontracking.txt","processtracking.txt","accounttracking.txt","usertracking.txt","sshalert.txt","webminalert.txt","sualert.txt","sudoalert.txt","uialert.txt","cpanelalert.txt","scriptalert.txt","filealert.txt","watchalert.txt","loadalert.txt","resalert.txt","integrityalert.txt","exploitalert.txt","relayalert.txt","portscan.txt","uidscan.txt","permblock.txt","netblock.txt","queuealert.txt","logfloodalert.txt","logalert.txt","modsecipdbcheck.txt") {print "<option>$tmp</option>\n"}
__ "</select> <button name='action' value='templates' type='submit' class='btn btn-default'>Edit</button></form></td><td style='width:100%'>Edit email alert templates. See Firewall Information for details of each file</td></tr>\n";
__ "<tr><td><form action='$script' method='post'><button name='action' value='logfiles' type='submit' class='btn btn-default' style='width: 200px;'>lfd Log Scanner Files</button></form></td><td style='width:100%'>Edit the Log Scanner file (csf.logfiles) - Scan listed log files for log lines and periodically send a report</td></tr>\n";
__ "<tr><td><form action='$script' method='post'><button name='action' value='blocklists' type='submit' class='btn btn-default' style='width: 200px;'>lfd Blocklists</button></form></td><td style='width:100%'>Edit the Blocklists configuration file (csf.blocklists)</td></tr>\n";
__ "<tr><td><form action='$script' method='post'><button name='action' value='syslogusers' type='submit' class='btn btn-default' style='width: 200px;'>lfd Syslog Users</button></form></td><td style='width:100%'>Edit the syslog/rsyslog allowed users file (csf.syslogusers)</td></tr>\n";
__ "</table>\n";
__ "</div> <!-- end id='lfd' -->\n"; # end TAB id='lfd'
# #
# Cluster
# #
if ( $config{CLUSTER_SENDTO} )
{
__ "<div id='cluster' class='tab-pane active'>\n";
__ "<table class='table table-bordered table-striped'>\n";
__ "<thead><tr><th colspan='2'><span>CSF - LFD Cluster</span></th></tr></thead>";
__ "<tr><td><form action='$script' method='post'><button name='action' value='cping' type='submit' class='btn btn-default' style='width: 200px;'>Cluster PING</button></form></td><td style='width:100%'>Ping each member of the cluster (logged in lfd.log)</td></tr>\n";
__ "<tr><td><button onClick='\$(\"#callow\").submit();' class='btn btn-default' style='width: 200px;'>Cluster Allow</button></td><td style='width:100%'><form action='$script' method='post' id='callow'><input type='submit' class='hide'><input type='hidden' name='action' value='callow'>Allow IP address <input type='text' name='ip' value='' size='18' class='input-allow'> through the Cluster and add to the allow file (csf.allow)<br>Comment: <input type='text' name='comment' value='' size='30'></form></td></tr>\n";
__ "<tr><td><button onClick='\$(\"#cdeny\").submit();' class='btn btn-default' style='width: 200px;'>Cluster Deny</button></td><td style='width:100%'><form action='$script' method='post' id='cdeny'><input type='submit' class='hide'><input type='hidden' name='action' value='cdeny'>Block IP address <input type='text' name='ip' value='' size='18' class='input-deny'> in the Cluster and add to the deny file (csf.deny)<br>Comment: <input type='text' name='comment' value='' size='30'></form></td></tr>\n";
__ "<tr><td><button onClick='\$(\"#cignore\").submit();' class='btn btn-default' style='width: 200px;'>Cluster Ignore</button></td><td style='width:100%'><form action='$script' method='post' id='cignore'><input type='submit' class='hide'><input type='hidden' name='action' value='cignore'>Ignore IP address <input type='text' name='ip' value='' size='18'> in the Cluster and add to the ignore file (csf.ignore)<br>Comment: <input type='text' name='comment' value='' size='30'> Note: This will result in lfd being restarted</form></td></tr>\n";
__ "<tr><td><button onClick='\$(\"#cgrep\").submit();' class='btn btn-default' style='width: 200px;'>Search the Cluster for IP</button></td><td style='width:100%'><form action='$script' method='post' id='cgrep'><input type='submit' class='hide'><input type='hidden' name='action' value='cgrep'>Search iptables for IP address <input type='text' name='ip' value='' size='18'></form></td></tr>\n";
__ "<tr><td><button onClick='\$(\"#ctempdeny\").submit();' class='btn btn-default' style='width: 200px;'>Cluster Temp Allow/Deny</button></td><td style='width:100%'><form action='$script' method='post' id='ctempdeny'><input type='submit' class='hide'><input type='hidden' name='action' value='ctempdeny'>Temporarily <select name='do'><option>block</option><option>allow</option></select> IP address <input type='text' name='ip' value='' size='18'> to port(s) <input type='text' name='ports' value='*' size='5'> for <input type='text' name='timeout' value='' size='4'> <select name='dur'><option>seconds</option><option>minutes</option><option>hours</option><option>days</option></select>.<br>Comment: <input type='text' name='comment' value='' size='30'><br>\n(ports can be either * for all ports, a single port, or a comma separated list of ports)</form></td></tr>\n";
__ "<tr><td><button onClick='\$(\"#crm\").submit();' class='btn btn-default' style='width: 200px;'>Cluster Remove Deny</button></td><td style='width:100%'><form action='$script' method='post' id='crm'><input type='submit' class='hide'><input type='hidden' name='action' value='crm'>Remove Deny IP address <input type='text' name='ip' value='' size='18' style=''> in the Cluster (temporary or permanent)</form></td></tr>\n";
__ "<tr><td><button onClick='\$(\"#carm\").submit();' class='btn btn-default' style='width: 200px;'>Cluster Remove Allow</button></td><td style='width:100%'><form action='$script' method='post' id='carm'><input type='submit' class='hide'><input type='hidden' name='action' value='carm'>Remove Allow IP address <input type='text' name='ip' value='' size='18' style=''> in the Cluster (temporary or permanent)</form></td></tr>\n";
__ "<tr><td><button onClick='\$(\"#cirm\").submit();' class='btn btn-default' style='width: 200px;'>Cluster Remove Ignore</button></td><td style='width:100%'><form action='$script' method='post' id='cirm'><input type='submit' class='hide'><input type='hidden' name='action' value='cirm'>Remove Ignore IP address <input type='text' name='ip' value='' size='18'> in the Cluster<br>Note: This will result in lfd being restarted</form></td></tr>\n";
if ( $config{CLUSTER_CONFIG} )
{
if ( $ips{$config{CLUSTER_MASTER}} or $ipscidr6->find( $config{CLUSTER_MASTER} ) or ( $config{CLUSTER_MASTER} eq $config{CLUSTER_NAT} ) )
{
my $options;
my %restricted;
if ( $config{RESTRICT_UI} )
{
sysopen ( my $IN, "/usr/local/csf/lib/restricted.txt", O_RDWR | O_CREAT ) or die "Unable to open file: $!";
flock ( $IN, LOCK_SH );
while ( my $entry = <$IN> )
{
chomp $entry;
$restricted{$entry} = 1;
}
close ( $IN );
}
foreach my $key ( sort keys %config )
{
unless ( $restricted{$key} ) { $options .= "<option>$key</option>" }
}
__ "<tr><td><button onClick='\$(\"#cconfig\").submit();' class='btn btn-default' style='width: 200px;'>Cluster Config</button></td><td style='width:100%'><form action='$script' method='post' id='cconfig'><input type='submit' class='hide'><input type='hidden' name='action' value='cconfig'>Change configuration option <select name='option'>$options</select> to <input type='text' name='value' value='' size='18'> in the Cluster";
if ( $config{RESTRICT_UI} )
{
__ "<br />\nSome items have been removed with RESTRICT_UI enabled"
}
__ "</form></td></tr>\n";
__ "<tr><td><form action='$script' method='post'><button name='action' value='crestart' type='submit' class='btn btn-default' style='width: 200px;'>Cluster Restart</button></form></td><td style='width:100%'>Restart csf and lfd on Cluster members</td></tr>\n";
}
}
__ "</table>\n";
__ "</div>\n";
}
# #
# Tab > Other
# #
__ "<div id='other' class='tab-pane active'>\n";
if ( $config{CF_ENABLE} )
{
__ "<table class='table table-bordered table-striped'>\n";
__ "<thead><tr><th colspan='2'><span>Cloudflare Integration</span></th></tr></thead>";
# Button › Cloudflare › Configure
__ "<tr><td><form action='$script' method='post'><button name='action' value='cloudflareedit' type='submit' class='btn btn-default' style='width: 200px;'"
. ($config{CF_ENABLE} ? '' : ' disabled="disabled"')
. ">Configure</button></td><td style='width:100%'>"
. ($config{CF_ENABLE} ? '' : "<span class='glyphicon glyphicon-cog' style='font-size:1.3em;' data-tooltip='tooltip' title='Requires CF_ENABLE=\"1\"'></span> ")
. "<span>Modify the CSF Cloudflare config file <code>csf.cloudflare</code>. Must configure this to populate user list.</span></td></tr>\n";
# Button › Cloudflare › Rules
__ "<tr><td><form action='$script' method='post'><button name='action' value='cloudflare' type='submit' class='btn btn-default' style='width: 200px;'"
. ($config{CF_ENABLE} ? '' : ' disabled="disabled"')
. ">User IP Rules</button></td><td style='width:100%'>"
. ($config{CF_ENABLE} ? '' : "<span class='glyphicon glyphicon-cog' style='font-size:1.3em;' data-tooltip='tooltip' title='Requires CF_ENABLE=\"1\"'></span> ")
. "<span>Setup associated users and IP rules</span></td></tr>\n";
__ "</table>\n";
}
if ( $config{SMTPAUTH_RESTRICT} )
{
__ "<table class='table table-bordered table-striped'>\n";
__ "<thead><tr><th colspan='2'><span>cPanel SMTP AUTH Restrictions</span></th></tr></thead>";
__ "<tr><td><form action='$script' method='post'><button name='action' value='smtpauth' type='submit' class='btn btn-default' style='width: 200px;'>Edit SMTP AUTH</button></form></td><td style='width:100%'>Edit the file that allows SMTP AUTH to be advertised to listed IP addresses (csf.smtpauth)</td></tr>\n";
__ "</table>\n";
}
if ( -e "/usr/local/cpanel/version" or $config{DIRECTADMIN} or $config{INTERWORX} )
{
my $resellers = "cPanel Resellers";
if ( $config{DIRECTADMIN} )
{
$resellers = "DirectAdmin Resellers"
}
elsif ( $config{INTERWORX} )
{
$resellers = "InterWorx Resellers"
}
__ "<table class='table table-bordered table-striped'>\n";
__ "<thead><tr><th colspan='2'><span>$resellers</span></th></tr></thead>";
__ "<tr><td><form action='$script' method='post'><button name='action' value='reseller' type='submit' class='btn btn-default' style='width: 200px;'>Edit Reseller Privs</button></form></td><td style='width:100%'>Privileges can be assigned to $resellers accounts by editing this file (csf.resellers)</td></tr>\n";
__ "</table>\n";
}
__ "<table class='table table-bordered table-striped'>\n";
__ "<thead><tr><th colspan='2'><span>Tools</span></th></tr></thead>";
__ "<tr><td><form action='$script' method='post'><button name='action' value='csftest' type='submit' class='btn btn-default' style='width: 200px;'>Test iptables</button></form></td><td style='width:100%'>Check that iptables has the required modules to run csf</td></tr>\n";
__ "</table>\n";
__ "</div> <!-- end id='other' -->\n"; # end TAB id='other'
# #
# Tab > Resources
# #
__ "<div id='resources' class='tab-pane active'>\n";
__ "<form action='$script' method='post'>\n";
__ "<table class='table table-bordered table-striped'>\n";
__ "<thead><tr><th colspan='2'>Resources</th></tr></thead>";
__ "<tr><td><a href='https://github.com/aetherinox/csf-firewall' target='_blank' class='btn btn-default' style='width: 200px;'>Repository</a></td><td style='width:100%'>View our source code; submit issues to the developers</td></tr>\n";
__ "<tr><td><a href='https://docs.configserver.dev' target='_blank' class='btn btn-default' style='width: 200px;'>Documentation</a></td><td style='width:100%'>View the CSF documentation</td></tr>\n";
__ "<tr><td><a href='https://download.configserver.dev' target='_blank' class='btn btn-default' style='width: 200px;'>Download</a></td><td style='width:100%'>Manually download the latest version of CSF</td></tr>\n";
__ "<tr><td><a href='https://blocklist.configserver.dev/help' target='_blank' class='btn btn-default' style='width: 200px;'>Blocklist API</a></td><td style='width:100%'>Utilize our blocklist service with <code>csf.blocklists</code> to restrict malicious traffic and bad actors</td></tr>\n";
__ "<tr><td><a href='https://discord.configserver.dev' target='_blank' class='btn btn-default' style='width: 200px;'>Discord</a></td><td style='width:100%'>Speak to others in the community, ask questions, share setups</td></tr>\n";
__ "<tr><td><a href='https://docs.configserver.dev/insiders' target='_blank' class='btn btn-default' style='width: 200px;'>Insiders</a></td><td style='width:100%'>Learn more about our insiders program</td></tr>\n";
__ "</table>\n";
__ "</form>\n";
__ "</div> <!-- end id='insiders' -->\n"; # end TAB id='insiders'
# #
# Tab > Insiders
# #
__ "<div id='insiders' class='tab-pane active'>\n";
__ "<div id='sponsor' class='tab-pane'>\n";
__ "<div class='panel panel-default'>\n";
__ "<div class='panel-heading text-center'><h4 style='height: 12px;line-height: 12px;'><span>Sponsorship & Insiders Program</span></h4></div>\n";
if ( !$config{SPONSOR_LICENSE} )
{
__ "<div class='panel-body'>\n";
__ "CSF will always be <b>free</b>. If you wish to donate / contribute to CSF, you will receive the following benefits:\n";
__ "<ul style='text-align:left;'>\n";
__ "<li style='font-size:9pt;'><a target='_blank' href='https://docs.configserver.dev/insiders'>License key for <a target='_blank' href='https://docs.configserver.dev/insiders'>Insiders access</a></b> and our <a target='_blank' href='https://blocklist.configserver.dev/help'>Blocklist Service</a> API</li>\n";
__ "<li style='font-size:9pt;'><a target='_blank' href='https://docs.configserver.dev/insiders'>Insiders access</a> to early releases of CSF before the public.</b></li>\n";
__ "<li style='font-size:9pt;'>Access to our <a target='_blank' href='https://blocklist.configserver.dev/help'>Blocklist Service</a> API with features such as <code>?include</code>, <code>?excldue</code>, <code>?limit</code>. (Over 1 million blocked bad-actor IPs)</li>\n";
__ "<li style='font-size:9pt;'><a target='_blank' href='https://discord.configserver.dev'>Discord</a> Sponsor role.</li>\n";
__ "<li style='font-size:9pt;'>This message and sponsor icon will disappear in CSF Interface.</li>\n";
__ "<li style='font-size:9pt;'>Added to our <a target='_blank' href='https://docs.configserver.dev/insiders/sponsors/'>Sponsor</a> membership page.</li>\n";
__ "<li style='font-size:9pt;'>Recommend perks to us (they can't block access to any CSF features).</li>\n";
__ "</ul>\n";
__ "</div>\n";
}
__ "<form action='$script' method='post'>\n";
__ "<table class='table table-bordered table-striped' style='margin-bottom:0px;'>\n";
if ( !$config{SPONSOR_LICENSE} )
{
__ "<tr><td><a target='_blank' href='https://buymeacoffee.com/aetherinox' class='btn btn-default' style='width: 200px;'>Contribute</a></td><td style='width:100%'><div id='license-status'>Checking license ... </div></td></tr>\n";
}
else
{
__ "<tr><td><a target='_blank' href='https://docs.configserver.dev/insiders/sponsors/' class='btn btn-default' style='width: 200px;'>Sponsor Status</a></td><td style='width:100%'><div id='license-status'>Checking license ... </div></td></tr>\n";
}
__ "<tr><td><a target='_blank' href='https://docs.configserver.dev/insiders/about' class='btn btn-default' style='width: 200px;'>Insiders Channel</a></td><td style='width:100%'><div id='insiders-status'>Checking status ... </div></td></tr>\n";
__ "</table>\n";
__ "</form>\n";
__ "</div>\n";
__ "</div>\n";
__ "</div> <!-- end id='insiders' -->\n"; # end TAB id='insiders'
__ "</div><!-- end class='tab-content' -->\n"; # end TAB class='tab-content'
# #
# Sponsorship License
#
# Fetch the current status of a user's license.
#
# @todo Migrate to dedicated lib; json perl dependency
# @note removes all sponsorship text from interface once a key is provided.
# #
my ( $licenseValid, $licenseMsg ) = ConfigServer::Config->getLicenseStatus();
my $licenseObj =
{
licenseValid => $licenseValid ? \1 : \0,
insidersFlag => ( $config{SPONSOR_RELEASE_INSIDERS} // 0 ) ? \1 : \0,
licenseMsg => $licenseMsg // '',
};
my $licenseJson = encode_json( $licenseObj );
print <<END_JS;
<script>
( function( )
{
// Redundancy
function reqPrepare( str )
{
return String( str ).replace( /[&<>"']/g, m => (
{
'&':'&', '<':'<', '>':'>', '"':'"', "'":''',
'/':'/', '=':'=', '`':'`'
})[m]);
}
const data = $licenseJson;
const licenseDiv = document.getElementById( 'license-status' );
licenseDiv.textContent = '';
if ( data.licenseValid )
{
const msg = document.createElement( 'div' );
msg.textContent = '✅ You are a sponsor';
licenseDiv.appendChild( msg );
}
else
{
const msg = document.createElement( 'div' );
msg.textContent = '❌ No sponsorship: ' + reqPrepare( data.licenseMsg );
licenseDiv.appendChild( msg );
}
const insidersDiv = document.getElementById( 'insiders-status' );
insidersDiv.textContent = ( data.licenseValid && data.insidersFlag ) ? '✅ Enabled' : '❌ Disabled';
})();
</script>
END_JS
# #
# Mobile
# #
if ( $config{STYLE_MOBILE} )
{
if ( -e "/usr/local/cpanel/version" and !$config{THIS_UI} )
{
require Cpanel::Version::Tiny;
if ( $Cpanel::Version::Tiny::major_version < 65 )
{
__ "<a id='cpframetr2' href='$ENV{cp_security_token}' class='btn btn-success' data-spy='affix' data-offset-bottom='0' style='bottom: 0; left:45%'><span class='glyphicon glyphicon-home'></span> cPanel Main Page</a>\n";
}
}
if ( defined $ENV{WEBMIN_VAR} and defined $ENV{WEBMIN_CONFIG} and !$config{THIS_UI} )
{
print "<a id='webmintr2' href='/' class='btn btn-success' data-spy='affix' data-offset-bottom='0' style='bottom: 0; left:45%'><span class='glyphicon glyphicon-home'></span> Webmin Main Page</a>\n";
}
print "<div class='panel panel-default'><div class='panel-heading panel-heading-cxs'>Shows a subset of functions suitable for viewing on mobile devices</div>\n";
print "<div class='panel-body text-center'><a class='btn btn-primary btn-block' style='margin:10px;padding: 18px 28px;font-size: 22px; line-height: normal;border-radius: 8px;' id='MobileView'>Mobile View</a></div></div>\n";
print "</div>\n<div class='mobilecontainer'>\n";
print "<form action='$script' method='post'>\n";
print "<div class='form-group' style='width:100%'>\n";
print "<p><label>IP address:</label><input id='ip' type='text' class='form-control' name='ip'></p>\n";
print "<p><button class='btn btn-primary btn-lg btn-block' type='submit' name='action' value='qallow'>Quick Allow IP</button></p>\n";
print "<p><button class='btn btn-primary btn-lg btn-block' type='submit' name='action' value='qdeny'>Quick Deny IP</button></p>\n";
print "<p><button class='btn btn-primary btn-lg btn-block' type='submit' name='action' value='qignore'>Quick Ignore IP</button></p>\n";
print "<p><button class='btn btn-primary btn-lg btn-block' type='submit' name='action' value='kill'>Quick Unblock IP</button></p>\n";
print "<p><button class='btn btn-primary btn-lg btn-block' type='submit' name='action' value='grep'>Search for IP</button></p>\n";
print "</div>\n";
print "<br><div class='form-group'>\n";
print "<p><button class='btn btn-success btn-lg btn-block' type='submit' name='action' value='enable'>Firewall Enable</button></p>\n";
print "<p><button class='btn btn-danger btn-lg btn-block' type='submit' name='action' value='disable'>Firewall Disable</button></p>\n";
print "<p><br></p>\n";
print "<p><button class='btn btn-warning btn-lg btn-block' type='submit' name='action' value='restart'>Firewall Restart</button></p>\n";
print "<p><button class='btn btn-primary btn-lg btn-block' type='submit' name='action' value='denyf'>Flush all Blocks</button></p>\n";
print "</div>\n";
print "</form>\n";
if ( -e "/usr/local/cpanel/version" and !$config{THIS_UI} )
{
if ( $Cpanel::Version::Tiny::major_version < 65 )
{
print "<br><p><a href='$ENV{cp_security_token}' class='btn btn-info btn-lg btn-block'><span class='glyphicon glyphicon-home'></span> cPanel Main Page</a></p>\n";
}
}
if (defined $ENV{WEBMIN_VAR} and defined $ENV{WEBMIN_CONFIG} and !$config{THIS_UI})
{
print "<br><p><a href='/' class='btn btn-info btn-lg btn-block'><span class='glyphicon glyphicon-home'></span> Webmin Main Page</a></p>\n";
}
print "<br><p><button class='btn btn-info btn-lg btn-block' id='NormalView'>Desktop View</button></p>\n";
print "</div>\n<div><br>\n";
}
if ( $codename eq "webmin" )
{
print "<div class='panel panel-info'>\n";
print "<div class='panel-heading'>Development Contribution</div>";
print "<div class='panel-body'>We are very happy to be able to provide this and other products for free. However, it does take time for us to develop and maintain them. If you would like to help with their development by providing a contribution, please <a href='https://buymeacoffee.com/aetherinox'>visit here</a> for details</div>\n";
print "</div>\n";
}
}
unless ($FORM{action} eq "tailcmd" or $FORM{action} =~ /^cf/ or $FORM{action} eq "logtailcmd" or $FORM{action} eq "loggrepcmd")
{
# #
# @notes generic loads theme switch, css, footer
# webmin does not load configserver.css; uses its own built-in theme and bootstramp
# cwp loads theme switch, css, footer
# directadmin loads theme switch, css, footer. (unless noscript used)
# cpanel loads theme switch, css, footer
# VestaCP loads theme switch, css, footer
# cyberpanel loads theme switch, css, footer
# #
if ( $codename eq "generic" || $codename eq "cyberpanel" || $codename eq "cwp" || $codename eq "directadmin" || $codename eq "cpanel" || $codename eq "interworx" || $codename eq "vestacp" )
{
print "<div class='footer'>";
print "<div class='footer-left'>";
my $has_license = length( $config{SPONSOR_LICENSE} // '' );
my $hide_icon = $config{SPONSOR_ICON_HIDE} // '0';
print "<button id='btn-sponsor' class='btn-footer btn-sponsor'><i class='axs ax-heart"
. ( ( $config{SPONSOR_ICON_ANIM} // '' ) eq '1' ? " heart" : "" )
. "'></i></button>"
if !$has_license || $hide_icon ne '1';
print "<button id='btn-blocklist' class='btn-footer btn-blocklist'><i class='axs ax-block-brick-fire'></i></button>";
print "<button id='btn-status' class='btn-footer btn-status'><i class='axs ax-wave-pulse'></i></button>";
print "<button id='btn-repo' class='btn-footer btn-repo'><i class='axb ax-github'></i></button>";
print "<span class='copyright'>© $year ConfigServer Firewall - <code>v$myv ($codename)</code></span>";
print "</div>";
print "<div class='footer-right'>";
print "<button id='btn-theme' class='btn-footer btn-theme'>Switch Theme</button>";
# #
# Only show logout button for generic installs; otherwise let the control panel handle it.
# #
if ( $codename eq "generic" )
{
print "<button id='btn-logout' class='btn-footer btn-logout' title='Logout'><i class='axs ax-lock'></i></button>";
}
print "</div>";
print "</div>";
}
else
{
print "<br>\n";
print "<div class='well well-sm'>csf: v$myv</div>";
print "<p>© 2006-$year <a href='https://github.com/Aetherinox/csf-firewall' target='_blank'>ConfigServer Security & Firewall</a> - <code>v$myv ($codename)</code></p>\n";
print "</div>\n";
}
if ( $codename ne "cwp" )
{
print "<div style='padding-bottom: 50px;'></div>";
}
}
return;
}
# #
# Execute Command › Stream Output
#
# Runs an external command or script using open3(),
# captures both STDOUT and STDERR, and printing
# all output in real time to the console or UI.
#
# Halts further script execution until the external
# process has completed.
#
# Accepts as varArg
#
# @args varArgs
# @usage &printcmd( '/usr/sbin/csf','--profile','apply','disable_alerts');
# /usr/sbin/csf --profile apply disable_alerts
# #
sub printcmd
{
my @command = @_;
my $text;
my ( $childin, $childout );
my $pid = open3( $childin, $childout, $childout, @command );
while ( <$childout> )
{
$_ = strip_ansi( $_ );
print html_escape( $_ );
$text .= $_;
}
waitpid ( $pid, 0 );
return $text;
}
# #
# Network Interfaces › Gather Local IPs
#
# Retrieves all IPv4 and (optionally) IPv6 addresses
# assigned to the server’s network interfaces using
# ConfigServer::GetEthDev.
#
# Each IPv4 address is stored in %ips.
#
# IPv6 support is enabled ($config{IPV6}), each IPv6
# address is added to the $ipscidr6 object.
#
# Called as &getethdev; during initialization.
#
# @args null
# @usage &getethdev;
# #
sub getethdev
{
my $ethdev = ConfigServer::GetEthDev->new( );
my %g_ipv4 = $ethdev->ipv4;
my %g_ipv6 = $ethdev->ipv6;
foreach my $key ( keys %g_ipv4 )
{
$ips{$key} = 1;
}
if ( $config{IPV6} )
{
foreach my $key (keys %g_ipv6)
{
eval
{
local $SIG{__DIE__} = undef;
$ipscidr6->add($key);
};
}
}
return;
}
# #
# Chart › Generate Server Statistics Graphs
#
# Reads and processes CSF/LFD statistical data from
# › `/var/lib/csf/stats/lfdstats`
#
# Converts it into graphical charts using ConfigServer::ServerStats.
#
# Automatically determines the correct image directories
# and permissions based on the detected control panel
# environment (e.g., cPanel, DirectAdmin, InterWorx,
# CyberPanel, or CSF web interface mode).
#
# @args: null
# @usage &chart;
# @notes › Detects env and sets image paths accordingly
# › Reads / migrates legacy stat files if needed
# › Calls ConfigServer::ServerStats::charts() to build charts
# › Prints generated chart HTML to STDOUT
# › Displays “no data” message if stats unavailable
# #
sub chart
{
my $img;
my $imgdir = "";
my $imghddir = "";
if ( -e "/usr/local/cpanel/version" )
{
$imgdir = "/";
$imghddir = "";
}
elsif ( -e "/usr/local/directadmin/conf/directadmin.conf" )
{
$imgdir = "/CMD_PLUGINS_ADMIN/csf/images/";
$imghddir = "plugins/csf/images/";
umask(0133);
}
elsif ( -e "/usr/local/interworx" )
{
$imgdir = "/configserver/csf/";
$imghddir = "/usr/local/interworx/html/configserver/csf/";
umask(0133);
}
elsif ( -e "/usr/local/CyberCP/" )
{
$imgdir = "/static/configservercsf/";
$imghddir = "/usr/local/CyberCP/public/static/configservercsf/";
umask(0133);
}
if ( $config{THIS_UI} )
{
$imgdir = "$images/";
$imghddir = "/etc/csf/ui/images/";
}
my $STATS;
if (-e "/var/lib/csf/stats/lfdstats")
{
sysopen ( $STATS,"/var/lib/csf/stats/lfdstats", O_RDWR | O_CREAT );
}
elsif (-e "/var/lib/csf/stats/lfdmain")
{
sysopen ( my $OLDSTATS,"/var/lib/csf/stats/lfdmain", O_RDWR | O_CREAT );
flock ($OLDSTATS, LOCK_EX);
my @stats = <$OLDSTATS>;
chomp @stats;
my @newstats;
my $cnt = 0;
foreach my $line ( @stats )
{
if ( $cnt == 55 ) {push @newstats,""}
push @newstats,$line;
$cnt++;
}
sysopen ( $STATS,"/var/lib/csf/stats/lfdstats", O_RDWR | O_CREAT );
flock ( $STATS, LOCK_EX );
seek ( $STATS, 0, 0 );
truncate ( $STATS, 0 );
foreach my $line ( @newstats )
{
print $STATS "$line\n";
}
close ( $STATS );
rename "/var/lib/csf/stats/lfdmain", "/var/lib/csf/stats/lfdmain.".time;
close ( $OLDSTATS );
sysopen ( $STATS,"/var/lib/csf/stats/lfdstats", O_RDWR | O_CREAT );
}
else
{
sysopen ( $STATS,"/var/lib/csf/stats/lfdstats", O_RDWR | O_CREAT );
}
flock ($STATS, LOCK_SH);
my @stats = <$STATS>;
chomp @stats;
close ($STATS);
if (@stats)
{
ConfigServer::ServerStats::charts($config{CC_LOOKUPS},$imghddir);
print ConfigServer::ServerStats::charts_html($config{CC_LOOKUPS},$imgdir);
}
else
{
print "<table class='table table-bordered table-striped'>\n";
print "<tr><td>No statistical data has been collected yet</td></tr></table>\n";
}
&renderBtnReturn;
return;
}
# end chart
###############################################################################
# start systemstats
sub systemstats
{
my $type = shift;
if ( $type eq "" ) { $type = "load" }
my $img;
my $imgdir = "";
my $imghddir = "";
if (-e "/usr/local/cpanel/version")
{
if (-e "/usr/local/cpanel/bin/register_appconfig")
{
$imgdir = "csf/";
$imghddir = "cgi/configserver/csf/";
}
else
{
$imgdir = "/";
$imghddir = "";
}
}
elsif (-e "/usr/local/directadmin/conf/directadmin.conf")
{
$imgdir = "/CMD_PLUGINS_ADMIN/csf/images/";
$imghddir = "plugins/csf/images/";
umask(0133);
}
elsif (-e "/usr/local/interworx")
{
$imgdir = "/configserver/csf/";
$imghddir = "/usr/local/interworx/html/configserver/csf/";
umask(0133);
}
elsif (-e "/usr/local/CyberCP/")
{
$imgdir = "/static/configservercsf/";
$imghddir = "/usr/local/CyberCP/public/static/configservercsf/";
umask(0133);
}
if ($config{THIS_UI})
{
$imgdir = "$images/";
$imghddir = "/etc/csf/ui/images/";
}
if (defined $ENV{WEBMIN_VAR} and defined $ENV{WEBMIN_CONFIG})
{
$imgdir = "/csf/";
$imghddir = "";
}
sysopen (my $STATS,"/var/lib/csf/stats/system", O_RDWR | O_CREAT);
flock ($STATS, LOCK_SH);
my @stats = <$STATS>;
chomp @stats;
close ($STATS);
if (@stats > 1)
{
ConfigServer::ServerStats::graphs($type,$config{ST_SYSTEM_MAXDAYS},$imghddir);
print "<div class='text-center'><form action='$script' method='post'><input type='hidden' name='action' value='systemstats'><select name='graph'>\n";
my $selected;
if ($type eq "" or $type eq "load") {$selected = "selected"} else {$selected = ""}
print "<option value='load' $selected>Load Average Statistics</option>\n";
if ($type eq "cpu") {$selected = "selected"} else {$selected = ""}
print "<option value='cpu' $selected>CPU Statistics</option>\n";
if ($type eq "mem") {$selected = "selected"} else {$selected = ""}
print "<option value='mem' $selected>Memory Statistics</option>\n";
if ($type eq "net") {$selected = "selected"} else {$selected = ""}
print "<option value='net' $selected>Network Statistics</option>\n";
if (-e "/proc/diskstats")
{
if ($type eq "disk") {$selected = "selected"} else {$selected = ""}
print "<option value='disk' $selected>Disk Statistics</option>\n";
}
if ($config{ST_DISKW})
{
if ($type eq "diskw") {$selected = "selected"} else {$selected = ""}
print "<option value='diskw' $selected>Disk Write Performance</option>\n";
}
if (-e "/var/lib/csf/stats/email")
{
if ($type eq "email") {$selected = "selected"} else {$selected = ""}
print "<option value='email' $selected>Email Statistics</option>\n";
}
my $dotemp = 0;
if (-e "/sys/devices/platform/coretemp.0/temp3_input") {$dotemp = 3}
if (-e "/sys/devices/platform/coretemp.0/temp2_input") {$dotemp = 2}
if (-e "/sys/devices/platform/coretemp.0/temp1_input") {$dotemp = 1}
if ($dotemp)
{
if ($type eq "temp") {$selected = "selected"} else {$selected = ""}
print "<option value='temp' $selected>CPU Temperature</option>\n";
}
if ($config{ST_MYSQL})
{
if ($type eq "mysqldata") {$selected = "selected"} else {$selected = ""}
print "<option value='mysqldata' $selected>MySQL Data</option>\n";
if ($type eq "mysqlqueries") {$selected = "selected"} else {$selected = ""}
print "<option value='mysqlqueries' $selected>MySQL Queries</option>\n";
if ($type eq "mysqlslowqueries") {$selected = "selected"} else {$selected = ""}
print "<option value='mysqlslowqueries' $selected>MySQL Slow Queries</option>\n";
if ($type eq "mysqlconns") {$selected = "selected"} else {$selected = ""}
print "<option value='mysqlconns' $selected>MySQL Connections</option>\n";
}
if ($config{ST_APACHE})
{
if ($type eq "apachecpu") {$selected = "selected"} else {$selected = ""}
print "<option value='apachecpu' $selected>Apache CPU Usage</option>\n";
if ($type eq "apacheconn") {$selected = "selected"} else {$selected = ""}
print "<option value='apacheconn' $selected>Apache Connections</option>\n";
if ($type eq "apachework") {$selected = "selected"} else {$selected = ""}
print "<option value='apachework' $selected>Apache Workers</option>\n";
}
print "</select> <input type='submit' class='btn btn-default' value='Select Graphs'></form></div><br />\n";
print ConfigServer::ServerStats::graphs_html($imgdir);
unless ($config{ST_MYSQL} and $config{ST_APACHE})
{
print "<br>\n<table class='table table-bordered table-striped'>\n";
print "<tr><td>You may be able to collect more statistics by enabling ST_MYSQL or ST_APACHE in the csf configuration</td></tr></table>\n";
}
}
else
{
print "<table class='table table-bordered table-striped'>\n";
print "<tr><td>No statistical data has been collected yet</td></tr></table>\n";
}
&renderBtnReturn;
return;
}
# #
# File > Edit
# #
sub editfile
{
my $file = shift;
my $filename = basename($file); # $filename = "my.blocklist"
my $save = shift;
my $extra = shift;
my $ace = 0;
sysopen ( my $IN, $file, O_RDWR | O_CREAT ) or die "Unable to open file: $!";
flock ( $IN, LOCK_SH );
my @confdata = <$IN>;
close ( $IN );
chomp @confdata;
if (-e "/usr/local/cpanel/3rdparty/share/ace-editor/optimized/src-min-noconflict/ace.js")
{
$ace = 1
}
if ( -e "/usr/local/cpanel/version" and $ace and !$config{THIS_UI} )
{
print "<script src='/libraries/ace-editor/optimized/src-min-noconflict/ace.js'></script>\n";
print "<h4>Edit <code>$file</code></h4>\n";
print "<button class='btn btn-default' id='toggletextarea-btn'>Toggle Editor/Textarea</button>\n";
print "<div class='pull-right btn-group'><button class='btn btn-default' id='fontminus-btn'><strong>a</strong><span class='glyphicon glyphicon-arrow-down'></span></button>\n";
print "<button class='btn btn-default' id='fontplus-btn'><strong>A</strong><span class='glyphicon glyphicon-arrow-up'></span></button></div>\n";
print "<form action='$script' method='post'>\n";
print "<input type='hidden' name='action' value='$save'>\n";
print "<input type='hidden' name='ace' value='1'>\n";
if ($extra ) {print "<input type='hidden' name='$extra' value='$FORM{$extra}'>\n";}
print "<div id='editor' style='width:100%;height:500px;border: 1px solid #000;display:none;'>";
print "Loading...</div>\n";
print "<div id='textarea'><textarea class='textarea' name='formdata' id='formdata' style='width:100%;height:500px;border: 1px solid #000;font-family:\"Courier New\", Courier;font-size:14px;line-height:1.1' wrap='off'>";
print "# Do not remove or change this line as it is a safeguard for the UI editor\n";
foreach my $line ( @confdata )
{
$line =~ s/\</\<\;/g;
$line =~ s/\>/\>\;/g;
print $line."\n";
}
print "</textarea><br></div>\n";
print "<br><div class='text-center'><input type='submit' class='btn btn-default' value='Change'></div>\n";
print "</form>\n";
print <<EOF;
<script>
var myFont = 14;
var textarea = \$('#formdata');
var editordiv = \$('#editor');
var editor = ace.edit("editor");
editor.setTheme("ace/theme/tomorrow");
editor.setShowPrintMargin(false);
editor.setOptions(
{
fontFamily: "Courier New, Courier",
fontSize: "14px"
});
editor.getSession().setMode("ace/mode/space");
editor.getSession().on('change', function ( )
{
textarea.val(editor.getSession().getValue() );
});
textarea.on('change', function ( )
{
editor.getSession().setValue(textarea.val() );
});
editor.getSession().setValue(textarea.val() );
\$( '#textarea' ).hide( );
editordiv.show( );
\$( "#toggletextarea-btn" ).on( 'click', function ( )
{
\$('#textarea').toggle();
editordiv.toggle();
});
\$( "#fontplus-btn" ).on( 'click', function ( )
{
myFont++;
if ( myFont > 20 )
{
myFont = 20
}
editor.setFontSize( myFont )
textarea.css( "font-size", myFont + "px" );
});
\$( "#fontminus-btn" ).on( 'click', function ( )
{
myFont--;
if ( myFont < 10 )
{
myFont = 10
}
editor.setFontSize( myFont )
textarea.css( "font-size", myFont + "px" );
});
</script>
EOF
}
else
{
# #
# File Blocklists
# #
if ( $filename eq 'csf.blocklists' )
{
print "<div id='insiders' class='tab-pane active'>\n";
print "<div id='sponsor' class='tab-pane'>\n";
print "<div class='panel panel-default'>\n";
print "<div class='panel-heading' style='line-height: 2.5em;height: 2.5em;font-weight:bold;'>CSF Blocklist Service</div>\n";
print "<div class='panel-body'>\n";
print "We provide a full <a target='_blank' href='https://blocklist.configserver.dev/help'>Blocklist API Service</a> which can be enabled via the file below. You can also use any 3rd-party blocklists available on the internet.";
print "<ul style='text-align:left;'>\n";
print "<li style='font-size:9pt;'>Includes popular <a target='_blank' href='https://blocklist.configserver.dev/master.ipset'>master.ipset</a> and <a target='_blank' href='https://blocklist.configserver.dev/highrisk.ipset'>highrisk.ipset</a> blocklists.</li>\n";
print "<li style='font-size:9pt;'>Millions of bad-actor IPs reported for brute-force, IoT, port-scanning, SSH abuse, and more.</li>\n";
print "<li style='font-size:9pt;'>Includes ASN and full country-level blocklists.</li>\n";
print "<li style='font-size:9pt;'>Updated automatically multiple times per day.</li>\n";
print "<li style='font-size:9pt;'><a target='_blank' href='https://blocklist.configserver.dev/help'>Blocklist API</a> offers customization parameters such as <code>?include</code>, <code>?exclude</code>, <code>?limit</code>, <code>?ipVersion</code>, <code>?note</code> </li>\n";
print "<li style='font-size:9pt;'>View full feature list and documentation <a target='_blank' href='https://blocklist.configserver.dev/help'>here</a></li>\n";
print "</ul>\n";
print "</div>\n";
print "</div>\n";
print "</div>\n";
print "</div> <!-- end id='insiders' -->\n"; # end TAB id='insiders'
}
if ($config{DIRECTADMIN})
{
print "<form action='$script?pipe_post=yes' method='post'>\n<div class='panel panel-default'>\n";
}
else
{
print "<form action='$script' method='post'>\n<div class='panel panel-default'>\n";
}
print "<div class='panel-heading panel-heading-cxs'>Modify <code>$file</code></div>\n";
# #
# Edit File Body
# #
print "<div class='panel-body'>\n";
print "<input type='hidden' name='action' value='$save'>\n";
if ( $extra )
{
print "<input type='hidden' name='$extra' value='$FORM{$extra}'>\n";
}
print "<textarea class='textarea' name='formdata' style='width:100%;height:600px;border: 1px solid #000;font-family: Monospace;' wrap='off'>";
foreach my $line ( @confdata )
{
$line =~ s/\</\<\;/g;
$line =~ s/\>/\>\;/g;
print $line."\n";
}
print "</textarea></div>\n";
print "<div class='panel-footer text-center'><input type='submit' class='btn btn-default' value='Change'></div>\n";
print "</div></form>\n";
}
return;
}
# #
# File > Save
# #
sub savefile
{
my $file = shift;
my $restart = shift;
$FORM{formdata} =~ s/\r//g;
if ( $FORM{ace} == "1" )
{
if ( $FORM{formdata} !~ /^# Do not remove or change this line as it is a safeguard for the UI editor\n/ )
{
print "<div>UI editor safeguard missing, changes have not been saved.</div>\n";
return;
}
$FORM{formdata} =~ s/^# Do not remove or change this line as it is a safeguard for the UI editor\n//g;
}
sysopen (my $OUT, $file, O_WRONLY | O_CREAT) or die "Unable to open file: $!";
flock ($OUT, LOCK_EX);
seek ($OUT, 0, 0);
truncate ($OUT, 0);
if ($FORM{formdata} !~ /\n$/)
{
$FORM{formdata} .= "\n"
}
print $OUT $FORM{formdata};
close ($OUT);
if ($restart eq "csf")
{
print "<div>Changes saved. You should restart csf.</div>\n";
print "<div><form action='$script' method='post'><input type='hidden' name='action' value='restart'><input type='submit' class='btn btn-default' value='Restart csf'></form></div>\n";
}
elsif ($restart eq "lfd")
{
print "<div>Changes saved. You should restart lfd.</div>\n";
print "<div><form action='$script' method='post'><input type='hidden' name='action' value='lfdrestart'><input type='submit' class='btn btn-default' value='Restart lfd'></form></div>\n";
}
elsif ($restart eq "both")
{
print "<div>Changes saved. You should restart csf and lfd.</p>\n";
print "<div><form action='$script' method='post'><input type='hidden' name='action' value='restartboth'><input type='submit' class='btn btn-default' value='Restart csf+lfd'></form></div>\n";
}
else
{
print "<div>Changes saved.</div>\n";
}
return;
}
# #
# Cloudflare
# #
sub cloudflare
{
my $scope = &ConfigServer::CloudFlare::getscope();
print "<link rel='stylesheet' href='$images/bootstrap-chosen.css'>\n";
print "<script src='$images/chosen.min.js'></script>\n";
print "<script>\n";
print "\$(function() {\n";
print " \$('.chosen-select').chosen();\n";
print " \$('.chosen-select-deselect').chosen({ allow_single_deselect: true });\n";
print "});\n";
print "</script>\n";
print "<table class='table table-bordered table-striped'>\n";
print "<thead><tr><th colspan='2'>CSF - CloudFlare</th></tr></thead>";
print "<tr><td>Select the user(s), then select the action below</td><td style='width:100%'><select data-placeholder='Select user(s)' class='chosen-select' id='domains' name='domains' multiple>\n";
foreach my $user ( keys %{$scope->{user}} )
{
print "<option>$user</option>\n"
}
print "</select></td></tr>\n";
# } else {
# print "<tr><td>Select the domain(s), then select the action below</td><td style='width:100%'><select data-placeholder='Select domain(s)' class='chosen-select' id='domains' name='domains' multiple>\n";
# foreach my $domain (keys %{$scope->{domain}}) {print "<option>$domain</option>\n"}
# print "</select></td></tr>\n";
# }
print "<tr><td><button type='button' id='cflistbtn' class='btn btn-default' disabled='true'>CF List Rules</button></td><td style='width:100%'><form action='#' id='cflist'>List <select name='type' id='type'><option>all</option><option>block</option><option>challenge</option><option>whitelist</option></select> rules in CloudFlare ONLY for the chosen accounts</form></td></tr>";
print "<tr><td><button type='button' id='cfaddbtn' class='btn btn-default' disabled='true'>CloudFlare Add</button></td><td style='width:100%'><form action='#' id='cfadd'>Add <select name='type' id='type'><option>block</option><option>challenge</option><option>whitelist</option></select> rule for target <input type='text' name='target' value='' size='18' id='target'> in CloudFlare ONLY for the chosen accounts</form></td></tr>\n";
print "<tr><td><button type='button' id='cfremovebtn' class='btn btn-default' disabled='true'>CloudFlare Delete</button></td><td style='width:100%'><form action='#' id='cfremove'>Delete rule for target <input type='text' name='target' value='' size='18' id='target'> in CloudFlare ONLY</form></td></tr>\n";
print "<tr><td><button type='button' id='cftempdenybtn' class='btn btn-default' disabled='true'>CF Temp Allow/Deny</button></td><td style='width:100%'><form action='#' id='cftempdeny'>Temporarily <select name='do' id='do'><option>allow</option><option>deny</option></select> IP address <input type='text' name='target' value='' size='18' id='target'> for $config{CF_TEMP} secs in CloudFlare AND csf for the chosen accounts and those with to \"any\"</form></td></tr>";
print "</table>\n";
print "<div id='CFajax'>\n";
print "<div class='panel panel-info'>\n";
print "<div class='panel-heading'>No output to show yet.</div>\n";
print "</div>\n";
print "</div>\n";
print "<div class='panel panel-default'>\n";
print "<div class='panel-heading' style='line-height: 2.5em;height: 2.5em;font-weight:bold;'>Important</div>\n";
print "<div class='panel-body'>\n";
print "Take note of the following information:\n";
print "<ul style='text-align:left;'>\n";
print "<li style='font-size:9pt;'><mark>target</mark> can be one of:\n";
print "<ul>\n";
print "<li style='font-size:9pt;'>An IP address</li>\n";
print "<li style='font-size:9pt;'>2 letter Country Code</li>\n";
print "<li style='font-size:9pt;'>IP range CIDR</li>\n";
print "</ul>";
print "<li style='font-size:9pt;'>Only Enterprise customers can <mark>block</mark> a Country Code, but all can <mark>allow</mark> and <mark>challenge</mark></li>\n";
print "<li style='font-size:9pt;'>IP range CIDR is limited to /16 and /24</blockquote></li>\n";
print "</ul>\n";
print "</div>\n";
print "</div>\n";
print "</div>\n";
print "<script>\n";
print "\$( document ).ready( function( ) {\n";
print " \$( '#cflist' ).submit( function() { \$( '#cflistbtn' ).click( ); return false } )\n";
print " \$( '#cftempdeny' ).submit( function() { \$( '#cftempdenybtn' ).click( ); return false } )\n";
print " \$( '#cfadd' ).submit( function() { \$( '#cfaddbtn' ).click( ); return false } )\n";
print " \$( '#cfremove' ).submit( function() { \$( '#cfremovebtn' ).click( ); return false } )\n";
print " \$( 'button' ).click( function() {\n";
print " \$( 'body' ).css( 'cursor', 'progress' );\n";
print " var myurl;\n";
print " if ( this.id == 'cflistbtn' ) { myurl = '$script?action=cflist&type='+\$( \"#cflist #type\" ).val( ) + '&domains=' + \$( \"#domains\" ).val( ); }\n";
print " if ( this.id == 'cftempdenybtn' ) { myurl = '$script?action=cftempdeny&do='+\$( \"#cftempdeny #do\" ).val( ) + '&target='+\$( \"#cftempdeny #target\" ).val( ).replace( /\\s/g, '' )+'&domains=' + \$( \"#domains\" ).val( ); }\n";
print " if ( this.id == 'cfaddbtn' ) { myurl = '$script?action=cfadd&type='+\$( \"#cfadd #type\" ).val( ) + '&target='+\$( \"#cfadd #target\" ).val( ).replace( /\\s/g,'' ) + '&domains=' + \$( \"#domains\" ).val( ); }\n";
print " if ( this.id == 'cfremovebtn' ) { myurl = '$script?action=cfremove&target=' + \$( \"#cfremove #target\" ).val( ).replace( /\\s/g,'' )+'&domains=' + \$( \"#domains\" ).val( ); }\n";
# print " alert('URL:'+myurl);\n";
print " \$( '#CFajax' ).html( '<div id=\"loader\"></div><div class=\"panel panel-info\"><div class=\"panel-heading\">Loading ...</div></div>' );\n";
print " \$( '#CFajax' ).load( myurl );\n";
print " \$( 'body' ).css( 'cursor', 'default' );\n";
print " });\n";
print " \$( '#domains' ).on( 'keyup change', function( ) {\n";
print " if (\$( '#domains' ).val() == null ) {\n";
print " \$( '#cflistbtn,#cftempdenybtn,#cfaddbtn,#cfremovebtn' ).prop( 'disabled', true );\n";
print " } else {\n";
print " \$( '#cflistbtn,#cftempdenybtn,#cfaddbtn,#cfremovebtn' ).prop( 'disabled', false );\n";
print " }\n";
print " });\n";
print "});\n";
print "</script>\n";
&renderBtnReturn;
return;
}
# end cloudflare
###############################################################################
# start resize
sub resize
{
my $part = shift;
my $scroll = shift;
if ( $part eq "top" )
{
print "<div class='pull-right btn-group'><button class='btn btn-default' id='fontminus-btn'><strong>a</strong><span class='glyphicon glyphicon-arrow-down'></span></button>\n";
print "<button class='btn btn-default' id='fontplus-btn'><strong>A</strong><span class='glyphicon glyphicon-arrow-up'></span></button></div>\n";
}
else
{
print "<script>\n";
if ( $scroll )
{
print "\$('#output').scrollTop(\$('#output')[0].scrollHeight);\n";
}
print <<EOF;
var myFont = 14;
\$( "#fontplus-btn" ).on( 'click', function ( )
{
myFont++;
if ( myFont > 20 )
{
myFont = 20
}
\$( '#output' ).css( "font-size", myFont + "px" );
});
\$( "#fontminus-btn" ).on( 'click', function ( )
{
myFont--;
if ( myFont < 10 )
{
myFont = 10
}
\$( '#output' ).css( "font-size", myFont + "px" );
});
</script>
EOF
}
return;
}
# #
# Print Return
# #
sub renderBtnReturn
{
print "<hr><div><form action='$script' method='post'><input type='hidden' name='mobi' value='$mobile'><input id='csfreturn' type='submit' class='btn btn-default' style='margin-bottom:3em;' value='Return'></form></div>\n";
return;
}
# #
# Confirm Modal
#
# start confirmmodal
# print "<button type='button' class='btn-settings-base btn-ok strict-width-settings confirmButton' data-query='Are you sure?' data-href='$script?action=fix' data-toggle='modal' data-target='#confirmmodal'>Submit</button>\n";
# &confirmmodal;
# #
sub confirmmodal
{
print "<div class='modal fade' id='confirmmodal' tabindex='-1' role='dialog' aria-labelledby='myModalLabel' aria-hidden='true' data-backdrop='false' style='background-color: rgba(0, 0, 0, 0.5)'>\n";
print "<div class='modal-dialog modal-sm'>\n";
print "<div class='modal-content'>\n";
print "<div class='modal-body'>\n";
print "<h4 id='modal-text'>text</h4>\n";
print "</div>\n";
print "<div class='modal-footer'>\n";
print "<a id='modal-submit' href='#' class='btn btn-success'>Yes - Continue</a>\n";
print "<button type='button' class='btn btn-danger' data-dismiss='modal'>No - Cancel</button>\n";
print "</div>\n";
print "</div>\n";
print "</div>\n";
print "</div>\n";
print "<script>\n";
print " \$('button.confirmButton').on( 'click', function(e) {\n";
print " var dataquery = \$(this).attr('data-query');\n";
print " var datahref = \$(this).attr('data-href');\n";
print " \$('#modal-text').html(dataquery);\n";
print " \$('#modal-submit').attr({'href':datahref});\n";
print "});\n";
print "\$('.modal').click(function(event){\n";
print " \$(event.target).modal('hide')\n";
print "});\n";
print "</script>\n";
return;
}
# #
# Version > Get
# #
sub csgetversion
{
my $product = shift;
my $current = shift;
my $upgrade = 0;
my $newversion;
my $file_error = "/var/lib/configserver/${product}.txt.error";
my $file_main = "/var/lib/configserver/${product}.txt";
my $file_fallback = "/var/lib/configserver/error";
if (-e $file_error)
{
open(my $VERSION, "<", $file_error);
flock($VERSION, LOCK_SH);
$newversion = <$VERSION>;
close($VERSION);
chomp $newversion;
$newversion = $newversion eq ""
? "Failed to retrieve latest version from ConfigServer"
: "Failed to retrieve latest version from ConfigServer: $newversion";
}
elsif (-e $file_main)
{
open(my $VERSION, "<", $file_main);
flock($VERSION, LOCK_SH);
$newversion = <$VERSION>;
close($VERSION);
chomp $newversion;
if ($newversion eq "")
{
$newversion = "Failed to retrieve latest version from ConfigServer";
}
else
{
# Split numeric part and optional suffix
my ($num_version, $suffix) = split /-/, $newversion, 2;
my @current_parts = split /\./, $current;
my @new_parts = split /\./, $num_version;
my $is_newer = 0;
for (my $i = 0; $i < @new_parts || $i < @current_parts; $i++)
{
my $n = $new_parts[$i] // 0;
my $c = $current_parts[$i] // 0;
if ($n > $c) { $is_newer = 1; last; }
if ($n < $c) { $is_newer = 0; last; }
}
$upgrade = $is_newer ? 1 : 0;
$newversion = $upgrade ? $newversion : "";
}
}
elsif ( -e $file_fallback )
{
open( my $VERSION, "<", $file_fallback );
flock( $VERSION, LOCK_SH );
$newversion = <$VERSION>;
close($VERSION);
chomp $newversion;
$newversion = $newversion eq ""
? "Failed to retrieve latest version from ConfigServer"
: "Failed to retrieve latest version from ConfigServer: $newversion";
}
else
{
$newversion = "Failed to retrieve latest version from ConfigServer";
}
return ( $upgrade, $newversion );
}
# #
# Version > Get Manual
#
# Stable https://$config{DOWNLOADSERVER}/csf/version.txt
# Alpha / RC https://$config{DOWNLOADSERVER}/csf/version.txtchannel=alpha
# #
sub manualversion
{
my $current = shift;
my $upgrade = 0;
my $url = "https://$config{DOWNLOADSERVER}/csf/version.txt";
if ($config{URLGET} == 1)
{
$url = "http://$config{DOWNLOADSERVER}/csf/version.txt";
}
# Insiders Release Channel
if ( ( $config{SPONSOR_RELEASE_INSIDERS} // 0 ) == 1 && ( $config{SPONSOR_LICENSE} // '' ) ne '' )
{
$url .= "?channel=insiders&license=$config{SPONSOR_LICENSE}";
}
print "<div><pre class='comment' style='white-space: pre-wrap;'>\n";
print "Connecting to <a href='$url'>$url</a>\n";
print "Fetching <code>csf/version.txt</code> from update server\n";
my ($status, $newversion) = $urlget->urlget($url);
if (!$status && $newversion ne "")
{
# Split numeric part and optional suffix
my ($num_version, $suffix) = split /-/, $newversion, 2;
my $current_num = $current;
my $is_newer = 0;
# Compare numeric parts
my @current_parts = split /\./, $current_num;
my @new_parts = split /\./, $num_version;
for (my $i = 0; $i < @new_parts || $i < @current_parts; $i++)
{
my $n = $new_parts[$i] // 0;
my $c = $current_parts[$i] // 0;
if ($n > $c) { $is_newer = 1; last; }
if ($n < $c) { $is_newer = 0; last; }
}
if ($is_newer)
{
$upgrade = 1;
}
else
{
$newversion = ""; # no upgrade
}
}
else
{
$newversion = "";
}
return ( $upgrade, $newversion );
}
1;