Viewing File: /usr/local/cpanel/whostmgr/docroot/cgi/addon_hgfirewall.cgi

#!/usr/local/cpanel/3rdparty/bin/perl                                                                                                                                                                                                      
# Source: /usr/local/cpanel/whostmgr/cgi/addon_hgfirewall.cgi
# Date: 10/15/13
# Revision: 2.0
# Author: Matthew Harris
# Hostgator.com LLC
#WHMADDON:hgfire:HG Firewall Administration:hgfire.gif

use lib '/usr/local/cpanel';
use strict;
use warnings;
use Whostmgr::ACLS();
use Whostmgr::HTMLInterface;
use File::Copy;
use File::Basename;
use Carp;
use CGI;
use CGI::Carp qw(warningsToBrowser fatalsToBrowser);

# Because its how the internet works
print "Content-type: text/html\n\n";

# Check if the user has root
Whostmgr::ACLS::init_acls();
if ( !Whostmgr::ACLS::checkacl('all') ) {
    print 'Access Denied.';
    exit;
}

# Subroutine definitions
sub error_check;      # Shared check, Firewall installed, Etc
sub open_file;        # Open files
sub append_file;      # Append to file
sub write_file;       # Write a file
sub draw_forms;       # Draw data tables
sub draw_controls;    # Draw controls
sub modify_file;      # Remove line from file
sub handle_ajax;      # Ajax all the things

# Handle ajax calls first
handle_ajax;

# Define the header
Whostmgr::HTMLInterface::defheader("HG Firewall Administration");

# Load CSS for x theme
print
qq{<link href="/cPanel_magic_revision_1314914829/themes/x/style_optimized.css" type="text/css" rel="stylesheet">};
print
qq{<link href="//code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" type="text/css" rel="stylesheet">};

# Inline CSS to keep it all in one file
print <<'CSS';
<style>
h1{margin-left:10px; display:inline;}
#help_firewall{margin-left:5px; color:#5388F2;}
p{display:inline;}
.left{float:left; width:30%; margin-left:30px !important;}
.cbox{float:left; width:30%; margin-left:30px !important; margin-bottom:20px; margin-top:20px;}
.row:hover{background-color:#FFB2B2;}
#custom_rules{clear:both;}
#manage_div{width:95%; margin:0 auto;}
#control_box{width:100%; margin:0 auto; clear:both;}
</style>
CSS

# Start Draw
error_check;
draw_controls;
draw_forms;

# JS @ the bottom
print <<'JS';
<script src="//code.jquery.com/jquery-1.10.1.min.js"></script>
<script src="//code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
<script src="//ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.4/jquery.dataTables.min.js"></script>
<script src="//cdn.jsdelivr.net/noty/2.1.0/jquery.noty.js"></script>
<script src="//cdn.jsdelivr.net/noty/2.1.0/layouts/top.js"></script>
<script src="//cdn.jsdelivr.net/noty/2.1.0/themes/default.js"></script>

<script type="text/javascript">
$(document).ready(function() {

        function ajaxRequest(query, callback) {
                $.ajax({
                        type: "POST",
                        data: query,
                        url: "addon_hgfirewall.cgi",
                        success: callback,
                });
        }

        function successMsg(msg) {
                var n = noty({
                        text: msg,
                        type: 'success',
                        layout: 'top',
                        timeout: '2000',
                });
        }

	function failureMsg(msg) {
		var n = noty({
			text: msg,
			type: 'error',
			layout: 'top',
			timeout: '2000',
		});
	}

	// Load Data Tables
        var cr = $("#custom_rules").dataTable({
                "oLanguage": {
                        "sEmptyTable": "No custom rules setup.",
                },
                "bInfo": false,
                "bFilter": false,
                "bPaginate": false,
                "fnInitComplete": function() {
                        $(this).on('dblclick', '.row', rm_cr);
                },
		"fnRowCallback": function(nRow, aData, iDisplayIndex, iDisplayIndexFull) {
			$('td:eq(0)', nRow).addClass('row');
		},
        });

	var i_p = $("#in_ports").dataTable({
                "oLanguage": {
                        "sEmptyTable": "No inbound ports.",
                },
                "bInfo": false,
                "bFilter": false,
                "bPaginate": false,
                "fnInitComplete": function() {
                        $(this).on('dblclick', '.row', rm_ip);
                },
                "fnRowCallback": function(nRow, aData, iDisplayIndex, iDisplayIndexFull) {
                        $('td:eq(0)', nRow).addClass('row');
                },
	});

	var o_p = $("#out_ports").dataTable({
                "oLanguage": {
                        "sEmptyTable": "No inbound ports.",
                },
                "bInfo": false,
                "bFilter": false,
                "bPaginate": false,
                "fnInitComplete": function() {
                        $(this).on('dblclick', '.row', rm_op);
                },
                "fnRowCallback": function(nRow, aData, iDisplayIndex, iDisplayIndexFull) {
                        $('td:eq(0)', nRow).addClass('row');
                },
        });

	var bip = $("#ips").dataTable({
                "oLanguage": {
                        "sEmptyTable": "No blocked IPs.",
                },
                "bInfo": false,
                "bFilter": false,
                "bPaginate": false,
                "fnInitComplete": function() {
                        $(this).on('dblclick', '.row', rm_bip);
                },
                "fnRowCallback": function(nRow, aData, iDisplayIndex, iDisplayIndexFull) {
                        $('td:eq(0)', nRow).addClass('row');
                },
        });


	function rm_cr() {
                var line = $('p', this).text();
	        var query = "func=remove_rule&line=" + line;
        	ajaxRequest(query, function(data) {
			successMsg("Custom Rule Removed!");
	        });
		var row = $(this).closest('tr')[0];
		cr.fnDeleteRow(row);
	}

	function rm_ip() {
                var line = $('p', this).text();
	        var query = "func=remove_iport&line=" + line;
        	ajaxRequest(query, function(data) {
	        	successMsg("Closed port: " + line);
		});
		var row = $(this).closest('tr')[0];
		i_p.fnDeleteRow(row);	
	}

	function rm_op() {
     		var line = $('p', this).text();
                var query = "func=remove_oport&line=" + line;
	        ajaxRequest(query, function(data) {
			successMsg("Closed port: " + line);
                });
		var row = $(this).closest('tr')[0];
                o_p.fnDeleteRow(row);
	}

	function rm_bip() {
                var line = $('p', this).text();
	        var query = "func=remove_ip&line=" + line;
        	ajaxRequest(query, function(data) {
			successMsg("Unblocked IP: " + line); 
	        });
		var row = $(this).closest('tr')[0];
		bip.fnDeleteRow(row);
	}

	function validateIP(ip) {
		// ipv4
		if(/^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/.test(ip)) {
			return true;
		// ipv4 cidr
		} else if (/^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(\d|[1-2]\d|3[0-2]))$/.test(ip)) {
			return true;
		// ipv6
		} else if (/^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*/.test(ip)) {
			return true;
		} else {
			return false;
		}
	}

	function validatePort(port) {
		if(/^\d{1,6}$/.test(port)) {
			return true;
		} else {
			return false;
		}
	}

	function validateRule(rule) {
		if(/^iptables/.test(rule)) {
			return rule;
		} else {
			rule = "iptables " + rule;
			return rule;
		}
	}

	$("#help_firewall").tooltip({
		show: { effect: "slideDown", delay: 250 },
		content: '<div id="instruction_box"><strong>Remove Item:</strong> Double-click Item<br/><strong>Add Item:</strong> Use Forms Below<br/><br/><em>Backups are created for each change.</em><br/><strong>Restore Backup:</strong> Contact Support<br/><br/><strong><em>In order for changes to take affect you must restart the firewall</em></strong></div>',
		items: "span",
	});

	$("#add_ip").click(function() {
		var ip = $("#in_ip").val();
		if ( ip ) {
			if ( validateIP(ip) ) {
				var query = "func=add_ip&line=" + ip;
				ajaxRequest(query, function(data) {
					cr.fnAddData([
						'iptables -I INPUT -s ' + ip + ' -j ACCEPT',
					]);
					successMsg("Whitelisted IP: " + ip);
					$("#in_ip").val('');
				});
			} else {
				failureMsg("Please enter a valid IP or range.");
			}
		} else {
			failureMsg("Missing required field");
		}
	});

	$("#block_ip").click(function() {
		var ip = $("#b_ip").val();
		if ( ip ) {
			if ( validateIP(ip) ) {
				var query = "func=block_ip&line=" + ip;
				ajaxRequest(query, function(data) {
                        	        bip.fnAddData([
						'<p>' + ip + '</p>',
        	                        ]);
					successMsg("Blacklisted IP: " + ip);
					$("#b_ip").val('');
				});
			} else {
				failureMsg("Please enter a valid IP or range.");
			}
		} else {
			failureMsg("Missing required field");
		}
	});

	$("#add_rule").click(function() {
		var rule = $("#add_custom_rule").val();
		if ( rule ) {
			rule = validateRule(rule);
			var query = "func=add_rule&line=" + rule;
                        ajaxRequest(query, function(data) {
                                cr.fnAddData([
                                        rule,
                                ]);
				successMsg("Added Rule: " + rule);
				$("#add_custom_rule").val('');
			});
		} else {
			failureMsg("Missing required field");
		}
	});

	$("#open_out_port").click(function() {
		var port = $("#open_outbound").val();
		if ( port ) {
			if ( validatePort(port) ) {
				var query = "func=open_out_port&line=" + port;
        	                ajaxRequest(query, function(data) {
                	                o_p.fnAddData([
                        	                '<p>' + port + '</p>',
	                                ]);
					successMsg("Opened Port " + port + " Outbound");
					$("#open_outbound").val('');
				});
			} else {
				failureMsg("Please enter a valid port.");
			}
		} else {
			failureMsg("Missing required field");
		}
	});

	$("#open_in_port").click(function() {
		var port = $("#open_inbound").val();
		if ( port ) {
			if ( validatePort(port) ) {
				var query = "func=open_in_port&line=" + port;
				ajaxRequest(query, function(data) {
					i_p.fnAddData([
                        	                '<p>' + port + '</p>',
                                	]);
					successMsg("Opened Port " + port + " Inbound");
					$("#open_inbound").val('');
				});
			} else {
				failureMsg("Please enter a valid port.");
			}
		} else {
			failureMsg("Missing required field");
		}
	});

	$("#stop_firewall").click(function() {
		var query = "func=stop_firewall";
		ajaxRequest(query, function(data) {
			successMsg("Stopped Firewall");
		});
	});

	$("#start_firewall").click(function() {
		var query = "func=start_firewall";
		ajaxRequest(query, function(date) {
			successMsg("Started Firewall");
		});
	});

	$("#restart_firewall").click(function() {
		var query = "func=restart_firewall";
		ajaxRequest(query, function(data) {
			successMsg("Restarted Firewall");
		});
	});

});
</script>
JS

Whostmgr::HTMLInterface::deffooter();

# Subroutine Bodies
sub error_check {
    croak "This is a shared server."      if -f '/admin/hgbackupdir/restore.pl';
    croak "This is an old shared server." if -f '/home/hgbackupdir/restore.pl';
    croak "Firewall not installed."       if !-f '/usr/sbin/firewall';
}

sub open_file {
    my $file = shift;
    open my $fh, '<', $file or croak "Cannot read file : $file";
    my @contents = <$fh>;
    close $fh;
    return \@contents;
}

sub append_file {
    my ( $file, $line ) = @_;
    open my $fh, '>>', $file or croak "Cannot append to file : $file";
    print {$fh} "$line\n";
    close $fh;
}

sub write_file {
    my ( $file, @content ) = @_;
    open my $fh, '>', $file or croak "Cannot write file: $file";
    print {$fh} @content;
    close $fh;
}

sub backup_file {
    my $file = shift;
    my $date = time();
    my $base = basename($file);
    mkdir '/etc/firewall/whm-backup', 0755 if !-d '/etc/firewall/whm-backup';
    copy( $file, "/etc/firewall/whm-backup/$base-$date" );
}

sub draw_forms {

    # START Custom Rules Table
    my $content = open_file('/etc/firewall/INCLUDE');
    print
qq{<table border=1 class="datatable brick" id="custom_rules" align=center width=90% cellpadding=5>};
    print qq{<thead><tr><th>Custom Firewall Rules</th></tr></thead>};
    print qq{<tbody>};

    # Print rows of iptables rules from /etc/firewall/INCLUDE
    foreach my $line (@$content) {
        if ( $line !~ /^#/ and $line !~ /^$/ and $line ) {
            print qq{<tr>};
            print qq{<td class="row"><p>$line</p></td>};
            print qq{</tr>};
        }
    }
    print qq{</tbody>};
    print qq{</table>};

    # END Custom Rules Table

    print qq{<br/><br/>};

    print qq{<div id="manage_div">};

    # START Port Management

    # INBOUND
    $content = open_file('/etc/firewall/INPUT');
    print
qq{<table border=1 class="datatable brick left" id="in_ports" align=center border=0 cellpadding=5>};
    print qq{<thead><th>Inbound Ports</th></thead>};
    print qq{<tbody>};
    foreach my $line (@$content) {
        if ( $line !~ /^#/ and $line !~ /^$/ ) {
            print qq{<tr><td><p>$line</p></td></tr>};
        }
    }
    print qq{</tbody>};
    print qq{</table>};

    # OUTBOUND
    $content = open_file('/etc/firewall/OUTPUT');
    print
qq{<table border=1 class="datatable brick left" id="out_ports" align=center border=0 cellpadding=5>};
    print qq{<thead><th>Outbound Ports</th></thead>};
    print qq{<tbody>};
    foreach my $line (@$content) {
        if ( $line !~ /^#/ and $line !~ /^$/ ) {
            print qq{<tr><td><p>$line</p></td></tr>};
        }
    }
    print qq{</tbody>};
    print qq{</table>};

    # END Port Management

    # BLOCKED IPS
    my $file = '/etc/firewall/IPDROP_GLOBAL';
    $file = '/etc/firewall/GLOBAL_DROP' if ! -f $file;
    $content = open_file($file);
    print
qq{<table border=1 class="datatable brick left" id="ips" align=center border=0 cellpadding=5>};
    print qq{<thead><th>Blocked IP's</th></thead>};
    print qq{<tbody>};
    foreach my $line (@$content) {
        if ( $line !~ /^#/ and $line !~ /^$/ ) {
            print qq{<tr><td><p>$line</p></td></tr>};
        }
    }
    print qq{</tbody>};
    print qq{</table>};

    # END IP Management
    print qq{</div>};
}

sub draw_controls {
    print qq{<div id="control_box">};
    print qq{<div class="cbox">};
    print qq{<p><em>Add a custom iptables rule to a chain.</em></p><br/>};
    print
qq{<input type="text" id="add_custom_rule"/><input type="submit" id="add_rule" value="Add Rule"/>};
    print qq{</div>};
    print qq{<div class="cbox">};
    print qq{<p><em>Open an outbound port.</em></p><br/>};
    print
qq{<input type="text" id="open_outbound"/><input type="submit" id="open_out_port" value="Open Port"/>};
    print qq{</div>};
    print qq{<div class="cbox">};
    print qq{<p><em>Open an inbound port.</em></p><br/>};
    print
qq{<input type="text" id="open_inbound"/><input type="submit" id="open_in_port" value="Open Port"/>};
    print qq{</div>};
    print qq{<div class="cbox">};
    print qq{<p><em>Whitelist an IP address.</em></p><br/>};
    print
qq{<input type="text" id="in_ip"/><input type="submit" id="add_ip" value="Add IP"/>};
    print qq{</div>};
    print qq{<div class="cbox">};
    print qq{<p><em>Blacklist an IP address.</em></p><br/>};
    print
qq{<input type="text" id="b_ip"/><input type="submit" id="block_ip" value="Block IP"/>};
    print qq{</div>};
    print qq{<div class="cbox">};
    print qq{<p><em>Manage Firewall Service</em></p><br/>};
    print qq{<input type="submit" id="stop_firewall" value="Stop"/>};
    print qq{<input type="submit" id="start_firewall" value="Start"/>};
    print qq{<input type="submit" id="restart_firewall" value="Restart"/>};
    print qq{</div>};
    print qq{</div>};
}

sub modify_file {
    my ( $file, $line ) = @_;
    my $content = open_file($file);
    backup_file($file);
    my @new_content;
    print "Line: '$line'\n";
    foreach (@$content) {
        chomp;
        print "SL: '$_'\n";
        if ( $_ ne $line ) {
            push @new_content, "$_\n";
        }
    }
    write_file( $file, @new_content );
}

sub handle_ajax {

    # Handle AJAX
    my $cgi        = CGI->new;
    my $num_params = $cgi->param();
    if ($num_params) {
        my $function = $cgi->param('func');
        my $line     = $cgi->param('line');
        chomp($line);
        print "\n\n\nFUNCTION: $function\nLINE: $line\n\n\n";
        if($function eq "remove_rule") {
            modify_file( '/etc/firewall/INCLUDE', $line );
        } elsif($function eq "remove_iport") {
            modify_file( '/etc/firewall/INPUT', $line );
        } elsif($function eq "remove_oport") {
            modify_file( '/etc/firewall/OUTPUT', $line );
        } elsif($function eq "remove_ip") {
            my $file = '/etc/firewall/IPDROP_GLOBAL';
            $file = '/etc/firewall/GLOBAL_DROP' if ! -f $file;
            modify_file( $file, $line );
        } elsif($function eq "add_rule") {
            append_file( '/etc/firewall/INCLUDE', $line );
        } elsif($function eq "open_out_port") {
            append_file( '/etc/firewall/OUTPUT', $line );
        } elsif($function eq "open_in_port") {
            append_file( '/etc/firewall/INPUT', $line );
        } elsif($function eq "add_ip") {
            $line = "iptables -I INPUT -s $line -j ACCEPT\n";
            append_file( '/etc/firewall/INCLUDE', $line );
        } elsif($function eq "block_ip") {
            my $file = '/etc/firewall/IPDROP_GLOBAL';
            $file = '/etc/firewall/GLOBAL_DROP' if ! -f $file;
            append_file( $file, $line );
        } elsif($function eq "stop_firewall") {
            system("/etc/init.d/firewall stop > /dev/null");
        } elsif($function eq "start_firewall") {
            system("/etc/init.d/firewall start > /dev/null");
        } elsif($function eq "restart_firewall") {
            system("/etc/init.d/firewall restart > /dev/null");
        }
        exit;
    }
}
Back to Directory File Manager