This guide consists of two parts related to securing your WDMyCloud NAS, first is to harden your root account and second is to prevent unauthorized access using my universal “hostsdeny.pl” Perl script which can also be used on other platforms. To use this “hostsdeny.pl” Perl script on platforms other than WDMyCloud, please refer to the script comments itself. I’ve tried to put it as simple as possible especially for beginners who’s new to WDMyCloud.
Disclaimer: As I’m frequently updating the original guides and installers here on TeaNazaR.com, I will not be responsible for any brick issues if you were to follow my obsolete guides copied elsewhere. Thus subscribe to this post to get latest updates. Modifying any part of a device may void its warranty.
1) Hardening root account
First part, ok so now you have open up SSH (port 22) and FTP (port 21) in your router to allow incoming access to the WDMyCloud from the internet. Many have overlooked the security issues when you do this. It will allow possible brute force bots attacks from anywhere in the world. If you were to monitor the incoming request especially to the SSH port 22 alone, you’ll be shocked to see lots of invalid logins from
tail -f /var/log/sshd.log some of which were excessively just over a short period. If you have a weak root password (e.g. the default WDMyCloud root password), then it could be compromised in no time.
Possible ways to secure your root access
Don’t forget to change the WDMyCloud default root password! Login as root@wdmycloud using i.e. PuTTY, then change the password:
1 2 3 4 | root@wdmycloud:~# passwd Enter new UNIX password: Retype new UNIX password: passwd: password updated successfully |
Disable root login with password! This will prevent the common password dictionary attacks. First and uttermost importantly, create a new admin user or use any preferred existing user in the WDMyCloud Dashboard, set the user to admin group if it’s not already an admin usermod -aG administrators username with a password-less sudo access echo "username ALL=(ALL) NOPASSWD: ALL">>/etc/sudoers. This admin user serve as a backup account to gain super user access incase you lose your RSA/DSA key thus losing the root access. Before you proceed further, please confirm that this admin account works. SSH login using this admin account and switch to root sudo su. If it’s good, then disable the root login using password in nano /etc/ssh/sshd_config:
1 2 3 4 5 6 7 | # Authentication: LoginGraceTime 120 # PermitRootLogin yes (comment out & add below) PermitRootLogin without-password # StrictModes yes (comment out & add below) StrictModes no # AllowUsers root (comment out) |
After saving the file (“CTRL+x” to exit and “y” to save), reload SSHD service service ssh reload.
Only allow root login with RSA/DSA key! Create a root RSA/DSA private/public key pairs to login without password (leave passphrase empty):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | root@wdmycloud:~# mkdir -pm700 /root/.ssh root@wdmycloud:~# ssh-keygen -t rsa Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /root/.ssh/id_rsa. Your public key has been saved in /root/.ssh/id_rsa.pub. The key fingerprint is: 6e:09:19:ca:6b:8b:d5:b9:3a:05:21:b7:e4:20:b2:b4 root@wdmycloudThe key's randomart image is: +--[ RSA 2048]----+ | | |o.o + | |oo.* o. | |.E .+. o | | o.o S | | o.+ . | | +.o + | | +.. o | | . oo. | +-----------------+ root@wdmycloud:~# cat /root/.ssh/id_rsa.pub|echo>/root/.ssh/authorized_keys root@wdmycloud:~# chmod 640 /root/.ssh/authorized_keys |
Copy the generated private key /root/.ssh/id_rsa to your PC or mobile device. You’ll need this key to login as root without password as you can no longer login remotely as root with password. To login remotely using this key from a normal CLI, ssh -i/path/id_rsa root@wdmycloud.your.isp.ip.or.domain. How to remember your dynamic ISP address? Checkout the first part of my post in WebHosting on WDMyCloud V4 Firmwares. You can also convert this key to a PuTTY Private Key Files (*.ppk) and load it into the PuTTY connection options “Connection->SSH->AUTH”.
2) Prevent Unauthorized Access
Second part, ok so you have secured your root access, what about the rest of the SSH/FTP accounts? What I’m presenting here was actually used long time ago when I was running SSHD on Windows via Cygwin before I had the WDMyCloud NAS. Similar issues I had with Cygwin (POSIX limitation), WDMyCloud doesn’t allow the use of iptables (WD didn’t include in the kernel) which with it you can then install some ready made daemons such as Fail2ban. So I had to resort to use the built-in Windows Firewall back then and here in this case, the tcp-wrapper access control from the
/etc/hosts.deny file. To make this access control automated, I’ve modified my old Perl script to work with both Cygwin and WDMyCloud. In fact it should work with any standard Linux/Cygwin distribution.
The setup is fairly simple. Copy the “hostsdeny.pl” Perl script found at the bottom of this post to any path on your WDMyCloud, give it an executable permission chmod 755 /shares/scripts/hostsdeny.pl. Add below to a new line in your crontab to run at boot crontab -e:
1 | @reboot nice -n19 /shares/scripts/hostsdeny.pl>/dev/null 2>&1 |
Then “CTRL+x” to exit and “y” to save. You can change the nice priority accordingly, nice -n19 as the lowest and nice -n-20 as the highest (not recommended) or just omit the nice command to run at normal priority.
Now run the script manually once, issue /shares/scripts/hostsdeny.pl and it will re-launch itself and run in background . If you’ve made changes to the script, just re-run it again and the previous process will get terminated. To stop the script manually, issue killall hostsdeny.pl.
If you want the script to protect your FTP access as well, you’ll need to modify the vsftpd configuration to allow tcp-wrapper usage . Issue echo "tcp_wrappers=YES">>/etc/vsftpd.conf then reload the vsftpd service service vsftpd reload.
Next is optional but I would highly recommend that you have it done for the sake of convenience in the future. This is for tackling any event that you got yourself remotely locked out from the WDMyCloud when the IP from your remote location gets denied. To regain access, you need to run this “hostsdeny.pl” Perl script in a webserver hosted on the same system, in this case Apache2 which is the preinstalled webserver in WDMyCloud. This requires modifications to the WDMyCloud Apache2 webserver. But if you’re not comfortable doing this yourself, you can also perform the automated install of Nginx from my post WebHosting on WDMyCloud V4 Firmwares and then symlink the “hostsdeny.pl” Perl script to your installed Nginx’s webroot path. Symlink sample is stated at the last point. Note, if you have installed Nginx from my “WebHosting Mods” installer post link above, do not modify Apache2’s configurations as below because it’s using a different incompatible “MPM_Event” module! Instead just follow the part where it doesn’t involves Apache2 changes.
Modifications to WDMyCloud WebServer to enable Perl CGI
Open TCP port 80 on your router to the WDMyCloud, similarly you would have done for SSH port 22 and FTP port 21. This depends on your router, search online on how to do it for your specific router, if necessary. If you’re lucky, the port 80 maybe already selected and opened in the WDMyCloud Dashboard. For Nginx, note the port route stated in my guide was Router:80 -> WDMyCloud:5080.
For Apache2, create a new CGI config file nano /etc/apache2/conf.d/cgi.conf then copy & paste below:
1 2 3 4 5 6 7 8 9 | <IfModule mod_cgi.c> AddHandler cgi-script .cgi .pl <Files ~ "\.pl$"> Options +ExecCGI </Files> <Files ~ "\.cgi$"> Options +ExecCGI </Files> </IfModule> |
Then “CTRL+x” to exit and “y” to save.
Enable the Apache2 CGI module, issue a2enmod cgi then reload Apache2 server daemon, issue service apache2 reload. Remember once again to skip the above if you already have Nginx installed from my installer.
It’s recommended to hide the script from public, best password protected, path e.g. here ./private/. This method is also applicable if you had installed Nginx from my installer except the default webroot for Nginx is cd /var/www/html/ and Apache is cd /var/www/htdocs/. After changing to the appropriate webroot path above, now create a new ./private/ folder in the webroot, issue mkdir -pm755 ./private/. Then password protect the path for user e.g. “username”, issue htpasswd -c ./private/.htpasswd username and enter a “password”, enter again to confirm.
Next is to enable the access control for this path.
For Apache2, create a new “.htaccess” file, nano /var/www/htdocs/private/.htaccess then copy & paste below:
1 2 3 4 | AuthType Basic AuthName "Authentication Required" AuthUserFile /var/www/htdocs/private/.htpasswd Require valid-user |
For Nginx, instead of “.htaccess” file, add below to your server directive config i.e. nano /etc/nginx/sites-enabled/default:
1 2 | auth_basic "Authentication Required"; auth_basic_user_file /var/www/html/private/.htpasswd; |
Then reload Nginx server daemon, issue service nginx reload. For either above, remember to “CTRL+x” to exit and “y” to save.
And finally, symlink the original “hostsdeny.pl” Perl script you had created earlier to the webserver’s webroot, issue ln -s /shares/scripts/hostsdeny.pl ./private/.
With the above changes made to your WDMyCloud webserver and at anytime you got yourself locked out due to wrong password entered several times, you can simply perform a self unlock by accessing your webserver e.g. http://wdmycloud.your.isp.ip.or.domain/private/hostsdeny.pl then login using the “username” and “password” you had created earlier. This will clear the ban on all your current location remote or proxy IPs. This simple text page will also display your current settings and blocked list.
You’ll be convinced that your WDMyCloud is well protected when you see lots of unique IP address masks gets blocked in just a few days.
Enjoy (-:
hostsdeny.pl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 | #!/usr/bin/perl use strict; use warnings; print CGI(q{ # hostsdeny.pl v1.2 by Nazar78 @ TeaNazaR.com ############################################# # Simple script to receive SSHD Windows event task scheduler or monitor Unix logs # which adds "unknown user" IP submask and brute force attempts to tcp-wrapper host file # /etc/hosts.deny and/or Windows firewall ports 0-79,81-65535 except 80 for self unlock. # Run this script in a webserver port 80 as a CGI if you got locked out remotely. # It's highly recommended to make this script not accessible to public: # e.g. http://my.dns.com/pass-protected-path/hostsdeny.pl # This will clear all your current remote/proxy IPs. Or add comma separated IPs: # e.g. http://my.dns.com/pass-protected-path/hostsdeny.pl?unlock=127.0.0.1,127.0.0.2 # # Requirements: # - Windows administrator or Unix root access. # - Windows Firewall is active to block TCP ports 0-79,81-65535. # - Unix with tcp-wrapper to block all services in /etc/hosts.deny. # - ActivePerl for Windows, Cygwin based Perl or Unix based Perl. # - Optional IIS/Apache/Nginx with Perl-CGI on port 80 for remote self unlock mechanism. # - Unix vsftpd requires tcp_wrappers=YES and xferlog_enable=YES in /etc/vsftpd.conf. # - Unix user running Perl-CGI i.e. 'www-data' needs sudo access for self unlock i.e.: # /etc/sudoers:www-data ALL=(ALL) NOPASSWD: ALL # # Installations: # Place the script anywhere i.e. Windows:"C:\scripts\" or Unix:"/root/scripts/". # Windows - Run once to setup task i.e. "C:\Perl\bin\perl.exe C:\scripts\hostsdeny.pl". # Unix - Run once i.e. "perl /root/scripts/hostsdeny.pl" then add to crontab @reboot. # # Uninstallations: # Windows - Delete the task hostsdeny.pl in Windows Task Scheduler. # Unix - Delete the crontab @reboot and kill the hostsdeny.pl process. # # History: # v1.0 - 20141105 - 1st release. # v1.1 - 20150929 - Add self unlock mechanism via HTTP/CGI port 80 if you got locked out. # v1.2 - 20151112 - Add warning during web access if hostsdeny.pl is not active. # # PS: Feel free to distribute but kindly retain the credits (-: ############################################# }) unless $ARGV[0] && lc $ARGV[0] eq '-bg'; # Begin settings # Windows environment (if using ActivePerl for Windows)_________________________________ # Full path to hosts.deny. my $hostsDenyWin = 'C:/Devel/cygwin/etc/hosts.deny'; # Full path to log created by this script. my $hostsDenyWinLog = 'C:/Users/Nazar/Documents/Scripts/hostsdeny.pl.log'; # Cygwin environment (if using Cygwin based Perl)_______________________________________ # Full path to hosts.deny. my $hostsDenyCyg = '/etc/hosts.deny'; # Full path to log created by this script. my $hostsDenyCygLog = '/cygdrive/c/Users/Nazar/Documents/Scripts/hostsdeny.pl.log'; # Unix environment (if using Unix based Perl)___________________________________________ # Full path to hosts.deny. my $hostsDenyNix = '/etc/hosts.deny'; # Full path to log created by this script. my $hostsDenyNixLog = '/var/log/hostsdeny.pl.log'; # Full path to sshd/vsftpd logs separated by space. my $hostsDenyNixDaemonLogs = '/var/log/sshd.log /var/log/vsftpd.log'; # Priority level to run the script, highest -20 to lowest 19, default is 0. my $hostsDenyNixNiceLevel = '19'; # Global environment____________________________________________________________________ # Ban after this number of retries, default is 3 times. my $hostsDenyBanAfterXFailedRetries = 3; # Ban only within this threshold period in seconds, default is 60secs. my $hostsDenyBanThresholdWithinXSecs = 60; # White list of IP masks which should not be banned separated by new line. my $hostsDenyWhiteListIPmask = q/ # Below are internal IPs which should not be auto blocked. 10. 172.16. 192.168. # My ISP 116.14.95. /; # End settings my $program = 'hostsdeny.pl v1.2 by Nazar78 @ TeaNazaR.com'; $| = 1; my $hostsDeny = ''; my $hostsDenyLog = ''; my %appInfo = (); my $arg = "@ARGV"; my $dat = &getDat; $hostsDenyNixNiceLevel = '0' if $hostsDenyNixNiceLevel eq ''; $hostsDenyBanAfterXFailedRetries = 3 if !int($hostsDenyBanAfterXFailedRetries); $hostsDenyBanThresholdWithinXSecs = 60 if !int($hostsDenyBanThresholdWithinXSecs); if ($ENV{GATEWAY_INTERFACE} || ($ARGV[0] && lc $ARGV[0] eq '-unlock')) { if (!$ARGV[0]) { my @ips = ($ENV{REMOTE_ADDR}); push @ips, $ENV{HTTP_CLIENT_IP} if $ENV{HTTP_CLIENT_IP}; push @ips, split(/\,\s?/, $ENV{HTTP_X_FORWARDED_FOR}) if $ENV{HTTP_X_FORWARDED_FOR}; if ($ENV{QUERY_STRING}) { $ENV{QUERY_STRING} =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; my %query = (); foreach my $list (split('&', $ENV{QUERY_STRING})) { my @queries = split('=', $list); $query{$queries[0]} = $queries[1]; } if ($query{unlock}) { foreach (split(/\,/, $query{unlock})) { chomp $_; push @ips, $_ if $_ ne ''; } } } my $ips = join(',', @ips); print `sudo "$ENV{SCRIPT_FILENAME}" -unlock $ips` || err("\nError: No password-less sudo access for user " . getpwuid($<) . "! hostsdeny.pl won't work )-:\n"); exit $?; } elsif (!$ARGV[1] || $ARGV[1] !~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/) { err("\nError: No IP address detected! hostsdeny.pl won't work )-:\n"); } elsif ($^O =~ /win/i) { &checkWin; my $task = &taskExists; if (!$task) { print "Warning: hostsdeny.pl task doesn't exists! Run $0 once to setup!\n\n"; } elsif ($task =~ /Disabled/i) { print "Warning: hostsdeny.pl task disabled! Please enable it!\n\n"; } } elsif ($^O eq 'linux') { $hostsDeny = $hostsDenyNix; $hostsDenyLog = $hostsDenyNixLog; &checkFiles; if (!`ps -ef|grep hostsdeny.pl|grep -v grep|grep -v $$`) { print "Warning: hostsdeny.pl process not active! Run $0 once to activate!\n\n"; } } else { err("\nError: Unable to determine OS platform! hostsdeny.pl won't work )-:\n"); } print "[Current Settings]---------------------------\n"; print qq{\$hostsDeny = $hostsDeny \$hostsDenyLog = $hostsDenyLog }; if ($^O eq 'linux') { print qq{\$hostsDenyNixDaemonLogs = $hostsDenyNixDaemonLogs \$hostsDenyNixNiceLevel = $hostsDenyNixNiceLevel }; } print qq{\$hostsDenyBanAfterXFailedRetries = $hostsDenyBanAfterXFailedRetries \$hostsDenyBanThresholdWithinXSecs = $hostsDenyBanThresholdWithinXSecs \$hostsDenyWhiteListIPmask = [}; my @whiteList = (); foreach my $list (split("\n", $hostsDenyWhiteListIPmask)) { chomp $list; next if $list eq '' || $list =~ /^\#/; push @whiteList, $list; } print join("] [", @whiteList) . "]\n\n"; print "[Self Unlock]--------------------------------"; foreach (split(/\,/, $ARGV[1])) { chomp $_; if ($_ =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) { unlockIP($_); } else { print "\n$_ is not a valid IP...\n"; } } print "\n[Currently Blocked]--------------------------\n"; print "Date_______Time_______IP Mask\n"; my $blocked = 0; if (open(DENY, "<$hostsDeny")) { foreach my $list (<DENY>) { chomp $list; $list =~ s/\\//g; if ($list =~ /^ALL: (\d{1,3}\.\d{1,3}\.\d{1,3}\.).+\[(.+)\].+/) { print "$2 - $1*\n"; $blocked++; } } close(DENY); } print "None (-:\n" if !$blocked; } elsif ($^O =~ /win/i) { &checkWin; if ($arg =~ /sshd: PID .+: Failed password for .+ from (.+) port .+/ || $arg =~ /sshd: PID .+: Invalid user .+ from (.+)/ || $arg =~ /sshd: PID .+: Did not receive identification string from (.+)/) { if (!allowIP($1) && monitorIP($1) >= $hostsDenyBanAfterXFailedRetries) { denyIP($1); banIP($1); } } elsif ($arg =~ /sshd: PID .+: Accepted .+ for .+ from (.+) port .+/) { clearIP($1); } elsif (!&taskExists) { &taskCreate; } else { print "Nothing to do here?\n"; } } elsif ($^O eq 'linux') { $hostsDeny = $hostsDenyNix; $hostsDenyLog = $hostsDenyNixLog; if (lc $arg ne '-bg') { &checkFiles; print "Killing current process to fork into background... Next time pipe out to null to hide these (-:\n"; system "ps -ef|grep hostsdeny.pl|grep -v grep|grep -v $$|awk '{print \$2}'|xargs kill -9>/dev/null 2>&1"; exec "nice -n$hostsDenyNixNiceLevel $0 -bg &"; exit 0; } else { my $tail = "tail -qFn1 $hostsDenyNixDaemonLogs"; my @processes = split(/\n/, `ps -ef|grep "$tail"|grep -v grep|awk '{print \$2}'`); system "kill -9 " . join(" ", @processes) . " >/dev/null 2>&1" if @processes; while (1) { if (open my $in, "$tail |") { while (my $line = <$in>) { chomp $line; $line =~ s/.+(sshd\[\d+\]: .+)/$1/; $line =~ s/.+(\[pid \d+\] .+)/$1/; if ($line =~ /sshd\[\d+\]: Failed password for .+ from (.+) port .+/ || $line =~ /sshd\[\d+\]: Invalid user .+ from (.+)/ || $line =~ /sshd\[\d+\]: Did not receive identification string from (.+)/ || $line =~ /\[pid \d+\] \[.+\] FAIL LOGIN: Client "(.+)"/ || $line =~ /\[pid \d+\] \[.+\] FTP response: Client "(.+)", "530 Permission denied\."/) { if ($line !~ /\[anonymous\] FTP/i && !allowIP($1) && monitorIP($1) >= $hostsDenyBanAfterXFailedRetries) { $arg = $line; denyIP($1); } } elsif ($line =~ /sshd\[\d+\]: Accepted .+ for .+ from (.+) port .+/ || $line =~ /\[pid \d+\] \[.+\] OK LOGIN: Client "(.+)"/) { clearIP($1); } } close($in); } else { warn "\nWarning: Unable to open daemon logs $hostsDenyNixDaemonLogs! Retrying infinitely in 60secs...\n"; } sleep 60; } } } else { err("\nError: Unable to determine OS platform! hostsdeny.pl won't work )-:\n"); } sub CGI { return ($ENV{GATEWAY_INTERFACE} ? "Pragma: no-cache\n" . "Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0\n" . "Expires: Sat, 8 Jul 1978 00:00:00 GMT\nContent-type: text/plain":'') . shift @_ if !$ARGV[0]; } sub err { my $err = shift @_; print $err; exit 1; } sub checkWin { if ($^O eq 'MSWin32') { $hostsDeny = $hostsDenyWin; $hostsDenyLog = $hostsDenyWinLog; } elsif ($^O eq 'cygwin') { $hostsDeny = $hostsDenyCyg; $hostsDenyLog = $hostsDenyCygLog; &checkFiles; } else { err("\nError: Unable to determine Win build! hostsdeny.pl won't work )-:\n"); } } sub checkFiles { if (system "touch $hostsDenyLog >/dev/null 2>&1") { warn "\nWarning: Unable to write log $hostsDenyLog!\n"; } if (system "touch $hostsDeny >/dev/null 2>&1") { err("\nError: Unable to write hosts.deny $hostsDeny! hostsdeny.pl won't work )-:\n"); } } sub allowIP { my $ip = shift @_; $ip =~ s/(.+\..+\..+\.).+/$1/; foreach my $list (split(/\n/, $hostsDenyWhiteListIPmask)) { chomp $list; next if $list eq '' || $list =~ /^\#/; $list =~ s/(.+\..+\..+\.).+/$1/; my $ipescaped = $list; $ipescaped =~ s/\./\\./g; if ($ip =~ /^$ipescaped/) { print "\n$ip* is in the \$hostsDenyWhiteListIPmask ($list*)...\n"; return 1; } } return 0; } sub getDat { my $tmp = ""; if ($ENV{tmp}) { $tmp = "$ENV{tmp}/hostsdeny.pl.dat"; } elsif ($ENV{temp}) { $tmp = "$ENV{temp}/hostsdeny.pl.dat"; } elsif (-d '/tmp') { $tmp = '/tmp/hostsdeny.pl.dat'; } else { $tmp = "$0.dat"; } unlink $tmp if (-f $tmp); return $tmp; } sub monitorIP { my $ip = shift @_; $ip =~ s/(.+\..+\..+\.).+/$1/; my @ips = (); if (open(IP, "<$dat")) { @ips = <IP>; close(IP); } open(IP, ">$dat"); my $count = 1; foreach my $list (@ips) { chomp $list; my ($tmp1, $tmp2, $tmp3) = ($list =~ /(.+):(.+):(.+)/); if (($tmp1 ne $ip) && (time - $tmp3 < $hostsDenyBanThresholdWithinXSecs)) { print IP "$list\n"; } elsif ($tmp1 eq $ip) { $count += $tmp2; } } print IP "$ip:$count:" . time . "\n"; close(IP); return $count; } sub clearIP { my $ip = shift @_; my $ipFull = $ip; $ip =~ s/(.+\..+\..+\.).+/$1/; my @ips = (); if (open(IP, "<$dat")) { @ips = <IP>; close(IP); open(IP, ">$dat"); foreach my $list (@ips) { chomp $list; my $tmp = $list; $tmp =~ /(.+):.+:.+/; if ($tmp eq $ip) { unlockIP($ipFull); } else { print IP "$list\n"; } } close(IP); } } sub unlockIP { my $ip = shift @_; my $ipFull = $ip; $ip =~ s/(.+\..+\..+\.).+/$1/; my $ipescaped = $ip; $ipescaped =~ s/\./\\./g; my @deny = (); if (open(DENY, "<$hostsDeny")) { @deny = <DENY>; close(DENY); } if ("@deny" =~ /ALL: $ipescaped/g) { open(DENY, ">$hostsDeny"); foreach my $list2 (@deny) { chomp $list2; print DENY "$list2\n" if ($list2 !~ /ALL: $ipescaped/); } close(DENY); print "\n$ip* is now allowed in $hostsDeny...\n"; unBanIP($ipFull) if $^O =~ /MSWin32|cygwin/; } else { print "\n$ip* is not locked...\n"; } } sub denyIP { my $ip = shift @_; my @deny = (); $ip =~ s/(.+\..+\..+\.).+/$1/; my $ipescaped = $ip; $ipescaped =~ s/\./\\./g; if (open(DENY, "<$hostsDeny")) { @deny = <DENY>; close(DENY); } if ("@deny" =~ /ALL: $ipescaped/g) { print "\n$ip* already denied in $hostsDeny...\n"; } elsif (open(DENY, ">$hostsDeny")) { foreach my $list (@deny) { chomp $list; print DENY "$list\n" if ($list !~ /ALL: $ipescaped/); } my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(); my $ts = "[" . sprintf("%04d/%02d/%02d %02d:%02d:%02d", $year + 1900, $mon + 1, $mday, $hour, $min, $sec) . "]"; my $tsescaped = $ts; $tsescaped =~ s/\:/\\:/g; print DENY "ALL: $ip : twist /bin/echo \"$tsescaped Hey %h... WTF? Blocked by $program...\"\n"; close(DENY); print "\n$ip* is now denied in $hostsDeny...\n"; if (open(DENY, ">>$hostsDenyLog")) { print DENY "$ts $arg\n"; close(DENY); } else { warn "\nWarning: Unable to write log $hostsDenyLog!\n"; } } else { err("\nError: Unable to write hosts.deny $hostsDeny! hostsdeny.pl won't work )-:\n"); } } sub banIP { my $ip = shift @_; $ip =~ s/(.+\..+\..+\.).+/$1/; my $ipescaped = $ip; $ipescaped =~ s/\./\\./g; my $rules = `netsh advfirewall firewall show rule name=hostsdeny.pl`; if ($rules =~ / $ipescaped.+/g) { print "\n$ip* already banned in firewall incoming rules...\n"; } else { my $cmd = "netsh advfirewall firewall add rule name=hostsdeny.pl dir=in localport=0-79,81-65535 protocol=tcp remoteip=$ip" . "0-$ip" . "255 action=block"; `$cmd` || err("\nError: Unable to add $ip* in firewall incoming rules! Running as Admin? hostsdeny.pl won't work )-:\n"); print "\n$ip* is now banned in firewall incoming rules...\n"; } } sub unBanIP { my $ip = shift @_; $ip =~ s/(.+\..+\..+\.).+/$1/; my $ipescaped = $ip; $ipescaped =~ s/\./\\./g; my $rules = `netsh advfirewall firewall show rule name=hostsdeny.pl`; if ($rules !~ / $ipescaped.+/g) { print "\n$ip* not banned in firewall incoming rules...\n"; } else { my $cmd = "netsh advfirewall firewall delete rule name=hostsdeny.pl dir=in localport=0-79,81-65535 protocol=tcp remoteip=$ip" . "0-$ip" . "255"; `$cmd` || err("\nError: Unable to delete $ip* in firewall incoming rules! Running as Admin? hostsdeny.pl won't work )-:\n"); print "\n$ip* is now allowed in firewall incoming rules...\n"; } } sub taskExists { my $cmd = `schtasks` || err("\nError: Unable to check Task Scheduler! Running as Admin? hostsdeny.pl won't work )-:\n"); foreach my $line (split("\n", $cmd)) { chomp $line; return $line if ($line =~ /hostsdeny\.pl/); } return 0; } sub taskCreate { print "Creating hostsdeny.pl Task Scheduler...\n"; if ($PerlApp::VERSION) { ($appInfo{cwd}, $appInfo{file}) = (PerlApp::exe() =~ /^(.+)\\([^\\]+)$/); } elsif ($0 =~ /\\/) { ($appInfo{cwd}, $appInfo{file}) = ($0 =~ /^(.+)\\([^\\]+)$/); } else { $appInfo{file} = $0; $appInfo{cwd} = '.'; } my $exe = "$appInfo{cwd}\\$appInfo{file}"; my $task = q |<?xml version="1.0" encoding="UTF-16"?><Task version="1.4" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task"><RegistrationInfo><Date>2014-04-17T01:38:43.4178523</Date> <Author>#PROGRAM#</Author></RegistrationInfo><Triggers><EventTrigger><Enabled>true</Enabled> <Subscription><QueryList><Query Id="0" Path="Application"><Select Path="Application">*[System[Provider[@Name='sshd'] and EventID=0]]</Select></Query></QueryList></Subscription> <ValueQueries><Value name="eventChannel">Event/System/Channel</Value><Value name="eventData">Event/EventData/Data</Value><Value name="eventRecordID">Event/System/EventRecordID</Value> <Value name="eventSeverity">Event/System/Level</Value></ValueQueries></EventTrigger></Triggers><Principals><Principal id="Author"><UserId>S-1-5-18</UserId> <RunLevel>HighestAvailable</RunLevel></Principal></Principals><Settings><MultipleInstancesPolicy>Queue</MultipleInstancesPolicy> <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries><StopIfGoingOnBatteries>true</StopIfGoingOnBatteries><AllowHardTerminate>true</AllowHardTerminate> <StartWhenAvailable>true</StartWhenAvailable><RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable><IdleSettings><StopOnIdleEnd>true</StopOnIdleEnd> <RestartOnIdle>false</RestartOnIdle></IdleSettings><AllowStartOnDemand>true</AllowStartOnDemand><Enabled>true</Enabled><Hidden>true</Hidden><RunOnlyIfIdle>false</RunOnlyIfIdle> <DisallowStartOnRemoteAppSession>false</DisallowStartOnRemoteAppSession><UseUnifiedSchedulingEngine>false</UseUnifiedSchedulingEngine><WakeToRun>false</WakeToRun> <ExecutionTimeLimit>PT0S</ExecutionTimeLimit><Priority>7</Priority></Settings><Actions Context="Author"><Exec><Command>#EXE#</Command> <Arguments>$(eventData)</Arguments></Exec></Actions></Task>|; $task =~ s/#PROGRAM#/$program/g; $task =~ s/#EXE#/$exe/g; my $file = ".hostsdeny.pl.$$"; if (open(TASK, ">$file")) { print TASK $task; close(TASK); my $cmd = "schtasks /create /tn hostsdeny.pl /xml $file"; `$cmd` || do { unlink $file; err("\nError: Unable to create Task Scheduler! Running as Admin? hostsdeny.pl won't work )-:\n"); }; unlink $file; } else { err("\nError: Unable to write temporary file $file! hostsdeny.pl won't work )-:\n"); } } |
Nazar,
For password-less log in for root, this line has to be uncomment, right?
# AllowUsers root (comment out)
I can never get it work otherwise.
Tony
Hi Tony,
Yes it needs to be commented out else only root can login. Or add the users manually to it then reload SSHD.
hostsdeny.pl Updated!
v1.2 – 20151112 – Add warning during web access if hostsdeny.pl is not active.
Nazar,
Awesome job. Although a community.wd.com member for several years and following your posts ever since, I didn’t realize this site existed. It’s bookmarked NOW !!
Thank You
Hi SectorGZ,
Thanks. I’ve seen your nick around for several years but think we didn’t get a chance to get into a discussion. WD forum is a bit hard to consolidate so I decided to put some of the write-ups here. Do subscribe to my mailing list. You will only get notified when there’s a new post or apps from me usually once in a blue moon when I have the time (-:
Nazar,
I have subscribed, so I am looking forward to your news and posts. After looking through your WordPress site I noticed you and your wifes “Lifestyle” … what a beautiful Family. I, too, use to fly RC planes … back in the day, but that got very expensive. Then I got into computing when it first started, not that is any easier on the pocket book … lol.
I do appreciate all the work on the My Cloud and time you spend at WD …. it is very much appreciated. Especially since we are about the only support we have. 😉
Hi SectorGZ,
Thanks again for the kinds words. I used to fly RC planes too but casually as compared to RC heli and yeah those were not easy on the pocket especially maintenance after each crash haha.