#!/usr/bin/perl ############################################################################### # # 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 2 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, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # ############################################################################### # # psa-pci.pl # ############################################################################### use DBI; use File::Copy; ############################################################################### # # Change the folllowing variables to fit your system # ############################################################################### my $extif = `ip route show | grep ^default | awk '{print \$5}'`; chomp($extif); my @ips = `ip -4 addr show $extif`; my @ssl_ports = qw(443 993 995 8443); my $backupdir = "plesk_pci_backup"; my $httpd_dir = "/etc/httpd/conf.d"; my $courier_dir = "/etc/courier-imap"; my $psaconf = "/etc/psa/psa.conf"; my $psa_dir = "/usr/local/psa/admin/conf"; my $psa_stop = "/etc/init.d/psa stopall"; my $psa_start = "/etc/init.d/psa start"; my $websrvmng = "/usr/local/psa/admin/bin/websrvmng -av"; my $httpd_conf = "/etc/httpd/conf/httpd.conf"; my $sysctl = "/etc/sysctl.conf"; my $db_user = "admin"; open(PSA_SHADOW,"/etc/psa/.psa.shadow"); my $db_pass = ; chomp($db_pass); my $dsn = "DBI:mysql:psa:localhost"; my $dbh = DBI->connect($dsn,$db_user,$db_pass); my $ip; my $hostname; my $oldconf; my $answer; my $certfile; my $psa_vhosts; open(PSA_CONF,$psaconf); while () { if(/^HTTPD_VHOSTS/) { my @conf = split(/ /); $psa_vhosts=$conf[1]; chomp($psa_vhosts); }; }; close(PSA_CONF); ############################################################################### # # Backup configuration files # ############################################################################### print "Creating backup directory\n"; mkdir("$backupdir"); print "Backing up configuration files\n"; copy("$httpd_conf","$backupdir/httpd.conf"); copy("$courier_dir/imapd-ssl","$backupdir/imapd-ssl"); copy("$courier_dir/pop3d-ssl","$backupdir/pop3d-ssl"); copy("$courier_dir/imapd","$backupdir/imapd"); copy("$courier_dir/pop3d","$backupdir/pop3d"); copy("$sysctl","$backupdir/sysctl.conf"); ############################################################################### # # disable TRACE/TRACK for http virtual hosts # ############################################################################### print "Disable TRACE and TRACK methods for http? (y/n): "; chomp($answer = ); if($answer =~ /^y/i) { open(PSA_TRACE,">$httpd_dir/zz000-psa-disable-trace-track.conf"); print PSA_TRACE "\n"; print PSA_TRACE < RewriteEngine on RewriteCond %{REQUEST_METHOD} !^(GET|POST|HEAD)\$ RewriteRule .* - [F] EOF close(PSA_TRACE); ############################################################################### # # disable TRACE/TRACK for SSL virtual hosts # ############################################################################### open(PSA_TRACE_SSL,">$httpd_dir/zz001-psa-disable-trace-track-ssl.conf"); foreach(@ips) { if(/((\d{1,3}\.){3}(\d){1,3})/) { $ip = $&; $hostname = $ip; $hostname =~ s/\./-/g; my $sth = $dbh->prepare("SELECT cert_file,ca_file FROM certificates WHERE id = (SELECT ssl_certificate_id FROM IP_Addresses WHERE ip_address='$ip')"); $sth->execute(); my @row = $sth->fetchrow(); my $certfile=$row[0]; my $cafile=$row[1]; chomp($ip); print PSA_TRACE_SSL "\n"; print PSA_TRACE_SSL < RewriteEngine on RewriteCond %{REQUEST_METHOD} !^(GET|POST|HEAD)\$ RewriteRule .* - [F] AllowOverride None Options None Order allow,deny Allow from all SSLRequireSSL EOF }; }; close(PSA_SHADOW); close(PSA_TRACE_SSL); $dbh->disconnect; }; ############################################################################### # # Apache # # Security through obscurity # Changes ServerTokens OS/Min/Full to ServerTokens Prod # ############################################################################### print "Set ServerTokens Prod? (y/n): "; chomp($answer = ); if($answer =~ /^y/i) { open(CONF,"+<$httpd_conf"); while() { s/ServerTokens (OS|Min|Full)/ServerTokens Prod/ig; $oldconf .= $_; }; seek(CONF,0,0); print CONF $oldconf; truncate(CONF,tell(CONF)); undef $oldconf; close(CONF); }; ############################################################################### # # Disable userdir # ############################################################################### print "Disable UserDir? (y/n): "; chomp($answer = ); if($answer =~ /^y/i) { open(CONF,"+<$httpd_conf"); while() { s/UserDir [a-zA-Z].+$/UserDir disabled/ig; $oldconf .= $_; }; seek(CONF,0,0); print CONF $oldconf; truncate(CONF,tell(CONF)); undef $oldconf; close(CONF); }; ############################################################################### # # Disable weak SSL ciphers # ############################################################################### print "Disable weak SSL ciphers in Apache? (y/n): "; chomp($answer = ); if($answer =~ /^y/i) { open(PSA_WEAK_SSL,">$httpd_dir/zz050-psa-disable-weak-ssl-ciphers.conf"); print PSA_WEAK_SSL <); if($answer =~ /^y/i) { # create the custom include file open(PSA_CUSTOM,">$psa_dir/httpsd.custom.include"); # disable UserDir and weak SSL ciphers print PSA_CUSTOM <\n"; print PSA_CUSTOM < EOF print PSA_CUSTOM "\n"; print PSA_CUSTOM < EOF close(PSA_CUSTOM); }; ############################################################################### # # courier # # disable weak SSL ciphers # ############################################################################### print "Disable weak SSL ciphers for IMAP/POP3? (y/n): "; chomp($answer = ); if($answer =~ /^y/i) { open(COURIER_IMAP,">>$courier_dir/imapd-ssl"); open(COURIER_POP3,">>$courier_dir/pop3d-ssl"); print COURIER_IMAP "TLS_CIPHER_LIST=\"HIGH:MEDIUM:!SSLv2:!LOW:!EXP:!aNULL:\@STRENGTH\"\n"; print COURIER_POP3 "TLS_CIPHER_LIST=\"HIGH:MEDIUM:!SSLv2:!LOW:!EXP:!aNULL:\@STRENGTH\"\n"; close(COURIER_IMAP); close(COURIER_POP3); }; ############################################################################### # # disable plain text authentication # ############################################################################### print "Disable plain text authentication for IMAP/POP3? (y/n): "; chomp($answer = ); if($answer =~ /^y/i) { print "Disabling plain text authentication may break authentication for legacy clients (Outlook/Outlook Express)\n"; open(CONF,"+<$courier_dir/imapd"); while() { s/IMAP_CAPABILITY=\"IMAP4rev1 UIDPLUS CHILDREN NAMESPACE THREAD=ORDEREDSUBJECT THREAD=REFERENCES SORT QUOTA AUTH=CRAM-MD5 AUTH=CRAM-SHA1 AUTH=PLAIN IDLE\"/IMAP_CAPABILITY=\"IMAP4rev1 UIDPLUS CHILDREN NAMESPACE THREAD=ORDEREDSUBJECT THREAD=REFERENCES SORT QUOTA AUTH=CRAM-MD5 AUTH=CRAM-SHA1 IDLE\"/ig; $oldconf .= $_; }; seek(CONF,0,0); print CONF $oldconf; truncate(CONF,tell(CONF)); undef $oldconf; close(CONF); open(CONF,"+<$courier_dir/pop3d"); while() { s/POP3AUTH=\"LOGIN CRAM-MD5 CRAM-SHA1\"/POP3AUTH=\"CRAM-MD5 CRAM-SHA1\"/ig; $oldconf .= $_; }; seek(CONF,0,0); print CONF $oldconf; truncate(CONF,tell(CONF)); undef $oldconf; close(CONF); }; ############################################################################### # # OS fingerprinting # ############################################################################### print "Disable OS fingerprinting? (y/n): "; chomp($answer = ); if($answer =~ /^y/i) { system("iptables -N OSFP"); system("iptables -A OSFP -i $extif -p icmp --icmp-type redirect -j DROP"); system("iptables -A OSFP -i $extif -p icmp --icmp-type timestamp-request -j DROP"); system("iptables -A OSFP -i $extif -p icmp --icmp-type timestamp-reply -j DROP"); system("iptables -A OSFP -i $extif -p icmp --icmp-type address-mask-request -j DROP"); system("iptables -A OSFP -i $extif -p icmp --icmp-type address-mask-reply -j DROP"); system("iptables -A OSFP -j RETURN"); system("iptables -I INPUT 1 -j OSFP"); open(SYSCTL,">>$sysctl"); print SYSCTL "net.ipv4.tcp_timestamps = 0"; system("sysctl -q -p"); close(SYSCTL); }; ############################################################################### # # Restart Plesk # ############################################################################### print "Modifications complete. Restart Plesk? (y/n): "; chomp($answer = ); if($answer =~ /^y/i) { system("$websrvmng"); system("$psa_stop"); system("$psa_start"); }; ############################################################################### # # Tests # ############################################################################### print "Run tests? (y/n): "; chomp($answer = ); if($answer =~ /^y/i) { use IO::Socket::SSL; use LWP; my $ua = LWP::UserAgent->new; $ua->agent("PleskPCI/1.0 "); my $req; my $res; my $client; foreach(@ips) { if(/((\d{1,3}\.){3}(\d){1,3})/) { $ip = $&; chomp($ip); # Test Apache TRACE/TRACK $req = HTTP::Request->new(TRACE => "http://$ip"); $res = $ua->request($req); if($res->status_line =~ /403/) { print "$ip HTTP TRACE/TRACK disabled\n"; }; $req = HTTP::Request->new(TRACE => "https://$ip"); $res = $ua->request($req); if($res->status_line =~ /403/) { print "$ip HTTPS TRACE/TRACK disabled.\n"; }; # Test weak SSL ciphers and SSLv2 foreach(@ssl_ports) { $port = $_; $client = IO::Socket::SSL->new(PeerAddr => "$ip", PeerPort => "$port", SSL_version => "SSLv2"); if(!$client) { print "$ip:$port SSLv2 disabled.\n"; }; }; foreach(@ssl_ports) { $port = $_; $client = IO::Socket::SSL->new(PeerAddr => "$ip", PeerPort => "$port", SSL_cipher_list => "'LOW:EXP'", SSL_version => "SSLv3"); if(!$client) { print "$ip:$port: Weak SSL ciphers disabled.\n"; }; }; }; }; }; exit;