File: //usr/local/cwaf/scripts/cwaf-cli.pl
#!/bin/sh
eval 'if [ -x /usr/local/cpanel/3rdparty/bin/perl ]; then exec /usr/local/cpanel/3rdparty/bin/perl -x -- $0 ${1+"$@"}; else exec /usr/bin/perl -x $0 ${1+"$@"}; fi;'
if 0;
#!/usr/bin/perl
#SVN
use strict qw(refs subs);
use warnings;
use Getopt::Long;
use Data::Dumper;
BEGIN { require '/etc/cwaf/use_lib.pl' if -f '/etc/cwaf/use_lib.pl'; }
use Comodo::CWAF::Main;# qw(%conf do_log get_name create_pid_file test_version);
use Comodo::CWAF::ClientAPI;
use Comodo::CWAF::Platform;
use Comodo::CWAF::Excludes;
use Comodo::CWAF::CatalogUpdate;
no warnings 'redefine';
# redefining functions to get more friendly domain names
sub get_domainlist_redef() {
my $list = Comodo::CWAF::Platform::get_domainlist();
$_ =~ s/(:[0-9|]+)// for @$list;
return $list;
}
sub get_disabled_domainlist_redef {
my $list = Comodo::CWAF::Excludes::get_disabled_domainlist();
$_ =~ s/(:[0-9|]+)// for @$list;
return $list;
}
# steps to run Webmin script
if(&is_webmin()) {
$ENV{'WEBMIN_CONFIG'} = '/etc/webmin';
my $var = '/var/webmin';
chop($var = `cat /etc/webmin/var-path`);
$ENV{'WEBMIN_VAR'} = $var;
my $root = &Comodo::CWAF::Webmin::get_webmin_root();
die "Can't get Webmin root directory. Please check if /etc/webmin/miniserv.conf is present and readable." unless $root;
chdir($root);
$0 = "$root/cwaf-cli.pl";
eval "use WebminCore;";
&init_config();
}
#Init vars
our (%conf, $pr_name);
my ($var,$command,%opts,$log_file,$domain, $catalog);
my $force_domain = 0;
# Prototypes
sub do_console_log($);
# undef - STDOUT
$pr_name = get_name();
$log_file = "$conf{'log_dir'}/$pr_name.log";
# PID file
$conf{'pid_dir'} = $conf{'cwaf_path'} . '/run';
######################################## BEGIN ####################################
$| = 1;
# open logfile for stderr
#open(LOGFILE, ">/dev/null");
unless(open(LOGFILE, ">>$log_file")) {
print STDERR "ERROR: can't open file $log_file\n";
}
# redirect errors to /dev/null if debug < 6
# or redirect errors to logfile
if(int($conf{'debug'}) < 6) {
open(DEVNULL, ">/dev/null");
STDERR->fdopen(\*DEVNULL, 'w');
}
else {
STDERR->fdopen(\*LOGFILE, 'w');
}
LOGFILE->autoflush(1);
# default values
$domain = 'global';
$catalog = undef;
# set avail arguments
$var = GetOptions(\%opts,'help|h','version|v','loglevel|g=i','domain|d=s','domain_list|l',
'exclude_add|xa=i@{1,}','exclude_del|xd=i@{1,}','exclude_add_cat|xac=s@{1,}',
'exclude_del_cat|xdc=s@{1,}','exclude_add_grp|xag=s@{1,}','exclude_del_grp|xdg=s@{1,}',
'exclude_list|xl','exclude_list_extended|xlx','list_cat|lc','list_grp|lg','force_domain|f',
'enable_domain|de=s@{1,}','disable_domain|dd=s@{1,}','disabled_list|dl',
'gui_start|gstart','gui_stop|gstop');
# check pid file
$var = create_pid_file();
if($var == 0 || $var > 1) {
print "ERROR: can't create pid file" if($var == 0);
print "WARN: another process is started ($var)" if($var > 1);
exit(0);
}
$domain = $opts{'domain'} if( $opts{'domain'} );
# argument --loglevel, -g
if( $opts{'loglevel'} ) {
if($opts{'loglevel'} > 0 && $opts{'loglevel'} < 12 ) {
$conf{'debug'} = $opts{'loglevel'};
} else {
print "loglevel should be from 1 to 10\n";
exit(0);
}
}
# argument --force_domain, -f
if( $opts{'force_domain'} ) {
$force_domain = 1;
}
# create catalog object if required
if($opts{'exclude_add'} || $opts{'exclude_del'} || $opts{'exclude_add_cat'} || $opts{'exclude_del_cat'} || $opts{'exclude_add_grp'} ||
$opts{'exclude_del_grp'} || $opts{'exclude_list'} || $opts{'exclude_list_extended'} || $opts{'list_cat'} || $opts{'list_grp'}) {
# check if rules exists
if(! &rules_exist()) {
print "No Comodo rules found. Please download latest ruleset with $conf{'cwaf_path'}/$conf{'updater_bin'} \n";
exit (1);
}
$catalog = Comodo::CWAF::CatalogUpdate->new('yml_path' => $conf{'cwaf_path'}.'/etc/yml',
'cache_path' => $conf{'cwaf_path'}.'/tmp/CACHE',
'verbose' => ( $conf{'debug'} eq 11 ) ? 1 : 0,
'cpanel_log' => 0 );
}
# argument --help, -h
if( $opts{'help'} ) { &do_print_help_message; exit(0); }
# argument --version, -v
elsif( $opts{'version'} ) {
print "Plugin version=".get_client_version()." \n";
print "Last available version=".get_available_version()." \n";
print "Installed rules version=".get_local_rules_version()."\n";
print "Available rules version=".get_remote_rules_version()."\n";
print "Installed for web platform=".get_web_platform()."\n";
exit(0);
}
# argument --exclude_add, -xa
elsif( $opts{'exclude_add'} ) {
my @data;
if(! &domain_exist($domain)) {
print "no domain found $domain\n";
exit (1);
}
my $excludes = $opts{'exclude_add'};
print "turning off rules:".join(',', @$excludes)."\n";
print "domain: $domain\n";
foreach my $ruleid (@$excludes) {
my $rstats = $catalog->find_rule_by('id' => $ruleid);
if(defined($rstats->{'category'}) && defined($rstats->{'group'})) {
my %ex;
$ex{'category'} = $rstats->{'category'};
$ex{'group'} = $rstats->{'group'};
$ex{'id'} = $ruleid;
$ex{'status'} = 0;
push(@data, \%ex);
}
}
$var = $catalog->update_exclude_list_by('domain' => $domain, 'data' => \@data);
unless($var) {
print "ERROR: ".$catalog->error."\n";
}
$domain = undef unless($force_domain);
my ($status, $errmsg) = create_exclude_list($domain, 1);
print "ERROR: ".$errmsg."\n" unless($status);
&fix_permissions();
}
# argument --exclude_del, -xd
elsif( $opts{'exclude_del'} ) {
my @data;
if(! &domain_exist($domain)) {
print "no domain found $domain\n";
exit (1);
}
my $excludes = $opts{'exclude_del'};
print "turning on rules:".join(',', @$excludes)."\n";
print "domain: $domain\n";
foreach my $ruleid (@$excludes) {
my $rstats = $catalog->find_rule_by('id' => $ruleid);
if(defined($rstats->{'category'}) && defined($rstats->{'group'})) {
my %ex;
$ex{'category'} = $rstats->{'category'};
$ex{'group'} = $rstats->{'group'};
$ex{'id'} = $ruleid;
$ex{'status'} = 1;
push(@data, \%ex);
}
}
$var = $catalog->update_exclude_list_by('domain' => $domain, 'data' => \@data);
unless($var) {
print "ERROR: ".$catalog->error."\n";
}
$domain = undef unless($force_domain);
my ($status, $errmsg) = create_exclude_list($domain, 1);
print "ERROR: ".$errmsg."\n" unless($status);
&fix_permissions();
}
# argument --exclude_add_cat, -xac
elsif( $opts{'exclude_add_cat'} ) {
my @data;
if(! &domain_exist($domain)) {
print "no domain found $domain\n";
exit (1);
}
my $excludes = $opts{'exclude_add_cat'};
print "turning off categories:".join(',', @$excludes)."\n";
print "domain: $domain\n";
foreach my $category (@$excludes) {
my $rstats = $catalog->find_category_by('category' => $category);
if(defined($rstats->{'category'})) {
my %ex;
$ex{'category'} = $rstats->{'category'};
$ex{'status'} = 0;
push(@data, \%ex);
}
}
$var = $catalog->update_exclude_list_by('domain' => $domain, 'data' => \@data);
unless($var) {
print "ERROR: ".$catalog->error."\n";
}
$domain = undef unless($force_domain);
my ($status, $errmsg) = create_exclude_list($domain, 1);
print "ERROR: ".$errmsg."\n" unless($status);
&fix_permissions();
}
# argument --exclude_del_cat, -xdc
elsif( $opts{'exclude_del_cat'} ) {
my @data;
if(! &domain_exist($domain)) {
print "no domain found $domain\n";
exit (1);
}
my $excludes = $opts{'exclude_del_cat'};
print "turning on categories:".join(',', @$excludes)."\n";
print "domain: $domain\n";
foreach my $category (@$excludes) {
my $rstats = $catalog->find_category_by('category' => $category);
if(defined($rstats->{'category'})) {
my %ex;
$ex{'category'} = $rstats->{'category'};
$ex{'status'} = 1;
push(@data, \%ex);
}
}
$var = $catalog->update_exclude_list_by('domain' => $domain, 'data' => \@data);
unless($var) {
print "ERROR: ".$catalog->error."\n";
}
$domain = undef unless($force_domain);
my ($status, $errmsg) = create_exclude_list($domain, 1);
print "ERROR: ".$errmsg."\n" unless($status);
&fix_permissions();
}
# argument --exclude_add_grp, -xag
elsif( $opts{'exclude_add_grp'} ) {
my @data;
if(! &domain_exist($domain)) {
print "no domain found $domain\n";
exit (1);
}
my $excludes = $opts{'exclude_add_grp'};
print "turning off groups:".join(',', @$excludes)."\n";
print "domain: $domain\n";
foreach my $group (@$excludes) {
my $rstats = $catalog->find_group_by('group' => $group);
if(defined($rstats->{'category'}) && defined($rstats->{'group'})) {
my %ex;
$ex{'category'} = $rstats->{'category'};
$ex{'group'} = $rstats->{'group'};
$ex{'status'} = 0;
push(@data, \%ex);
}
}
$var = $catalog->update_exclude_list_by('domain' => $domain, 'data' => \@data);
unless($var) {
print "ERROR: ".$catalog->error."\n";
}
$domain = undef unless($force_domain);
my ($status, $errmsg) = create_exclude_list($domain, 1);
print "ERROR: ".$errmsg."\n" unless($status);
&fix_permissions();
}
# argument --exclude_del_grp, -xdg
elsif( $opts{'exclude_del_grp'} ) {
my @data;
if(! &domain_exist($domain)) {
print "no domain found $domain\n";
exit (1);
}
my $excludes = $opts{'exclude_del_grp'};
print "turning on groups:".join(',', @$excludes)."\n";
print "domain: $domain\n";
foreach my $group (@$excludes) {
my $rstats = $catalog->find_group_by('group' => $group);
if(defined($rstats->{'category'}) && defined($rstats->{'group'})) {
my %ex;
$ex{'category'} = $rstats->{'category'};
$ex{'group'} = $rstats->{'group'};
$ex{'status'} = 1;
push(@data, \%ex);
}
}
$var = $catalog->update_exclude_list_by('domain' => $domain, 'data' => \@data);
unless($var) {
print "ERROR: ".$catalog->error."\n";
}
$domain = undef unless($force_domain);
my ($status, $errmsg) = create_exclude_list($domain, 1);
print "ERROR: ".$errmsg."\n" unless($status);
&fix_permissions();
}
# argument --exclude_list, -xl
elsif( $opts{'exclude_list'} ) {
my %out_hash;
if(! &domain_exist($domain)) {
print "no domain found $domain\n";
exit (1);
}
print "list of excluded rules for domain: $domain\n";
my $exs = $catalog->list_excludes('domain' => $domain);
foreach my $category (keys %$exs) {
#print "category: $category\n";
foreach my $group (keys %{$exs->{$category}}) {
#print "group: $group\n";
foreach my $rule_id (keys %{$exs->{$category}->{$group}}) {
my $rule_parent = $catalog->find_rule_by('id' => $rule_id)->{'parent'};
if( $rule_id eq $rule_parent ) {
unless(defined $out_hash{$rule_id}) {
#print "$rule_id\n";
$out_hash{$rule_id} = 0;
}
}
} #foreach rule
} # foreach group
} # foreach category
my @output = sort(keys %out_hash);
print join("\n", @output)."\n";
}
# argument --exclude_list_extended, -xlx
elsif( $opts{'exclude_list_extended'} ) {
my %out_hash;
if(! &domain_exist($domain)) {
print "no domain found $domain\n";
exit (1);
}
print "list of excluded rules for domain: $domain\n";
my $exs = $catalog->list_excludes('domain' => $domain);
foreach my $category (sort keys %$exs) {
print '-'x 25 ."\n$category (category)\n".'-'x 25 ."\n";
foreach my $group (sort keys %{$exs->{$category}}) {
next if ($group eq '_LIST_');
print "$group (group)\n";
foreach my $rule_id (sort keys %{$exs->{$category}->{$group}}) {
my $rule_parent = $catalog->find_rule_by('id' => $rule_id)->{'parent'};
if( $rule_id eq $rule_parent ) {
# unless(defined $out_hash{$rule_id}) {
print " $rule_id (parent)\n";
# $out_hash{$rule_id} = 0;
# }
} else {
print " $rule_id\n";
}
} #foreach rule
} # foreach group
} # foreach category
my @output = sort(keys %out_hash);
print join("\n", @output)."\n";
}
# argument --list_cat, -lc
elsif( $opts{'list_cat'} ) {
if(! &domain_exist($domain)) {
print "no domain found $domain\n";
exit (1);
}
print "list of categories for domain: $domain\n";
my $cats = $catalog->list_category_by('domain' => $domain)->{'list'};
foreach my $category (sort keys %$cats) {
my $status;
$status = ($cats->{$category}->{status} == 1) ? 'ON' : 'OFF';
printf "%-16s %s\n", $category, $status;
}
}
# argument --list_grp, -lg
elsif( $opts{'list_grp'} ) {
if(! &domain_exist($domain)) {
print "no domain found $domain\n";
exit (1);
}
print "list of excluded groups for domain: $domain\n";
my $cats = $catalog->list_category_by('domain' => $domain)->{'list'};
foreach my $category (sort keys %$cats) {
my $status;
$status = ($cats->{$category}->{status} == 1) ? 'ON' : 'OFF';
printf "%-17s (%s)\n", $category, $status;
my $grps = $catalog->list_group_by('category' => $category, 'domain' => $domain)->{'list'};
foreach my $group (sort keys %$grps) {
$status = ($grps->{$group}->{status} == 1) ? 'ON' : 'OFF';
printf " %-15s %s\n", $group, $status;
}
}
}
# argument --enable_domain, -nd
elsif( $opts{'enable_domain'} ) {
my (%domains_hash, $disabled);
my $domains = $opts{'enable_domain'};
print "enabling domains:".join(',', @$domains)."\n";
#@domains_hash{@$domains} = 0;
map { $domains_hash{$_} = 0; } @$domains;
my $prev_disabled = &get_disabled_domainlist();
foreach my $domn (@$prev_disabled) {
push(@$disabled, $domn) unless(defined $domains_hash{$domn});
}
set_disabled_domainlist($disabled);
&fix_permissions();
}
# argument --disable_domain, -dd
elsif( $opts{'disable_domain'} ) {
my (%prev_hash, %all_hash, $disabled);
my $domains = $opts{'disable_domain'};
print "disabling domains:".join(',', @$domains)."\n";
if( $force_domain ) {
# go straight to domain disabling
push(@$disabled, @$domains);
} else {
# make check if domain exists and not disabled yet
my $all_domains = &get_domainlist();
#@all_hash{@$all_domains} = 0;
map { $all_hash{$_} = 0; } @$all_domains;
my $prev_disabled = &get_disabled_domainlist();
#@prev_hash{@$prev_disabled} = 0;
map { $prev_hash{$_} = 0; } @$prev_disabled;
push(@$disabled, @$prev_disabled);
foreach my $domn (@$domains) {
unless(defined $all_hash{$domn}) {
print "no domain found: $domn\n";
next;
}
push(@$disabled, $domn) unless(defined $prev_hash{$domn});
}
}
set_disabled_domainlist($disabled);
&fix_permissions();
}
# argument --disabled_list, -dl
elsif( $opts{'disabled_list'} ) {
print "list of disabled domains:\n";
my $domains = &get_disabled_domainlist();
foreach my $domn (@$domains) {
print "$domn\n";
}
}
# argument --domain_list, -l
elsif( $opts{'domain_list'} ) {
print "list of all domains:\n";
# if(! &is_webpanel() ) {
# print "Not available for standalone installation\n";
# } else {
my $domains = &get_domainlist();
foreach my $domn (@$domains) {
print "$domn\n";
}
# }
}
# argument --gui_start, -gstart
elsif( $opts{'gui_start'} ) {
unless (-e "$conf{'cwaf_path'}/scripts/standalone-gui.pl") { print "GUI script $conf{'cwaf_path'}/scripts/standalone-gui.pl not found\n"; exit (1); }
unless (-e "$conf{'cwaf_path'}/etc/standalone-gui.conf") { print "Config file $conf{'cwaf_path'}/etc/standalone-gui.conf not found\n"; exit (1); }
our ($gui_log, $web_port);
require "$conf{'cwaf_path'}/etc/standalone-gui.conf";
my $t_cmd = $conf{'cwaf_path'}.'/scripts/standalone-gui.pl 2>&1 > '.$gui_log.' &';
system($t_cmd);
print "Standalone GUI Server starting on port $web_port\n";
print " Now you can setup SSH tunnel on your client host, for example:\n";
print ' ssh -N -p <ssh_port> ssh_user@ssh.server.host -L <local_port>:localhost:'.$web_port."\n";
print " and connect to GUI by accessing http://localhost:<local_port>/manage/\n";
}
# argument --gui_stop, -gstop
elsif( $opts{'gui_stop'} ) {
unless(-e "$conf{'cwaf_path'}/etc/standalone-gui.conf") { print "Config file $conf{'cwaf_path'}/etc/standalone-gui.conf not found\n"; exit (1); }
our $gui_pid;
require "$conf{'cwaf_path'}/etc/standalone-gui.conf";
my $t_cmd = 'kill -9 $(cat '.$gui_pid.')';
system($t_cmd);
print "Standalone GUI Server stopped\n";
}
elsif( $var ) {
&do_print_help_message;
exit(0);
}
##################################### FUNCTIONS ###################################
# &domain_exist()
# check if domain exists
#
# RETURN: 0 or 1
sub domain_exist($) {
my ($domain) = @_;
my (%dom_hash);
return 1 if ($domain eq 'global');
return 1 if ($force_domain);
my $all_domains = &get_domainlist();
#@dom_hash{@$all_domains} = 0;
map { $dom_hash{$_} = 0; } @$all_domains;
return defined $dom_hash{$domain};
}
# &rules_exist()
# check if rules exists
#
# RETURN: 0 or 1
sub rules_exist() {
return get_local_rules_version();
}
# &help_message()
# print help message
#
# RETURN: none
sub do_print_help_message {
my $sgui_msg = is_standalone_gui() ?
<<SGUI
Standalone GUI Manager (root required):
To start Standalone GUI Server please run $conf{'cwaf_path'}/scripts/standalone-gui.pl and follow instructions
SGUI
: '';
print <<END;
Usage: $0 [arguments]
Arguments:
-h, --help - this help message
-g, --loglevel - set loglevel (1 - 10)
-v, --version - show client version
-l, --domain_list - show list of domains
-f, --force_domain - apply domain even if it not found
Exclude rules:
-d, --domain - set domain for exclude operation (global exclude list if not specified)
-xa, --exclude_add [rule_ID1 rule_ID2...] - add rules to exclude list
-xac, --exclude_add_cat [cat1 cat2...] - add categories to exclude list
-xag, --exclude_add_grp [grp1 grp2...] - add groups to exclude list
-xd, --exclude_del [rule_ID1 rule_ID2...] - remove rules from exclude list
-xdc, --exclude_del_cat [cat1 cat2...] - remove categories from exclude list
-xdg, --exclude_del_grp [grp1 grp2...] - remove groups from exclude list
-xl, --exclude_list - show list of excluded rules
-xlx, --exclude_list_extended - show structured list of excluded rules
-lc, --list_cat - show list of categories
-lg, --list_grp - show list of groups
Disable/enable mod_security for domains:
-dd, --disable_domain [domain1 domain2...] - disable mod_security for domains
-de, --enable_domain [domain1 domain2...] - enable mod_security for domains
-dl, --disabled_list - show list of disabled domains
$sgui_msg
END
}
# &do_exit($return_code)
# log "exit"-message and exit
#
# RETURN: none
sub do_exit($) {
my ($rcode) = @_;
do_console_log("console process finished!");
&fix_permissions();
exit($rcode);
}
# &do_console_log($msg, $level)
# log to console
# log to logfile if $log_flag set
#
# RETURN: none
sub do_console_log($) {
my ($msg) = @_;
print("$msg\n");
}