Web Pages Not Databases – Part 2: Fail2ban, Apache, IP Addresses, Linux, SELinux

23 08 2015

August 23, 2015 (Modified August 31, 2015, September 14, 2015)

(Back to the Previous Article in this Series)

I started using Linux in 1999, specifically Red Hat Linux 6.0, and I recall upgrading to Red Hat Linux 6.1 after downloading the files over a 56k modem – the good old days.  I was a little more wise when I upgraded to another release a couple of months later – I found a site on the Internet that offered Red Hat Linux CD sets for a couple of dollars.  In late 2001/early 2002 I picked up a very good book about creating Linux-based IPTables firewalls, so I set up a dual firewall setup (with a DMZ in between) using a couple of spare computers.  That setup worked great in a corporate environment for several years – I even upgraded the hardware in 2006 to inexpensive Dell PowerEdge servers and installed the latest version of Red Hat Linux (I believe Fedora 5).  I was excited about the potential capabilities of this free operating system, even going so far in 2004 to use it as the operating system for the primary file servers (Red Hat Enterprise Linux 3, if I remember correctly) in an effort to save a few thousand dollars in Microsoft licensing fees (it almost worked too).

F.A.I.L.S.?  I must have put those keywords in the blog article title for a reason, or maybe not.  In 2003 I tried setting up the Frees/wan VPN server on a spare Linux computer as an alternative to having to use a 28k/33k dial up modem connection.  It was around that time that I learned the dark side of Linux and the “free” software that could be installed.  I found an old message thread that I posted in 2003 related to Frees/wan where I mentioned that I spent in excess of 2.5 months trying to make this free VPN solution work correctly.  There were several how-to articles returned by a Google search, some of which were written for other Linux variants, others did not use X.509 certificates, and others almost worked.  Making matters worse, the Red Hat Linux kernel at the time did not support X.509 certificates, so I eventually ended up installing the Working Overloaded Linux Kernel.  I recall desperately looking for a program called Setup.exe that would just take care of the problem, but no such program was found.  A couple of months after I had Frees/wan working, a security compromise was reported in all products like Frees/wan, and the Frees/wan development had been abandoned.  I learned a very important lesson that “free” software may not be free software when you consider the time that it takes to implement and maintain the free software.  I also learned another important lesson – Linux how-to articles that are more than a couple of months old may be misleading or nearly useless; Linux articles that are written for one of the other 790 Linux Distributions may be just as misleading or useless; and not everything on the Internet in a hot-to article is true/correct (this article is no exception).

With that long introduction out of the way, I thought that I would share a couple of notes that I collected along the way when I setup Fedora 22 Linux as a server for a website that uses Apache and WordPress.  I have the headache inspiring SELinux enabled on the server, as well as the latest version of Fail2ban to temporarily block IP addresses used by the clowns on the Internet that want to make the Linux server running WordPress their new best friend.  So far, Fail2ban is working great, once the how-to articles that apply to Fedora 21 or Fedora 20 are ignored, although the current version does output apparently incorrect error messages when certain commands are executed:

[fedora 22]# fail2ban-client reload wordpress-login
ERROR  NOK: ('Cannot change database when there are jails present',)

Protecting Fedora 22 Linux with a Firewall

In one of the recent 17 Fedora releases, there was a transition from directly calling iptables commands in a script to using a command called firewall-cmd to accomplish the same task.  So, on Fedora 22 you should no longer execute commands like this:

iptables -t nat -A PREROUTING -i $INET_INTERFACE -p esp -j DNAT --to $VPN_IPADDR
 
iptables -A FORWARD -i $INET_INTERFACE -o $DMZ_INTERFACE -p udp --sport 4500 --dport 4500 -d $VPN_IPADDR -j ACCEPT
 
iptables -A FORWARD -i $INET_INTERFACE -o $DMZ_INTERFACE -p esp -j ACCEPT

Instead, with Fedora 22 the commands that are used to control the firewall have an entirely different syntax (allow access to port http 80, https port 443, ssh port 22, and ftp ports 20/21, remove access to FTP ports 20/21, and then reload and activate the changed rules):

firewall-cmd --set-default-zone=public 
 
firewall-cmd --permanent --zone=public --add-service=http 
 
firewall-cmd --permanent --zone=public --add-service=https 
 
firewall-cmd --permanent --zone=public --add-service=ssh
 
firewall-cmd --permanent --zone=public --add-service=ftp
 
firewall-cmd --permanent --zone=public --remove-service=ftp
 
firewall-cmd --reload

The changes do not take effect until the reload command is executed.  If you are planning to setup a publically accessible website, and you do not want the server to respond to ping requests and similar icmp requests, you might add a couple of additional firewall rules:

firewall-cmd --permanent --zone=public --add-icmp-block=destination-unreachable
firewall-cmd --permanent --zone=public --add-icmp-block=echo-reply
firewall-cmd --permanent --zone=public --add-icmp-block=echo-request
firewall-cmd --permanent --zone=public --add-icmp-block=parameter-problem
firewall-cmd --permanent --zone=public --add-icmp-block=redirect
firewall-cmd --permanent --zone=public --add-icmp-block=router-advertisement
firewall-cmd --permanent --zone=public --add-icmp-block=router-solicitation
firewall-cmd --permanent --zone=public --add-icmp-block=source-quench
firewall-cmd --permanent --zone=public --add-icmp-block=time-exceeded
firewall-cmd --reload

You might also decide to block certain web content spiders that mercilessly drain your server’s Internet bandwidth without returning any benefit to your website.  I noticed that the Baiduspider web crawler is a frequent offender, using several ranges of IP addresses.  I put an end to a large portion of the bandwidth drain from this web content spider with a simple firewall rule that blocks the IP address range 180.76.15.1 through 180.76.15.254 (don’t forget to reload after):

firewall-cmd --permanent --add-rich-rule="rule family='ipv4' source address='180.76.15.0/24' reject"

Note that you may see a message similar to the following when attempting to execute the reload command:

Error: 'NoneType' object has no attribute 'query_rule'

If you see the above error message when trying to reload the firewall rules, just shout “free Linux software” five times and execute this command to restart the firewall – this command should have the same end effect as the reload command, except that this command works:

systemctl restart firewalld

Now, assume that you have setup Fail2ban’s ssh jail.  After a couple of hours you have received over 200 emails from Fail2ban telling you that it has blocked 200+ computers wanting to be best ssh friends with your server.  Obviously, you skipped the step of setting up a different port for ssh.  Modify the sshd config file (if you forgot the basic vi commands: press i to be able to make changes in the file, Esc ZZ to save the changes and exit, Esc :q! to quit without saving changes):

vi /etc/ssh/sshd_config

Assume that you want to change the ssh port from 22 to 1492 (something about sailing the ocean blue?).  Below the #Port 22 heading, add:

Port 1492

Then save the file and exit vi.  Since SELinux is enabled, we need to instruct SELinux to behave correctly when an ssh client attaches to port 1492:

semanage port -a -t ssh_port_t -p tcp 1492

Note: Using the semanage command requires another package to be installed first:

dnf install policycoreutils-python

Note 2: If you think that SELinux is blocking something that should not be blocked, SELinux may be temporarily disabled with this command:

setenforce 0

To re-enable SELinux, either reboot the server or execute this command:

setenforce 1

Next, we need to add a firewall rule to permit connections on port 1492, and reload the firewall rules (note that I am using the command to restart the firewall daemon instead due to the error that appeared with the reload command):

firewall-cmd --permanent --zone=public  --add-port=1492/tcp
systemctl restart firewalld

As a final verification, make certain that the Linux firewall and SELinux recognize the new port:

firewall-cmd --list-ports
semanage port -l | grep ssh

If there are no apparent problems with the above output, restart the ssh daemon:

systemctl reload sshd.service

You may also wish to confirm which services are enabled for the Linux firewall:

firewall-cmd --list-services

Beating on a Linux box that lacks a monitor and keyboard is only so much fun (that old reboot joke, I guess).  If you have a Windows computer handy, the free Putty program will allow access to the ssh interface on the Linux server.  WinSCP is a helpful utility that provides Windows Explorer-like views through the ssh interface on the Linux server.

Protecting Fedora 22 Linux with Fail2ban

Fail2ban is a utility that monitors various log files on the server, looking for unexpected activity that typically originates from another computer on the network or on the Internet.  Fail2ban may be setup to take various actions when a problem is noticed, such as the same IP address failing to connect to SSH 10 times in 15 minutes.  The action may be to send an email to an administrator and/or to configure a firewall rule that temporarily blocks the offender’s IP address.  There are a few how-to articles found through Google searches that describe how to install and configure Fail2ban.  Shockingly (not really), some of those articles are more than a couple of months old (so the articles may not work with Fedora 22) and/or instruct people to modify files that explicitly state in the header:

# YOU SHOULD NOT MODIFY THIS FILE.

What to do?  What to do?

If you have not done so recently, make certain that the installed Fedora packages are up to date (dfn… another new command, what happened to the rpm command?):

dnf update

If the Apache web server is running on the server, there is a good chance that you execute commands similar to the following at some time in the past:

dnf install httpd
systemctl start httpd.service
systemctl enable httpd.service

Fail2ban is able to send emails using Sendmail, so if Sendmail is not installed, consider installing it:

dnf install sendmail
systemctl start sendmail
systemctl enable sendmail

While not directly applying to Fail2ban, SELinux, by default, blocks Apache from using Sendmail.  It is possible to verify that this is the case, and remove the restriction with these two commands:

sestatus -b | grep -i sendmail
setsebool -P httpd_can_sendmail 1

With Sendmail installed and running, we are able to proceed with the Fail2ban installation and configuration:

dnf install fail2ban ipset
dnf install whois fail2ban-sendmail
systemctl start fail2ban
systemctl enable fail2ban

The configuration file for Fail2ban that should be modified is /etc/fail2ban/jail.d/local.conf – but that file does not exist after installation.  The local.conf file references files in the /etc/fail2ban/filter.d/ directory that tell Fail2ban how to read the various log files and recognize problems using regular expressions (they look pretty irregular to me, but then I have not done much with regular expressions since that Turbo Pascal programming class years ago).  A starting point for the local.conf file with Fedora 22 and Sendmail, blocking ssh connection requests after a few incorrect login attempts from the same IP address within an hour, would look like the following (replace my.IP.address.here with your IP address so that Fail2ban will ignore your incorrect login attempts):

[DEFAULT]
bantime = 2592000
banaction = firewallcmd-ipset
backend = systemd
sender = emailaddress1@mydomain.com
destemail = emailaddress2@mydomain.com
action = %(action_mwl)s
ignoreip = 127.0.0.1 my.IP.address.here
 
[sshd]
enabled = true
findtime = 3600

The settings listed under the [DEFAULT] heading apply to all of the other sections in this file, unless those settings are also mentioned in the other sections of the file.  For example, the bantime (number of seconds to block an IP address) applies to the [sshd] section, as does the backend = systemd setting.  If we want Fail2ban to help protect WordPress, we will want Fail2ban to monitor a variety of log files, which cannot be done with the backend = systemd setting, so that setting will need to be modified in other sections for the file.  [sshd] describes the sshd jail, so we will need to select logical names for the sections of the file that will be added later.  The sshd jail was not defined (actually, not enabled – it is defined in another configuration file) when Fail2ban was first started, so we need to let Fail2ban know that it should load/reload the sshd jail configuration, and then verify that the jail is functional:

fail2ban-client reload sshd
fail2ban-client status sshd

If you wait a couple of minutes between executing the first of the above and second of the above commands, you may see output similar to this, which indicates that some candidates for blocking were identified and blocked, and a notification email was sent to the email address specified by the destemail setting:

Status for the jail: sshd
|- Filter
|  |- Currently failed: 0
|  |- Total failed:     0
|  `- Journal matches:  _SYSTEMD_UNIT=sshd.service + _COMM=sshd
`- Actions
   |- Currently banned: 307
   |- Total banned:     307
   `- Banned IP list:   1.215.253.186 101.78.2.106 103.15.61.138 103.224.105.7 103.248.234.3 103.253.211.244 ...

Protecting WordPress running on Fedora 22 with Fail2ban.

When an attempt is made to access the password protected /wp-admin section of a WordPress site, and a bad password is entered, by default WordPress silently destroys that failed connection attempt, so Fail2ban is not able to help by blocking repeat offenders.  A partial solution that I found on several websites is to add the following code near the start of the WordPress theme’s functions.php file:

add_action('wp_login_failed', 'log_wp_login_fail'); // hook failed login
function log_wp_login_fail($username) {
        error_log("WP login failed for username: $username");

Once that code is in place, some of the bad login attempts will be written to either the /var/log/httpd/error_log or /var/log/httpd/ssl_error_log file.  You might then start seeing errors such as these buried in those files:

[Thu Aug 13 10:17:43.578391 2015] [auth_basic:error] [pid 30933] [client 75.145.nnn.nnn:50683] AH01618: user admin not found: /wp-admin/css/login.min.css, referer: http://www.websitehere.com/wp-login.php
[Thu Aug 13 19:12:53.054913 2015] [:error] [pid 2060] [client 50.62.136.183:33789] WP login failed for username: k-mm
[Thu Aug 13 20:13:02.316777 2015] [:error] [pid 1873] [client 50.62.136.183:42677] WP login failed for username: k-mm
[Thu Aug 13 21:13:12.012160 2015] [:error] [pid 15701] [client 50.62.136.183:52432] WP login failed for username: k-mm.com
[Thu Aug 13 21:28:32.073261 2015] [:error] [pid 15697] [client 50.62.136.183:58571] WP login failed for username: k-mm.com
[Thu Aug 13 21:58:43.118303 2015] [:error] [pid 21245] [client 50.62.136.183:52059] WP login failed for username: k-mm.com
[Thu Aug 13 22:03:49.150456 2015] [:error] [pid 21244] [client 50.62.136.183:60540] WP login failed for username: k-mm.com
[Thu Aug 13 22:23:28.348351 2015] [:error] [pid 15688] [client 50.62.136.183:52911] WP login failed for username: k-mm.com
[Thu Aug 13 23:14:14.453002 2015] [:error] [pid 19632] [client 50.62.136.183:37700] WP login failed for username: admin
[Fri Aug 14 01:14:15.455095 2015] [:error] [pid 5085] [client 50.62.136.183:45656] WP login failed for username: administrator
[Fri Aug 14 02:14:16.478660 2015] [:error] [pid 4114] [client 50.62.136.183:53068] WP login failed for username: administrator

In the above, note the behavior of the computer at IP address 50.62.136.183 – that computer is slowly hitting the server with different username and password combination – slow so as not to set off blocking utilities like Fail2ban that might be configured to start blocking when there have been, for instance, five bad password attempt in an hour.  Note that I stated that the addition to the theme’s functions.php file would help to identify some of the bad login attempts – to see the others, the /var/log/httpd/access_log and /var/log/httpd/ssl_access_log files must also be monitored.  In those files you may see patterns such as these where a single IP address will try to rapidly and repeatedly post to the /wp-login.php file for more than eight hours straight:

85.97.41.164 - - [12/Aug/2015:17:17:34 -0400] "POST /wp-login.php HTTP/1.1" 200 1628 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"
85.97.41.164 - - [12/Aug/2015:17:17:35 -0400] "POST /wp-login.php HTTP/1.1" 200 1628 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"
85.97.41.164 - - [12/Aug/2015:17:17:36 -0400] "POST /wp-login.php HTTP/1.1" 200 1628 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"
85.97.41.164 - - [12/Aug/2015:17:17:37 -0400] "POST /wp-login.php HTTP/1.1" 200 1628 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"
85.97.41.164 - - [12/Aug/2015:17:17:38 -0400] "POST /wp-login.php HTTP/1.1" 200 1628 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"
85.97.41.164 - - [12/Aug/2015:17:17:38 -0400] "POST /wp-login.php HTTP/1.1" 200 1628 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"
85.97.41.164 - - [12/Aug/2015:17:17:40 -0400] "POST /wp-login.php HTTP/1.1" 200 1628 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"
85.97.41.164 - - [12/Aug/2015:17:17:42 -0400] "POST /wp-login.php HTTP/1.1" 200 1628 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"
85.97.41.164 - - [12/Aug/2015:17:17:43 -0400] "POST /wp-login.php HTTP/1.1" 200 1628 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"
...
109.228.0.250 - - [13/Aug/2015:01:42:43 -0400] "POST /wp-login.php HTTP/1.0" 403 3030 "-" "-"
109.228.0.250 - - [13/Aug/2015:01:42:48 -0400] "POST /wp-login.php HTTP/1.0" 403 3030 "-" "-"
109.228.0.250 - - [13/Aug/2015:01:42:49 -0400] "POST /wp-login.php HTTP/1.0" 403 3030 "-" "-"
109.228.0.250 - - [13/Aug/2015:01:42:50 -0400] "POST /wp-login.php HTTP/1.0" 403 3030 "-" "-"
109.228.0.250 - - [13/Aug/2015:01:42:56 -0400] "POST /wp-login.php HTTP/1.0" 403 3030 "-" "-"
109.228.0.250 - - [13/Aug/2015:01:42:56 -0400] "POST /wp-login.php HTTP/1.0" 403 3030 "-" "-"

Obviously, the computers at those IP addresses were up to no good, and should also be blocked.  Another interesting pattern that might be seen in the access_log or ssl_access_log files is an attacker trying to retrieve the login of the first author username in WordPress, working slowly to try logging into the website so as not to trip protection utilities like Fail2ban that identify multiple failed logins from the same IP address in a short period of time:

185.93.187.69 - - [20/Aug/2015:00:38:16 -0400] "GET /?author=1 HTTP/1.1" 302 -
185.93.187.69 - - [20/Aug/2015:00:38:20 -0400] "GET /wp-login.php HTTP/1.1" 403 221
185.93.187.69 - - [20/Aug/2015:00:58:35 -0400] "GET /?author=1 HTTP/1.1" 302 -
185.93.187.69 - - [20/Aug/2015:00:58:37 -0400] "GET /wp-login.php HTTP/1.1" 403 221
185.93.187.69 - - [20/Aug/2015:01:19:20 -0400] "GET /?author=1 HTTP/1.1" 302 -
185.93.187.69 - - [20/Aug/2015:01:19:22 -0400] "GET /wp-login.php HTTP/1.1" 403 221
185.93.187.69 - - [20/Aug/2015:01:39:45 -0400] "GET /?author=1 HTTP/1.1" 302 -
185.93.187.69 - - [20/Aug/2015:01:39:46 -0400] "GET /wp-login.php HTTP/1.1" 403 221
185.93.187.69 - - [20/Aug/2015:01:59:59 -0400] "GET /?author=1 HTTP/1.1" 302 -
185.93.187.69 - - [20/Aug/2015:02:00:00 -0400] "GET /wp-login.php HTTP/1.1" 403 221

You might also see something like this in the access_log or ssl_access_log file:

220.163.10.250 - - [17/Aug/2015:21:03:43 -0400] "DELETE / HTTP/1.1" 400 226

I strongly suspect that the computer at IP address 220.163.10.250 had other uses in mind for my website.  From the documentation:

“The DELETE method requests that the origin server delete the resource identified by the Request-URI. This method MAY be overridden by human intervention (or other means) on the origin server. The client cannot be guaranteed that the operation has been carried out, even if the status code returned from the origin server indicates that the action has been completed successfully. However, the server SHOULD NOT indicate success unless, at the time the response is given, it intends to delete the resource or move it to an inaccessible location. “

A quick method to determine if a potential attacker tried to use the above DELETE request is to use the grep command to search within the ssl_access_log and access_log files:

grep "DELETE" /var/log/httpd/ssl_access_log*
grep "DELETE" /var/log/httpd/access_log*

Another set of attempted compromises that is not directed at WordPress sites are also visible in the ssl_access_log and access_log files:

162.246.61.20 - - [29/Jul/2015:02:13:11 -0400] "GET /cgi-bin/php HTTP/1.1" 404 209 "-" "-"
162.246.61.20 - - [29/Jul/2015:02:13:11 -0400] "GET /cgi-bin/php5 HTTP/1.1" 404 210 "-" "-"
162.246.61.20 - - [29/Jul/2015:02:13:11 -0400] "GET /cgi-bin/php-cgi HTTP/1.1" 404 213 "-" "-"
162.246.61.20 - - [29/Jul/2015:02:13:11 -0400] "GET /cgi-bin/php.cgi HTTP/1.1" 404 213 "-" "-"
162.246.61.20 - - [29/Jul/2015:02:13:11 -0400] "GET /cgi-bin/php4 HTTP/1.1" 404 210 "-" "-"
195.145.157.189 - - [30/Jul/2015:12:07:38 -0400] "GET /cgi-bin/test-cgi HTTP/1.1" 404 214 "-" "the beast"
37.144.20.31 - - [01/Aug/2015:09:34:10 -0400] "GET /tmUnblock.cgi HTTP/1.1" 400 226 "-" "-"
69.64.46.86 - - [03/Aug/2015:01:48:28 -0400] "GET /cgi-bin/rtpd.cgi HTTP/1.0" 404 214 "-" "-"
69.64.46.86 - - [14/Aug/2015:01:24:35 -0400] "GET /cgi-bin/rtpd.cgi HTTP/1.0" 404 214 "-" "-"
23:46.148.18.122 - - [16/Aug/2015:20:30:17 -0400] "GET /tmUnblock.cgi HTTP/1.1" 403 - "-" "-"
23:46.148.18.122 - - [16/Aug/2015:20:30:17 -0400] "GET /hndUnblock.cgi HTTP/1.1" 403 - "-" "-"
88.202.224.162 - - [23/Aug/2015:07:05:15 -0400] "GET //cgi-bin/webcm?getpage=../html/menus/menu2.html&var:lang=%26%20allcfgconv%20-C%20voip%20-c%20-o%20-%20../../../../../var/tmp/voip.cfg%20%2 HTTP/1.1" 404 211
80.82.65.186 - - [01/Aug/2015:08:42:51 -0400] "GET //cgi-bin/webcm?getpage=../html/menus/menu2.html&var:lang=%26%20allcfgconv%20-C%20voip%20-c%20-o%20-%20../../../../../var/tmp/voip.cfg%20%26 HTTP/1.1" 404 211
46.165.220.215 - - [16/Aug/2015:20:51:51 -0400] "GET /cgi-bin/webcm?getpage=../html/menus/menu2.html&var:lang=%26%20allcfgconv%20-C%20voip%20-c%20-o%20-%20../../../../../var/tmp/voip.cfg%20%26 HTTP/1.1" 404 211
46.165.220.215 - - [17/Aug/2015:03:09:59 -0400] "GET /cgi-bin/webcm?getpage=../html/menus/menu2.html&var:lang=%26%20allcfgconv%20-C%20voip%20-c%20-o%20-%20../../../../../var/tmp/voip.cfg%20%26 HTTP/1.1" 404 211

If any of the above appear to be interesting, you might try a Google search to see what the remote computers were attempting to compromise.

Far less obnoxious are entries that show your Nagios monitoring utility checking the website availability:

50.196.nnn.nnn - - [19/Aug/2015:09:30:54 -0400] "GET / HTTP/1.1" 200 57465 "-" "check_http/v1.4.16 (nagios-plugins 1.4.16)"
50.196.nnn.nnn - - [19/Aug/2015:09:31:07 -0400] "GET / HTTP/1.1" 200 57465 "-" "check_http/v1.4.16 (nagios-plugins 1.4.16)"
50.196.nnn.nnn - - [19/Aug/2015:09:31:42 -0400] "GET / HTTP/1.1" 200 57465 "-" "check_http/v1.4.16 (nagios-plugins 1.4.16)"
50.196.nnn.nnn - - [19/Aug/2015:09:31:47 -0400] "GET / HTTP/1.1" 200 57465 "-" "check_http/v1.4.16 (nagios-plugins 1.4.16)"

As well as random computers trying to download a file named wpad.dat (in the webserver root directory execute touch wpad.dat to create a zero byte file for that name – this is important if your client computers should not be trying to retrieve such a file and you have a custom error page configured for the website that is a feature rich web page).  There is a chance that your client computers could be searching for this file due to a specific configuration setting:

WebNotDatabaseWPAD

Example output, showing repeated requests, is shown below:

76.29.115.160 - - [20/Aug/2015:02:07:40 -0400] "GET /wpad.dat HTTP/1.1" 200 - "-" "-"
76.29.115.160 - - [20/Aug/2015:02:07:46 -0400] "GET /wpad.dat HTTP/1.1" 200 - "-" "-"
76.29.115.160 - - [20/Aug/2015:02:08:03 -0400] "GET /wpad.dat HTTP/1.1" 200 - "-" "-"
76.29.115.160 - - [20/Aug/2015:02:08:14 -0400] "GET /wpad.dat HTTP/1.1" 200 - "-" "-"

Regular Expression Building Assistance:

If we intend to have Fail2ban help protect WordPress running on Apache on Fedora 22 Linux, we need to first create “filter” files that contain the regular expressions needed to recognize bad guy attempted access.  The filter files are located in the /etc/fail2ban/filter.d/ directory and all end with .conf, although the .conf portion of the filename is not specified in the /etc/fail2ban/jail.d/local.conf file that we created earlier.  I will create separate filter files for ssl and non-ssl log files, although that is not required.  The first filter file is apache-wp-login.conf:

vi /etc/fail2ban/filter.d/apache-wp-login.conf

I set that file to have four regular expressions to recognize a bad guy’s attempted access (one or two of the regular expressions below may be incorrect because I have not had enough recent practice at writing regular expressions):

[Definition]
failregex = [[]client <HOST>[]] WP login failed.*
            [[]client <HOST>[]] client denied.*wp-login.php
            .*\[auth_basic:error\] \[pid.*\] \[client <HOST>.*?
            .*\[:error\] \[pid.*\] \[client .*?(?P<host>\S+):\d+\] WP login failed.*
ignoreregex =

Save the file and exit vi.  Verification of the regular expression syntax is important.  The fail2ban-regex utility will process a Linux log file of your choice using one of the regular expression filters that you create in the /etc/fail2ban/filter.d/ directory.  For example, to test the filter than was created above, execute the following command:

fail2ban-regex --print-all-matched /var/log/httpd/error_log /etc/fail2ban/filter.d/apache-wp-login.conf

Your output may be similar to what appears below (note that I processed an error_log from a previous week:

Running tests
=============
 
Use   failregex filter file : apache-wp-login, basedir: /etc/fail2ban
Use         log file : /var/log/httpd/error_log-20150816
Use         encoding : UTF-8
 
 
Results
=======
 
Failregex: 40 total
|-  #) [# of hits] regular expression
|   3) [26] .*\[auth_basic:error\] \[pid.*\] \[client <HOST>.*?
|   4) [14] .*\[:error\] \[pid.*\] \[client .*?(?P<host>\S+):\d+\] WP login failed.*
`-
 
Ignoreregex: 0 total
 
Date template hits:
|- [# of hits] date format
|  [140] (?:DAY )?MON Day 24hour:Minute:Second(?:\.Microseconds)?(?: Year)?
`-
 
Lines: 144 lines, 0 ignored, 40 matched, 104 missed [processed in 0.24 sec]
|- Matched line(s):
...
|  [Thu Aug 13 22:03:49.150456 2015] [:error] [pid 21244] [client 50.62.136.183:60540] WP login failed for username: k-mm.com
|  [Thu Aug 13 22:23:28.348351 2015] [:error] [pid 15688] [client 50.62.136.183:52911] WP login failed for username: k-mm.com
|  [Thu Aug 13 23:14:14.453002 2015] [:error] [pid 19632] [client 50.62.136.183:37700] WP login failed for username: admin
|  [Fri Aug 14 01:14:15.455095 2015] [:error] [pid 5085] [client 50.62.136.183:45656] WP login failed for username: administrator
|  [Fri Aug 14 02:14:16.478660 2015] [:error] [pid 4114] [client 50.62.136.183:53068] WP login failed for username: administrator
|  [Fri Aug 14 13:02:10.181252 2015] [auth_basic:error] [pid 30239] [client 75.145.nnn.nnn:54787] AH01618: user test not found: /wp-admin/css/login.min.css, referer: http://www.mydomain.com/wp-login.php
|  [Fri Aug 14 13:02:12.819515 2015] [auth_basic:error] [pid 30239] [client 75.145.nnn.nnn:54787] AH01618: user test not found: /wp-admin/css/login.min.css, referer: http://www.mydomain.com/wp-login.php
|  [Fri Aug 14 13:02:14.880515 2015] [auth_basic:error] [pid 30239] [client 75.145.nnn.nnn:54787] AH01618: user test not found: /wp-admin/css/login.min.css, referer: http://www.mydomain.com/wp-login.php
|  [Fri Aug 14 13:02:29.497034 2015] [:error] [pid 3357] [client 75.145.nnn.nnn:54798] WP login failed for username: k-mm, referer: http://www.mydomain.com/wp-login.php
|  [Fri Aug 14 13:02:29.531482 2015] [auth_basic:error] [pid 3357] [client 75.145.nnn.nnn:54798] AH01618: user test not found: /wp-admin/css/login.min.css, referer: http://www.mydomain.com/wp-login.php
...

The /etc/fail2ban/filter.d/apache-wp-login-ssl.conf filter file that I created is identical to the /etc/fail2ban/filter.d/apache-wp-login.conf file:

[Definition]
failregex = [[]client <HOST>[]] WP login failed.*
            [[]client <HOST>[]] client denied.*wp-login.php
            .*\[auth_basic:error\] \[pid.*\] \[client <HOST>.*?
            .*\[:error\] \[pid.*\] \[client .*?(?P<host>\S+):\d+\] WP login failed.*
ignoreregex =

After saving the file and exiting vi, we are able to test the filter:

fail2ban-regex --print-all-matched /var/log/httpd/ssl_error_log /etc/fail2ban/filter.d/apache-wp-login-ssl.conf

The wordpress-login.conf and wordpress-login-ssl.conf filter files will be used to examine the /var/log/httpd/access_log and /var/log/httpd/ssl_access_log files, respectively.

The /etc/fail2ban/filter.d/wordpress-login.conf file (note once again that one or two of the regular expressions used for matching may need to be adjusted):

[Definition]
failregex = ^<HOST> .* "POST .*\/wp-login.php HTTP/1.0" 403 .*$
            ^<HOST> .* "POST .*\/wp-login.php HTTP/1.1" 403 .*$
            ^<HOST> .* "POST .*wp-login.php HTTP.1.*" 403
            ^<HOST> .* "POST .*wp-login.php HTTP.1.*" 200
            ^<HOST> .* "GET .*wp-login.php HTTP/1.*" 403 221
            ^<HOST> .* "GET ..author=1 HTTP/1.*" 302 -
ignoreregex =

The /etc/fail2ban/filter.d/wordpress-login-ssl.conf file:

[Definition]
failregex = ^<HOST> .* "POST .*\/wp-login.php HTTP/1.0" 403 .*$
            ^<HOST> .* "POST .*\/wp-login.php HTTP/1.1" 403 .*$
            ^<HOST> .* "POST .*wp-login.php HTTP.1.*" 403
            ^<HOST> .* "POST .*wp-login.php HTTP.1.*" 200
            ^<HOST> .* "GET .*wp-login.php HTTP/1.*" 403 221
            ^<HOST> .* "GET ..author=1 HTTP/1.*" 302 -
ignoreregex =

To test those two filters, use these commands:

fail2ban-regex --print-all-matched /var/log/httpd/access_log /etc/fail2ban/filter.d/wordpress-login.conf
fail2ban-regex --print-all-matched /var/log/httpd/ssl_access_log /etc/fail2ban/filter.d/wordpress-login-ssl.conf

Added August 31, 2015:

I have found that a couple of computers on the Internet are trying to access a variety of *.cgi files in rapid fashion, resulting in entries such as these being written to the /var/log/httpd/error_log file:

[Sun Aug 30 20:38:08.187093 2015] [cgi:error] [pid 6426] [client 64.15.155.177:53122] AH02811: script not found or unable to stat: /var/www/cgi-bin/webmap.cgi
[Sun Aug 30 20:38:08.271430 2015] [cgi:error] [pid 6230] [client 64.15.155.177:53316] AH02811: script not found or unable to stat: /var/www/cgi-bin/whois.cgi
[Sun Aug 30 20:38:08.599455 2015] [cgi:error] [pid 6094] [client 64.15.155.177:54035] AH02811: script not found or unable to stat: /var/www/cgi-bin/register.cgi
[Sun Aug 30 20:38:08.733852 2015] [cgi:error] [pid 6453] [client 64.15.155.177:54213] AH02811: script not found or unable to stat: /var/www/cgi-bin/download.cgi
[Sun Aug 30 20:38:09.048479 2015] [cgi:error] [pid 5353] [client 64.15.155.177:54516] AH02811: script not found or unable to stat: /var/www/cgi-bin/shop.cgi
[Sun Aug 30 20:38:09.533326 2015] [cgi:error] [pid 5673] [client 64.15.155.177:56107] AH02811: script not found or unable to stat: /var/www/cgi-bin/profile.cgi
[Sun Aug 30 20:38:09.736446 2015] [cgi:error] [pid 6455] [client 64.15.155.177:56274] AH02811: script not found or unable to stat: /var/www/cgi-bin/about_us.cgi
[Sun Aug 30 20:38:09.830315 2015] [cgi:error] [pid 6456] [client 64.15.155.177:56734] AH02811: script not found or unable to stat: /var/www/cgi-bin/php.fcgi
[Sun Aug 30 20:38:09.918823 2015] [cgi:error] [pid 4232] [client 64.15.155.177:56923] AH02811: script not found or unable to stat: /var/www/cgi-bin/calendar.cgi
[Sun Aug 30 20:38:10.013162 2015] [cgi:error] [pid 6423] [client 64.15.155.177:57115] AH02811: script not found or unable to stat: /var/www/cgi-bin/download.cgi
[Sun Aug 30 20:38:10.106597 2015] [cgi:error] [pid 6425] [client 64.15.155.177:57399] AH02811: script not found or unable to stat: /var/www/cgi-bin/light_board.cgi
[Sun Aug 30 20:38:10.193901 2015] [cgi:error] [pid 6426] [client 64.15.155.177:57574] AH02811: script not found or unable to stat: /var/www/cgi-bin/main.cgi
[Sun Aug 30 20:38:10.288724 2015] [cgi:error] [pid 6230] [client 64.15.155.177:57754] AH02811: script not found or unable to stat: /var/www/cgi-bin/search.cgi
[Sun Aug 30 20:38:10.516842 2015] [cgi:error] [pid 5349] [client 64.15.155.177:57949] AH02811: script not found or unable to stat: /var/www/cgi-bin/test.cgi
[Sun Aug 30 20:38:10.601953 2015] [cgi:error] [pid 6094] [client 64.15.155.177:58409] AH02811: script not found or unable to stat: /var/www/cgi-bin/file_up.cgi

If you have Fail2ban running on the webserver, and you are seeing entries like the above in the error_log file, consider creating a file named /etc/fail2ban/filter.d/apache-cgi-bin.conf with the following contents:

[Definition]
failregex   = ^.*\[cgi:error\] \[pid.*\] \[client .*?(?P<host>\S+):\d+\] AH02811: script not found or unable to stat: \/var\/www\/cgi-bin.*$
ignoreregex =

To test the above filter definition, execute this command:

fail2ban-regex --print-all-matched /var/log/httpd/error_log /etc/fail2ban/filter.d/apache-cgi-bin.conf

(Note that the steps that follow assume that the local.conf file has already been created, see the steps below.)  To set up the jail that uses the above filter, in the /etc/fail2ban/jail.d/local.conf file, you would then add the following lines, which will setup blocking when a search locates five or more matching entries from the same IP address within two days:

[apache-cgi-bin]
enabled  = true
filter   = apache-cgi-bin
logpath  = /var/log/httpd/error_log
bantime  = 2592000
findtime = 172800
port     = http,https
maxretry = 5
backend  = polling
journalmatch =

To activate the jail, execute:

fail2ban-client reload apache-cgi-bin

To see the jail status, execute:

fail2ban-client status apache-cgi-bin

Below is sample output for the above command:

Status for the jail: apache-cgi-bin
|- Filter
|  |- Currently failed: 1
|  |- Total failed:     111
|  `- File list:        /var/log/httpd/error_log
`- Actions
   |- Currently banned: 4
   |- Total banned:     4
   `- Banned IP list:   118.219.233.133 27.254.67.157 118.163.223.214 64.15.155.177

Added September 14, 2015:

I noticed a couple of additional suspicious access entries in the access_log file.  The first set of entries appears to be from a computer looking for a wide range of web server vulnerabilities:

185.25.48.89 - - [13/Sep/2015:22:57:49 -0400] "GET /wp-content/uploads/samplc.php HTTP/1.1" 301 - "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"
185.25.48.89 - - [13/Sep/2015:22:57:50 -0400] "GET /wp-content/uploads/samplc.php HTTP/1.1" 301 - "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"
185.25.48.89 - - [13/Sep/2015:22:57:55 -0400] "POST /uploadify/uploadify.php HTTP/1.1" 301 - "http://k-mm.com/uploadify/uploadify.php" "Mozilla/5.0 (Windows; Windows NT 5.1; en-US) Firefox/3.5.0"
185.25.48.89 - - [13/Sep/2015:22:57:58 -0400] "GET /samplc.php HTTP/1.1" 301 - "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"
185.25.48.89 - - [13/Sep/2015:22:57:59 -0400] "GET /samplc.php HTTP/1.1" 301 - "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"
185.25.48.89 - - [13/Sep/2015:22:58:02 -0400] "POST /wp-admin/admin-ajax.php HTTP/1.1" 200 1 "http://k-mm.com/wp-admin/admin-ajax.php" "Mozilla/5.0 (Windows; Windows NT 5.1; en-US) Firefox/3.5.0"
185.25.48.89 - - [13/Sep/2015:22:58:06 -0400] "GET /wp-content/plugins/revslider/temp/update_extract/samplc.php HTTP/1.1" 301 - "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"
185.25.48.89 - - [13/Sep/2015:22:58:06 -0400] "GET /wp-content/plugins/revslider/temp/update_extract/samplc.php HTTP/1.1" 301 - "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"
185.25.48.89 - - [13/Sep/2015:22:58:09 -0400] "POST /php-ofc-library/ofc_upload_image.php?name=sample.php HTTP/1.1" 301 - "/php-ofc-library/ofc_upload_image.php?name=sample.php" "Mozilla/5.0 (Windows; Windows NT 5.1; en-US) Firefox/3.5.0"
185.25.48.89 - - [13/Sep/2015:22:58:12 -0400] "GET /tmp-upload-images/samplc.php HTTP/1.1" 301 - "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"
185.25.48.89 - - [13/Sep/2015:22:58:13 -0400] "GET /tmp-upload-images/samplc.php HTTP/1.1" 301 - "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"
185.25.48.89 - - [13/Sep/2015:22:58:13 -0400] "GET /large-machining-fabricating-capabilities/ HTTP/1.1" 200 50109 "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"
185.25.48.89 - - [13/Sep/2015:22:58:17 -0400] "POST /components/com_creativecontactform/fileupload/index.php HTTP/1.1" 301 - "/components/com_creativecontactform/fileupload/index.php" "Mozilla/5.0 (Windows; Windows NT 5.1; en-US) Firefox/3.5.0"
185.25.48.89 - - [13/Sep/2015:22:58:20 -0400] "GET /components/com_creativecontactform/fileupload/files/samplc.php HTTP/1.1" 301 - "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"
185.25.48.89 - - [13/Sep/2015:22:58:21 -0400] "GET /components/com_creativecontactform/fileupload/files/samplc.php HTTP/1.1" 301 - "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"
185.25.48.89 - - [13/Sep/2015:22:58:27 -0400] "GET /wp-content/uploads/samplc.php HTTP/1.1" 301 - "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"
185.25.48.89 - - [13/Sep/2015:22:58:28 -0400] "GET /wp-content/uploads/samplc.php HTTP/1.1" 301 - "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"
185.25.48.89 - - [13/Sep/2015:22:58:31 -0400] "HEAD /plugins/editor.zoho/agent/save_zoho.php HTTP/1.1" 301 - "-" "-"
185.25.48.89 - - [13/Sep/2015:22:58:32 -0400] "HEAD /sites/all/libraries/elfinder/elfinder.html HTTP/1.1" 301 - "-" "-"
185.25.48.89 - - [13/Sep/2015:22:58:33 -0400] "POST /wp-admin/admin-ajax.php?page=pmxi-admin-settings&action=upload&name=samplc.php HTTP/1.1" 200 1 "/wp-admin/admin-ajax.php?page=pmxi-admin-settings&action=upload&name=samplc.php" "Mozilla/5.0 (Windows; Windows NT 5.1; en-US) Firefox/3.5.0"
@
185.25.48.89 - - [13/Sep/2015:22:58:27 -0400] "GET /wp-content/uploads/samplc.php HTTP/1.1" 301 - "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"
185.25.48.89 - - [13/Sep/2015:22:58:28 -0400] "GET /wp-content/uploads/samplc.php HTTP/1.1" 301 - "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"
185.25.48.89 - - [13/Sep/2015:22:58:31 -0400] "HEAD /plugins/editor.zoho/agent/save_zoho.php HTTP/1.1" 301 - "-" "-"
185.25.48.89 - - [13/Sep/2015:22:58:32 -0400] "HEAD /sites/all/libraries/elfinder/elfinder.html HTTP/1.1" 301 - "-" "-"
185.25.48.89 - - [13/Sep/2015:22:58:33 -0400] "POST /wp-admin/admin-ajax.php?page=pmxi-admin-settings&action=upload&name=samplc.php HTTP/1.1" 200 1 "/wp-admin/admin-ajax.php?page=pmxi-admin-settings&action=upload&name=samplc.php" "Mozilla/5.0 (Windows; Windows NT 5.1; en-US) Firefox/3.5.0"
185.25.48.89 - - [13/Sep/2015:22:58:34 -0400] "GET /wp-content/plugins/wpallimport/samplc.php HTTP/1.1" 301 - "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"
185.25.48.89 - - [13/Sep/2015:22:58:35 -0400] "GET /wp-content/plugins/wpallimport/samplc.php HTTP/1.1" 301 - "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"
185.25.48.89 - - [13/Sep/2015:22:58:38 -0400] "POST /server/php/ HTTP/1.1" 301 - "/server/php/" "Mozilla/5.0 (Windows; Windows NT 5.1; en-US) Firefox/3.5.0"
185.25.48.89 - - [13/Sep/2015:22:58:41 -0400] "GET /server/php/files/samplc.php HTTP/1.1" 301 - "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"
185.25.48.89 - - [13/Sep/2015:22:58:42 -0400] "GET /server/php/files/samplc.php HTTP/1.1" 301 - "-" "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)"

The second set of entries appear to be from two different computers that are apparently trying to take advantage of a SQL injection attempt to deface a website, or something similar:

122.154.24.254 - - [14/Sep/2015:03:29:38 -0400] "GET /phpMyAdmin/scripts/setup.php HTTP/1.1" 301 - "-" "-"
122.154.24.254 - - [14/Sep/2015:03:29:41 -0400] "GET /pma/scripts/setup.php HTTP/1.1" 301 - "-" "-"
122.154.24.254 - - [14/Sep/2015:03:29:45 -0400] "GET /myadmin/scripts/setup.php HTTP/1.1" 301 - "-" "-"
122.155.190.132 - - [14/Sep/2015:07:52:22 -0400] "GET /phpMyAdmin/scripts/setup.php HTTP/1.1" 301 - "-" "-"
122.155.190.132 - - [14/Sep/2015:07:52:27 -0400] "GET /pma/scripts/setup.php HTTP/1.1" 301 - "-" "-"
122.155.190.132 - - [14/Sep/2015:07:52:33 -0400] "GET /myadmin/scripts/setup.php HTTP/1.1" 301 - "-" "-"

While the hacking attempts were unsuccessful, I decided that there is little point in wasting the server’s resources with similar attempts.  I created a new Fail2ban filter with the filename /etc/fail2ban/filter.d/apache-block-misc-php.conf and added the following lines to recognize the above entries in the Apache access_log file:

[Definition]
failregex = ^<HOST> .* "POST .*uploadify.php HTTP.1.*" .*$
            ^<HOST> .* "HEAD .*uploadify.php HTTP.1.*" .*$
            ^<HOST> .* "POST .*ofc_upload_image.php.*" .*$
            ^<HOST> .* "POST .*fileupload.index.php .*" .*$
            ^<HOST> .* "HEAD .*save_zoho.php .*" .*$
            ^<HOST> .* "POST .*save_zoho.php .*" .*$
            ^<HOST> .* "HEAD .*elfinder.html .*" .*$
            ^<HOST> .* "POST .*elfinder.html .*" .*$
            ^<HOST> .* "GET .*scripts.setup.php .*" .*$
            ^<HOST> .* "POST .*scripts.setup.php .*" .*$
            ^<HOST> .* "GET .*\/samplc.php .*" .*$
            ^<HOST> .* "GET .*\/?author=.*" .*$
            ^<HOST> .* "GET .*abdullkarem.*" .*$
            ^<HOST> .* "GET .*\/uploadify.php.*" .*$
            ^<HOST> .* "GET .*\/bin\/perl .*$
            ^<HOST> .* "GET .*wp-admin\/admin-ajax.php .*" .*$
            ^<HOST> .* "GET <title>phpMyAdmin HTTP.*$
            ^<HOST> .* "GET \/phpmyadmin.*$
            ^<HOST> .* "GET \/phpMyAdmin.*$
            ^<HOST> .* "GET \/PMA\/.*$
            ^<HOST> .* "GET \/pma\/.*$
            ^<HOST> .* "GET \/admin\/.*$
            ^<HOST> .* "GET \/dbadmin\/.*$
            ^<HOST> .* "GET \/mysql\/.*$
            ^<HOST> .* "GET \/myadmin\/.*$
            ^<HOST> .* "GET \/sqlmanager\/.*$
            ^<HOST> .* "GET \/mysqlmanager\/.*$
            ^<HOST> .* "GET \/wcd\/top.xml.*$
            ^<HOST> .* "GET \/wcd\/system_device.xml.*$
            ^<HOST> .* "GET \/wcd\/system.xml.*$
            ^<HOST> .* "GET \/openurgencevaccin\/index.php.*$
            ^<HOST> .* "GET \/zeuscms\/index.php.*$
            ^<HOST> .* "GET \/phpcoin\/license.php.*$
            ^<HOST> .* "GET \/authadmin\/.*$
            ^<HOST> .* "GET \/backup\/.*$
            ^<HOST> .* "GET \/backups\/.*$
            ^<HOST> .* "GET \/bak\/.*$
            ^<HOST> .* "GET \/cbi-bin\/.*$
            ^<HOST> .* "GET \/ccard\/.*$
            ^<HOST> .* "GET \/ccards\/license.php.*$
            ^<HOST> .* "GET \/cd-cgi\/.*$
            ^<HOST> .* "GET \/cfide\/.*$
            ^<HOST> .* "GET \/cgi\/.*$
            ^<HOST> .* "POST .*\/fileupload\/index.php.*$
            ^<HOST> .* "POST .*\/php\/index.php.*$
            ^<HOST> .* "GET .*wp-config.php.*$
            ^<HOST> .* "POST .*\/examples\/upload.php.*$
ignoreregex =

Once the new filter file is created, test the filter to see if it allows Fail2ban to find any matching lines in the access_log:

fail2ban-regex --print-all-matched /var/log/httpd/access_log /etc/fail2ban/filter.d/apache-block-misc-php.conf

If it appears that the filter is finding matching lines, add a new jail definition in the /etc/fail2ban/jail.d/local.conf file (note that maxretry is set to 2):

[apache-block-misc-php]
enabled = true
filter   = apache-block-misc-php
logpath  = /var/log/httpd/access_log
bantime = 2592000
findtime = 86400
port    = http,https
maxretry = 2
backend = polling
journalmatch =

To activate the new jail, execute the reload command:

fail2ban-client reload apache-block-misc-php

To check the status of the new jail, execute the status command:

fail2ban-client status apache-block-misc-php

Sample output is shown below:

Status for the jail: apache-block-misc-php
|- Filter
|  |- Currently failed: 0
|  |- Total failed:     30
|  `- File list:        /var/log/httpd/access_log
`- Actions
   |- Currently banned: 4
   |- Total banned:     4
   `- Banned IP list:   114.27.9.31 122.154.24.254 122.155.190.132 185.25.48.89

For Fail2ban to use the filters that were just created, we must add additional lines (jail descriptions) to the /etc/fail2ban/jail.d/local.conf file:

vi /etc/fail2ban/jail.d/local.conf

At the end of the file add the following four jail definitions (note that without the backend and journalmatch lines the jails will not work due to the settings in the [DEFAULT] section of this file):

[apache-wp-login]
enabled = true
filter   = apache-wp-login
logpath  = /var/log/httpd/error_log
bantime  = 2592000
findtime = 3600
port    = http,https
maxretry = 5
backend  = polling
journalmatch =
 
[apache-wp-login-ssl]
enabled = true
filter   = apache-wp-login-ssl
logpath  = /var/log/httpd/ssl_error_log
bantime  = 2592000
findtime = 3600
port    = http,https
maxretry = 5
backend  = polling
journalmatch =
  
[wordpress-login]
enabled = true
filter   = wordpress-login
logpath  = /var/log/httpd/access_log
bantime = 345600
findtime = 86400
port    = http,https
maxretry = 6
backend = polling
journalmatch =
 
[wordpress-login-ssl]
enabled = true
filter   = wordpress-login-ssl
logpath  = /var/log/httpd/ssl_access_log
bantime = 345600
findtime = 86400
port    = http,https
maxretry = 6
backend = polling
journalmatch =

Save the file and exit vi.  Next we need to instruct Fail2ban to recognize the four new jails:

fail2ban-client reload apache-wp-login
fail2ban-client reload apache-wp-login-ssl
fail2ban-client reload wordpress-login
fail2ban-client reload wordpress-login-ssl

As an alternative to the above, we could just restart Fail2ban, which will restart all of the jails, and potentially spam your inbox with ssh blocking notifications:

systemctl restart fail2ban.service

Checking the status of the jails is quite simple to accomplish:

fail2ban-client status apache-wp-login
fail2ban-client status apache-wp-login-ssl
fail2ban-client status wordpress-login
fail2ban-client status wordpress-login-ssl

You might be curious about the emails that Fail2ban sends.  Below is a portion of an actual email that I received from Fail2ban recently:

Hi,

The IP 46.119.117.47 has just been banned by Fail2Ban after
12 attempts against wordpress-login.

Here is more information about 46.119.117.47:

[Querying whois.ripe.net]
[whois.ripe.net]
% This is the RIPE Database query service.
% The objects are in RPSL format.
%
% The RIPE Database is subject to Terms and Conditions.
% See http://www.ripe.net/db/support/db-terms-conditions.pdf

% Note: this output has been filtered.
%       To receive output for a database update, use the “-B” flag.

% Information related to ‘46.118.0.0 – 46.119.255.255’

% Abuse contact for ‘46.118.0.0 – 46.119.255.255’ is ‘abuse@kyivstar.net’

inetnum:        46.118.0.0 – 46.119.255.255
descr:          Golden Telecom LLC
netname:        UA-SVITONLINE-20100517
org:            ORG-SOGT1-RIPE
country:        UA
admin-c:        GTUA-RIPE
tech-c:         GTUA-RIPE
status:         ALLOCATED PA
mnt-by:         RIPE-NCC-HM-MNT
mnt-lower:      GTUA-MNT
mnt-lower:      GTUA-WO-MNT
mnt-domains:    GTUA-ZONE-MNT
mnt-domains:    GTUA-MNT
mnt-routes:     GTUA-RT-MNT
mnt-routes:     GTUA-MNT
created:        2010-05-17T08:47:45Z
last-modified:  2011-08-04T15:58:57Z
source:         RIPE # Filtered

organisation:   ORG-SOGT1-RIPE
org-name:       Golden Telecom LLC
org-type:       LIR
address:        15/15/6 V. Khvojki str.
address:        04080
address:        Kiev
address:        UKRAINE
phone:          +380444900000
fax-no:         +380444900048
admin-c:        AEL17-RIPE
admin-c:        NP1533-RIPE
mnt-ref:        RIPE-NCC-HM-MNT
mnt-ref:        GTUA-MNT
mnt-by:         RIPE-NCC-HM-MNT
abuse-c:        GTL6-RIPE
created:        2004-04-17T12:09:58Z
last-modified:  2015-07-17T13:48:48Z
source:         RIPE # Filtered

role:           Golden Telecom Ukraine NOC
address:        Golden Telecom
address:        4 Lepse blvr
address:        Kiev, 03067, Ukraine
phone:          +380 44 4900000
fax-no:         +380 44 4900048
remarks:        All abuse notifications have to be sent on:
abuse-mailbox:  abuse@kyivstar.net
admin-c:        AEL17-RIPE
admin-c:        NP1533-RIPE
nic-hdl:        GTUA-RIPE
mnt-by:         GTUA-MNT
created:        2007-07-25T09:02:04Z
last-modified:  2014-06-17T08:24:26Z
source:         RIPE # Filtered

% Information related to ‘46.119.112.0/20AS15895’

route:          46.119.112.0/20
descr:          Kyivstar GSM, Kiev, Ukraine
origin:         AS15895
mnt-by:         GTUA-MNT
created:        2012-03-21T09:29:14Z
last-modified:  2012-03-21T09:29:14Z
source:         RIPE # Filtered

% This query was served by the RIPE Database Query Service version 1.80.1 (DB-2)
Lines containing IP:46.119.117.47 in /var/log/httpd/access_log

I am not sure why, but this particular email did not list the lines from the access_log that matched the filter rule.

Protecting WordPress running on Fedora 22 with .htaccess Files

One step that you may want to take is to password protect the /wp-admin directory on your web server.  To do that, you would create a new Linux user with a username and password that are difficult to guess based on your website name and WordPress users – the password should be at least eight characters long with upper and lower case letters, numbers, and punctuation marks.  Then, using tips from the last post in this message thread, create a file name .htaccess in the /wp-admin directory.  Inside that file, add the following lines (replace /full/path/to/your/wp-admin with the directory where you will later create a .htpasswd file):

AuthName "Admin Area"
AuthType Basic
AuthUserFile /full/path/to/your/wp-admin/.htpasswd
require valid-user
 
<Files admin-ajax.php>
    Order allow,deny
    Allow from all
    Satisfy any
</Files>

Next use the htpasswd generator website to create an encrypted version of the password for the Linux username.  For example, if you created the Linux user hillbillyforpresident with a password of GreatScott1TrumpIsAhead? the htpasswd website would instruct you to create a .htpasswd file with the following contents:

hillbillyforpresident:$apr1$gAgbX0SU$YjtXg5pAvXrD6i.F2lh6z1

Make certain that the .htaccess file (and possibly the .htpasswd file also) have read/write access for the owner, read access for the group in which Apache runs (the Apache user should not own the files), and that the files are not world readable.  For example:

chmod 640 /var/www/html/wp-admin/.htaccess

The wp-config.php file should also be protected with similar file permissions:

chmod 640 /var/www/html/wp-config.php

The .htaccess file in the web server’s root directory should also be adjusted to control which files may be accessed.  Below the # END WordPress line in the file, consider adding the following (once you understand what the lines accomplish – note that the entry containing 123\.123\.123\.123 should allow the IP address 123.123.123.123 to access the wp-login.php file):

# Block access to files.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^wp-admin/includes/ - [F,L]
RewriteRule !^wp-includes/ - [S=3]
RewriteRule ^wp-includes/[^/]+\.php$ - [F,L]
RewriteRule ^wp-includes/js/tinymce/langs/.+\.php - [F,L]
RewriteRule ^wp-includes/theme-compat/ - [F,L]
 
RewriteCond %{REQUEST_URI} ^(.*)?wp-login\.php(.*)$ [OR]
RewriteCond %{REQUEST_URI} ^(.*)?wp-admin$
RewriteCond %{REMOTE_ADDR} !^123\.123\.123\.123$
RewriteRule ^(.*)$ - [R=403,L]
</IfModule>
 
<files wp-config.php>
order allow,deny
deny from all
</files>
 
<Files .htaccess>
 order allow,deny
 deny from all
</Files>
 
# Stop Apache from serving .ht* files
<Files ~ "^\.ht">
Order allow,deny
Deny from all
</Files>
 
Options -Indexes

WordPress and SELinux – a Headache Waiting to Attack

From what I understand, everything in the webserver’s root directory is set by default to the httpd_sys_content_t SELinux context – and sometimes that context is not present when files are copied into various subdirectories that are accessible to Apache.  The following command resets the SELinux context to the default context:

chcon -R -v -t httpd_sys_content_t /var/www/

Using FTP integrated with WordPress to install updated plugins or new WordPress versions is a bit of a nightmare because different SELinux contexts are required for the different directories – I never did find a combination that worked.  As a result, I added the following line to the wp-config.php file so that FTP integration is not necessary:

define( 'FS_METHOD', 'direct');

Of course the WordPress upload directory must have the httpd_sys_rw_content_t SELinux context, so at some point the following command would need to be executed:

chcon -R -v -t httpd_sys_rw_content_t /var/www/html/wp-content/uploads/

The same command may also need to be executed for the WordPress plugins and upgrade directories (and probably a tempfiles directory) so that it is possible to install and update plugins using the WordPress interface.  Right now I do not permit WordPress to auto-update when a new version is released (this is due to the file system permissions that I use that only allow the apache user to read the files, not change the files).  I previously created a download directory in the /var directory.  Whenever I need to upgrade WordPress to a new version I use a script with the following contents (note that the script was pieced together based on what the WordPress release notes stated needed to be updated):

cd /var/downloads
rm -rf /var/downloads/wordpress
rm /var/downloads/wordpress.tar.gz
wget https://wordpress.org/latest.tar.gz
mv latest.tar.gz wordpress.tar.gz
tar -xzf wordpress.tar.gz
chcon -R -v -t httpd_sys_content_t /var/downloads/wordpress/
chown -R FileOwnerHere:ApacheGroupHere /var/downloads/wordpress/
find /var/downloads/wordpress/ -type d -exec chmod 2755 {} +
find /var/downloads/wordpress/ -type f -exec chmod 2644 {} +
cp -av /var/downloads/wordpress/wp-admin/* /var/www/html/wp-admin/
cp -av /var/downloads/wordpress/wp-includes/* /var/www/html/wp-includes/
cp -v /var/downloads/wordpress/wp-content/* /var/www/html/wp-content/
cp /var/downloads/wordpress/*.php /var/www/html/
cp /var/downloads/wordpress/*.txt /var/www/html/
cp /var/downloads/wordpress/*.html /var/www/html/

The above information is consolidated from weeks, maybe months, of hammering on a seemingly simple problem – 12 years later (OK, maybe 16 years later) and I am still in search of the Linux program named Setup.exe that configures everything that needs to be configured to get a job done quickly.  Oh, going out on a limb here, let’s ask for a GUI interface too that works with Putty.  Or, even further out on a limb, let’s ask for consistency of file paths, filenames, and commands across the 790+ Linux distributions and versions within each distribution so that a how-to article created two years ago is still valid today.  Stepping off the soap box… or SOAP box.

If any readers have comments or suggestions that improve upon the above information (or gently correct), please feel free to add a comment below.  Maybe someone else will find some of the above information useful to avoid putting a couple of extra dents in the top surface of their desk.





Hyper-Extended Oracle Performance Monitor 7.0 Beta

16 08 2015

August 16, 2015

Three and a half years ago I offered the Hyper-Extended Oracle Performance 6.0 Beta for download – that Beta version expired a year later.  This program has been somewhat of a pet project for the last 13 years (almost 10 years at the time of the previous Beta offering), so I was excited to read some of the feedback about the 6.0 Beta.  The minimal feedback had me wondering for a while, then the pet project was pushed to a dark corner for roughly three years.  I taught the pet a few new tricks on Windows 10, so I thought that I make the latest version available.  Unlike the previous Beta, Beta 7.0 does not have an expiration date.

NOTE August 17, 2015: The first two download links are working now – Wordpress was blocking the downloads.  Note that the first two downloads are actually compressed .zip files with a .doc extension – inside those .zip files are the files that are of interest.  Right-click the files, save the files to your computer, then rename the files to end with .zip.

  1. Program download (right-click Save As, then change the .doc extension to .zip): Hyper-ExtendedOraclePerformanceMonitor7.zip
  2. Documentation download (right-click Save As, then change the .doc extension to .zip – still incomplete, see old docs): Hyper-Extended Oracle Performance Monitor7.doc
  3. Old Documentation download: Hyper-Extended Oracle Performance Monitor3.doc
  4. Update August 20, 2015: If you have an old computer, you may need to put the MSCOMCTL.OCX file in your C:\Windows\SysWOW64 folder (on 64 bit Windows, or C:\Windows\System32 on 32 bit Windows).  You will then need to register (process) the file with REGSVR32 – see the command in the instructions below.  A recent version of MSCOMCTL.OCX may be downloaded here: MSCOMCTL.OCX (save the file, then rename the file as MSCOMCTL.OCX).

Requirements:

Windows 2000 through Windows 10, 32 bit Oracle Client, MDAC/ADO 2.8 (or greater – preinstalled starting with Windows XP), connection to the Oracle database by database SID (tnsnames.ora) using Oracle’s Oracle Provider for OLE DB (Oracle’s Oracle ODBC Driver is required for connections that are initiated by SYS – primarily for access to the Advanced Init Parameter functionality), Excel 2000-2013, Microsoft Grid control (provided in the download – put into the program’s folder), TIMED_STATISTICS set to TRUE. Most features require DBA permissions (SYSADM or SYS), or specific grants to views or packages. For example, sessions that use the DBMS Xplan and Trace functionality must have permission to execute ALTER SESSION, have execute permission on the DBMS_XPLAN package, and select permissions on V$SQL, V$SQL_PLAN, and V$SQL_PLAN_STATISTICS.  Configuring session tracing requires execute permission on the DBMS_SYSTEM package and/or the DBMS_MONITOR package.

Description:

The Hyper-Extended Oracle Performance Monitor provides a variety of functions to simplify working with and performance monitoring Oracle databases. Various types of simple data dictionary and database instance related reports may be generated in Excel by simply putting a check in a box on the program’s main window, and clicking the Report button. Generating DBMS Xplan output (using the raw information from a 10046 level 4/12 trace) with optional 10046/10053/other traces, creating data change logging triggers using the data dictionary, peeking at the hidden and non-hidden initialization parameters, and access to a quick Oracle keyword search are also a click away on the program’s main window. Performance monitoring and tracing tools for checking a cross-reference of the time model and wait event statistics (at the system and session levels), checking a cross-reference of the system statistics and wait event statistics (with drill-down to the session level), reviewing potentially high-load SQL statements and their execution plans, cross-referencing the current session waits with enqueues, and enabling various types of Oracle traces are also a click away on the program’s main window. Permanently recording of performance monitoring statistics in a Microsoft Access compatible database (Microsoft Access is not required) is also a click away on the program’s main window.

General Command Line Parameters:

  • -D   The Database instance SID to which the program should connect.
  • -U   The user name to be used for connecting to the database instance.
  • -P   The password to be used for connecting to the database instance.

Logging-Specific Command Line Parameters:

  • -LC 20   Specifies Force a Log Capture when CPU Usage Exceeds value to 20%
  • -LI 30   Specifies Force a Log Capture if No Log Captured in Minutes value to 30 minutes
  • -LB   Specifies the Force a Log Capture when a Blocking Lock is Detected value to checked
  • -LW  Specifies the Force a Log Capture when a Wait Reason is Detected value to checked
  • -LR  Specifies the Capture SQL Execution Statistics for Wait Reasons value to checked
  • -LD   Specifies the Capture Segment Change Statistics value to checked
  • -LO   Specifies the Capture Operating System and Time Model Statistics value to checked
  • -LH   Specifies the Capture High Load SQL Statement Statistics value to checked
  • -LT   Specifies the Capture High Load SQL Statement Text value to checked
  • -LP   Specifies the Capture High Load SQL Statement Plan value to checked
  • -LHC 60   Species the minimum CPU time that is considered high load to 60 seconds accum.
  • -LHE 90   Species the minimum elapsed time that is considered high load to 90 seconds accum.
  • -LS   Specifies that Smart Logging should begin as soon as the login completes
  • -LE 240   Specifies that Smart Logging should end after 240 minutes
  • -LQ   Specifies that the program should quit (end) when logging ends

Using the above command line parameters, you could create a program shortcut for each of the databases that you administer.  For example, create a shortcut, and set its Target to (assuming that the program is on the root of the F: drive, the Oracle SID is OR1122P, the username is myuser, the password for that account is pword, and the smart logging should default to logging when 10% of CPU utilization is observed):

"F:\Hyper-Extended Oracle Performance Monitor.exe" -D OR1122P -U myname -p pword -LC 10

What’s New:

The Hyper-Extended Oracle Performance Monitor runs on Windows 10, even though the touchpad on the Sony laptop sometimes stops working until the computer is put to sleep and then awoken.  The program should also work with Oracle Database 12c (and might still be compatible with Oracle Database 8i, although the Time Model Viewer will not work).

Hyper7HyperExtendedMainScreen

The Time Model Viewer main window is still essentially the same, showing the hierarchy of the time model statistics, with color-coded session-level time model statistics (color ranges from yellow to red to indicate the percentage of the total represented by the session).  The bottom of the window shows the system-wide wait events, and the sessions that contributed to those system level wait events:

Hyper7TimeModelViewer

When the Time Model Viewer is active, an Excel spreadsheet is created that shows the graphic history of the statistics for the 20 most recent time capture periods – if you find this view helpful, you might want to widen the DB Time and CPU chart:

Hyper7TimeModelViewerExcelCharts

The statistics also appear in numerical form on the Statistics tab in the Excel spreadsheet:

Hyper7TimeModelViewerExcelStatistics

The Real-Time Monitor is also essentially the same as in previous Beta versions:

Hyper7RealTimeMonitor

Like the Time Model Viewer, an Excel spreadsheet is created showing the graphical history of the statistics.  Which statistics?  Click a wait event or one of the white statistics boxes to add that statistic to the list for which charts are created:

Hyper7RealTimeMonitorExcelCharts

Previous Beta versions of the program wrote out a tab delimited file containing the statistic deltas after every 30 time capture periods (this version also creates such a file).  The Beta for version 7 writes those statistics to Excel after each time capture period to facilitate the chart creation:

Hyper7RealTimeMonitorExcelStatistics

Oracle’s OLEDB provider is used for connectivity, see this article for a description of how to fix the bug in the Oracle Database 11.2.0.3 Client installer that is related to the OLEDB provider (the problem may have been corrected in later releases).  I have not yet found a method for SYS to login using Oracle’s OLEDB provider when “AS SYSDBA” must be specified for the login to complete.  As such, Oracle’s ODBC driver is used when SYS tries to login and the login attempt using the OLEDB provider fails.  Oracle’s ODBC driver is also used when viewing the normally hidden parameters using the SYS login.  Because there could be multiple Oracle homes, you must create a 32 bit ODBC System DSN using the Oracle ODBC driver – name that DSN HYPEREXTEND:

Hyper7Login

Hyper7ODBC1

Hyper7ODBC2

Hyper7ODBC3

The ODBC connection driver information is used to login as the SYS user (AS SYSDBA) to view the Advanced Initialization Parameters:

Hyper7AdvancedInitParameters

Hyper-Extended Oracle Performance Monitor 7.0 Beta is also more compliant with restrictions placed on the computer by User Account Control (User Access Control), which prohibits programs from create folders in the root of the C:\ drive and writing files into that folder.  Version 7 instead creates the OracleLog folder in the current user’s profile (typically C:\Users\username\OracleLog\), and separates the files created by database.  The picture below shows a couple of Microsoft Access compatible logging database that were created, as well as some of the tab-delimited statistic files, and various other generated files:

Hyper7LoggingFolder

As mentioned above, the documentation is still incomplete, with several pages of the documentation containing nothing but pictures.  It takes a lot of effort and time  to put the documentation together, so I will try to piece it together over the next couple of months.  You might be able to refer to the older documentation if the pictures are not self-explanatory.  If you find the program useful, please leave a note.  If you need help understanding a program window or the generated output, leave a comment attached to this blog article and I will try to help.

Hyper7b3.zip (Save with a .zip extension, not a .doc extension.)





On the Topic of Technology… 8 – First 48 hours with Windows 10

3 08 2015

August 3, 2015 (Updated August 5, 2015, August 10, 2015, August 29, 2015)

(Back to the Previous Post in the Series)

I have been testing Windows 10 under the Windows Insider Program for several months using an old Dell Precision Core 2 Extreme computer (with NVidia graphics card) that was retired from engineering service a couple of years ago. I had some issues upgrading from one preview version to the next, such as the new Start menu failing to display – forcing a format and reinstall of the operating system. One version that automatically installed over the top of build 10074 caused the computer to continuously reboot. Odd, I thought, that a Windows update could cause that problem. I traced that issue back to disabling data execution protection (DEP) several years earlier on the engineering computer to keep some of the software from spontaneously crashing – turning DEP back on in the BIOS immediately resolved the reboot loop. I was still seeing occasional Start menu display glitches a week or so before the official release date for Windows 10 (July 29, 2015), but those problems were diminished with the application of Windows updates as the official release date neared.

—–

Quick note on August 12, 2015: I came across an article on Spiceworks that lead me down an interesting path since I am fighting driver issues on a Sony Vaio laptop.  In short, if you have a Sony computer, don’t expect Windows 10 compatible drivers for a while.  How long?  October or November 2015.

Windows10SonyWarning

In a related article, the following statement appears, apparently quoting an unconfirmed Sony source:

“In a message to Sony laptop owners, the firm has pleaded for patience when it comes to installing the new operating system (OS) due to the real risk of software or driver corruption that could result in a catastrophic data loss.

Sony’s advice goes on to say that if customers don’t wait for the test results then there is a chance that their computer “may no longer work as intended.” This can mean anything from the system crashing, becoming unresponsive or suffering from hardware damage. The bottom line is that tapping Sony to service your PC following any of these issues could result in “losing all of the data” on your computer.”

—–

A couple of years ago I bought a Sony SVE14AE13L (SVE14A27CXH) touch screen laptop as an incentive to learn how to use and troubleshoot Windows 8. With a Core i7 processor and 8GB of memory, I anticipated having an easy time working through Windows 8, and after watching several hours of how-to videos I was reasonably comfortable with Windows 8 – for someone who has worked with computers since the early 1980s it was a bit of a struggle, but not as bad as some people claimed (I also thought that Windows Vista was reasonably good before the release of Windows 7). That said, this computer was updated to Windows 8.1 on the day that the new version became available. This Sony laptop computer, however, had a couple of annoying behaviors. It would randomly wake up in the middle of the night, the touchpad would occasionally stop working, Internet Explorer on the computer was occasionally slow, and the touchpad would send random zoom in/out messages to the current application.

Windows 8.1 worked reasonably well on the Sony laptop, but I went ahead and reserved a copy of Windows 10 for the computer to get some experience with the release version of Windows 10. The first 48 hours of Windows 10 began this past Saturday. I grew tired of waiting for Microsoft to tell me that my copy of Windows 10 was available for installation, so I opened a Windows command line and executed this command: wuauclt.exe /updatenow – that seemed to kick start the upgrade, but it failed to install once roughly 3.5GB of installer files were downloaded. Another search of the Internet indicated that I should have first deleted everything in the C:\Windows\SoftwareDistribution\Download folder. After downloading another 3.5GB copy of the Windows 10 installer, I found that this attempt also failed after I clicked the Continue (or whatever that button showed) button in the Windows Update window. I started wondering what I was doing wrong, so I downloaded the ISO DVD image for Windows 10 from Microsoft.  Progress – writing the ISO DVD image to a DVD permitted the Windows 10 installer to start with a simple double-click.

Five or six hours in now, roughly 10.5GB downloaded from the Internet (I sure am happy that I no longer have the Internet connection with the 6GB monthly limit), and the Windows 10 installer is showing a message “Your PC will restart several times. Sit back and relax.” Don’t worry, be happy (click the picture for a larger view).

Windows10-95PercentComputerMayRestart

The install completed… with an error message, “The installation failed in the FIRST_BOOT phase with an error during MIGRATE_DATA operation.” Fantastic, at least the installer brought back the previous Windows 8.1 (click the picture for a larger view).

Windows10CouldNotInstallErrorFailedFirstBootMigrateData

That sure is an obscure error message. Google searches seemed to indicate that the problem could be caused by a corrupt profile, or a half dozen other issues.

I made another attempt at the upgrade, this time telling the installer NOT to install any Windows updates during the initial install. Perhaps eight or nine hours in, success – the Windows 10 Pro logon window is a keyboard tap away (note that the picture below was taken the following day).

Windows10FirstLogin

Great, Windows updates are automatically downloading and installing. Five minutes later, the laptop is apparently unhappy with Windows 10, a frown on a blue screen of death with the message “If you’d like to know more, you can search online later for this error: INTERNAL_POWER_ERROR

Windows10BlueScreenInternalPowerError

E-Moe-Gee! (emoji). Sure, collect your error info and restart. Recalling the message from the earlier picture: “Your PC will restart several times. Sit back and relax.” And thus started an automatic reboot loop, sometimes lasting just long enough to log into Windows 10 and display the Device Manager. In this case, that INTERNAL_POWER_ERROR message means that Windows 10 does not like the AMD Radeon HD 7600M series graphics card. Congratulations Microsoft and AMD, I have not had this level of difficulty with video card drivers since 1999 when I set up a triple boot on a computer with the latest NVidia graphics card, booting into Windows 98 (drivers easy to find), Windows NT 4.0 (a little more challenging), and Red Hat Linux with the Gnome X-Window desktop (on par with Windows 10 on this Sony laptop).

A couple of tips at this point. Pressing F8 during the initial Windows boot apparently does not display the old Windows 95 style boot menu that allows the computer to start in Safe Mode with limited functionality. If the computer is able to boot to the logon screen, there is a power button at the bottom-right of that screen. Hold down the Shift key on the keyboard, click the power button, and select Restart – if you continue to hold down the Shift key, you will have the option to Troubleshoot the computer. If you select Troubleshoot, then Advanced options, then Startup Settings, you will be able to select to start the computer in Safe Mode. Shutting off the computer when it is booting into Windows will often result in the next boot attempt taking the computer to the screen that allows selecting Troubleshoot, and eventually the option to start in Safe Mode. Once in Windows 10, holding the Windows flag key and pressing the X key on the keyboard has the same effect as right-clicking the Start button. A menu will appear permitting quick access to the Device Manager, Computer Management, Task Manager, and a variety of other computer administration tasks.

One of the tips that I found online for dealing with the INTERNAL_POWER_ERROR message was to quickly navigate to the Device Manager, and delete the Windows installed AMD Radeon HD 7600M item under the Display adapters heading. I tried that without success, and even tried deleting the Intel HD Graphics 4000 item under the Display adapters heading without success. Instructing Windows to scan for new devices resulted in two “Microsoft Basic Display Adapter” items being added under the Display adapters heading, leading to another blue screen after a couple of minutes.

Windows10DeviceManager1

Booting the computer to Safe Mode with Networking to do some troubleshooting, Windows 10 was stable. A web search using a different computer suggested that I needed to download the latest video card drivers for the AMD video card to fix the blue screen. I found that the new Edge web browser, which is the new default web browser on Windows 10, cannot start in Safe Mode, resulting in a message stating “This app can’t open. Microsoft Edge can’t be opened using the Built-in Administrator account. Sign in with a different account and try again.”  I was logged into the computer using my personal account, not the Built-In Administrator account, but I guess that detail did not matter.

Windows10CannotStartEdgeInSafeMode

Where did Microsoft hide Internet Explorer on Windows 10? Hold down the Windows key and press R. Type iexplore.exe and press the Enter key. Now, if only the wireless network worked in Safe Mode with Network Support! Trying again with the laptop connected by Cat 5e cable to a network switch, I managed to download the correct drivers. Sorry, cannot install AMD video card drivers in Safe Mode, “Failed to load detection driver.” Clicking OK caused the installer to hang at 100% complete. The previously registered Microsoft Word 2010 claimed that it could not verify that I was running an authentic version of Word 2010 while in Safe Mode, so the problem is not just with AMD when in Safe Mode.

Windows10CannotInstallATIDriversInSafeMode

Now what? Tell the computer to try reinstalling Windows 10 without installing updates – tried that, only to be greeted with a near instantaneous frowny face (a blue screen of death) upon completion. It took roughly 10 attempts to make it into the BIOS setup on the laptop (F2 did not work at the start of the boot) – I eventually found that applying about 200 pounds of pressure to the pink Assist button while powering on the Sony laptop allowed access to the BIOS setup. I found an option titled “Discrete Graphics Adapter” that was set to Enabled, so I changed that to Disabled, saved the changes, and managed to log into a stable copy of Windows 10… about 30 to 36 hours after the first download of Windows 10 initiated.

Windows10BIOSDisableDiscrete

After verifying that the computer worked fine with the Discrete Graphics Adapter disabled, I re-enabled that setting and made another attempt with the AMD graphics card.  The AMD graphics drivers installed without issue in Windows 10, but again resulted in a continuous reboot blue screen loop. I found that by quickly navigating to Device Manager after logon, I was able to set the AMD graphics device to Disabled before the computer would blue screen – once again Windows 10 was stable (and Microsoft Word 2010 worked OK too). Windows 10 installed various Windows updates, but simply refused to install the video driver for the Intel HD Graphics 4000 device.

Windows10WindowsUpdateCannotInstallIntelDrivers

As a result, Device Manager still shows a “Microsoft Basic Display Adapter” in Device Manager. Instructing Device Manager to update the drivers also failed.

Windows10DeviceManagerCannotInstallVideo

Even though the correct video card drivers are not installed, Windows 10 seems to work OK. Unfortunately, the problem where the touchpad would send random zoom in/out messages is still present – note the size of the zoomed icons in the desktop background in the following picture. I have yet to find a logical way to send the zoom back to normal on the desktop. The right side of the below picture shows the notification Action Center that is displayed by clicking the icon near the clock at the bottom-right of the screen.

Windows10VideoPlaybackNotifications2

The following picture shows that the Windows 8 new style apps are now able to appear on the desktop with regular Windows applications. I was shocked to see that Windows 8 would not permit overlapped windows for the new style apps – I think that the original release of Windows had the same problem, even though the Commodore Amiga from the same time period (mid to late 1980s) supported windows arranged on top of other windows. Cortana is shown at the left of the window in the picture below – apparently Cortana lost an argument with the Internet shortly before this screen capture was saved (Cortana is a bit evasive when asked “who are your programmers”). The Windows 10 scientific calculator appears at top-center, Microsoft Edge at bottom-center, Paint.Net at top-right, and the Microsoft Solitaire Collection app at the bottom right.

Windows10NewOldAppsNoInternet

As I stated above, overlapping Windows 8 apps are supported, and Cortana sent me to a web page about Easter Eggs – I thought that maybe that would be a good way to learn more about Cortana’s programmers. The speech input recognition seems to work very well in Cortana – as long as the Internet connection is not down.

Windows10OverlappingWindows

The picture below shows the new Start menu at the left – it is possible to stretch or narrow the Start menu to show more or less of the Windows 8 style live tiles on the Start menu. Changing settings is still a little confusing – for some settings it is necessary to click All Settings in the Action Center (the gray background window at the top-right), and in other cases it is necessary to use the Windows Vista style Control Panel (the white background window at the bottom-right).

Windows10StartMenuControlPanelAllSettings

The task view trick that was introduced with Windows Vista (Windows key and Tab key) still works, but has changed for the better. The Task View may also be opened by clicking the Task View button on the task bar at the bottom of the screen.

Windows10TaskView

The location of important settings seems to change with just about every new release of Windows. It would be nice if (almost) all settings could be found within a single interface. A trick that worked with older versions of Windows also works with Windows 10. Right-click the desktop (or inside any folder) and select New – Folder. Give that folder the following name:

GodMode.{ED7BA470-8E54-465E-825C-99712043E01C}

Double-clicking that folder to open it gives you just what you need (until the touchpad starts randomly sending zoom in/out messages).

Windows10GodMode1
Windows10GodMode2
Windows10GodMode3
Windows10GodMode4

Oh, it appears that the Oracle Database 11.2.0.3 database instance survived all of the upgrading nonsense, although it did take an exceptionally long time to start SQL Plus.

I have been working with computers for a long time – started back in the early 1980s. My new boss (almost five months now) at work for some reason thinks that I am a programmer… just a programmer? Last week I casually mentioned to him that I had read 30+ books about Oracle Database, and hinted that I co-authored another. I wonder if he has a clue about the books that I read about network administration (including a couple of CNE books in the late 1990s), operating systems, Microsoft Exchange (two for Exchange 2013, and other for previous versions), computer hardware troubleshooting, and a variety of other computer related skills. Well, at least he thinks that I am a programmer, and not just one who fixes computers (not that there is anything wrong with fixing computers, right Microsoft/Sony/AMD?).

As a suggestion, before you attempt a Windows 10 upgrade on a computer with an AMD video card, ask yourself if you have 24+ hours to spare and how much you like seeing frowns on blue screens. If the above is not enough of a warning, consider the before and after webcam photos captured by the Sony laptop’s webcam that follow.

Before picture: staring down Windows 8.1, trying to figure out why the window zoom keeps randomly changing:

Windows10Before

After Windows 10 is installed picture: note that more than the background changed.  I think that Cortana is stalking me now (why do I have a purple square target on my head?).  Just what is she doing in the middle of the night when the computer spontaneously turns on from a deep sleep?:

Windows10After

Humor aside, like Windows NT 4.0, once you get through the blue screens of death during the driver installs, Windows 10 will likely be rock solid stable.  I am already liking it more than Windows 8.1, even if it is not as fast on the same hardware.

Update August 4, 2015:

Below is a possible fix for the random zoom in/zoom out problem if your computer has a Synaptics touchpad.  This solution will probably work on Windows 8/8.1 also.  Near the bottom right of the screen, click the up pointing arrow to show all of the tray program icons.  There may be a tray icon named Synaptics Pointing Device that looks like a rectangle with two smaller rectangles below – click that tray icon, and then click Pointing Device Properties from the menu.  Note that if you also want to turn off the potentially annoying left-clicks due to touching the touchpad with a bit too much force, click the Tap to Click menu item to remove the checkmark in front of that item.

aWindows10FixUnintentionalZoom1

On the Device Settings tab, click the Settings button.  Remove the checkmark in front of Pinch Zoom, click the OK button, and then click the OK button in the Mouse Properties window.  This random zoom behavior has plagued the laptop for some time, but only became beyond bearable when the randomly activating feature affected the desktop icons in Windows 10.

Windows10FixUnintentionalZoom2

Update August 5, 2015:

If your desktop icons are super-sized as shown below (icons are shown at actual size) due to random zoom-in/zoom-out messages from your touchpad, there is a simple quick-fix (after shutting off this feature using the instructions above).  Right-click an open area of the desktop, select View and then Medium icons.

Windows10FixOverZoomedDesktopIcons

Microsoft released the first cumulative update KB3081424 for Windows 10 64 bit computers within the last hour – that update is being rolled out and installed automatically on all Windows 10 64 bit computers (Windows 10 computers that are joined to a domain may or may not automatically install this update).  After the update installed, I told the computer to go ahead and reboot.  After rebooting the computer, the Windows Start menu would not appear, Cortana would not respond, and the Action Center would not displayI saw this same behavior when testing the automatic updates in the various Technical Preview versions – in those cases I had to format the hard drive and reinstall Windows to recover.  Fortunately, in this case the functionality was restored by simply rebooting the computer (if this problem happens to you, there is no obvious way to tell Windows to reboot – just hold down Ctrl, then Alt, and press the Delete key once – a power option should appear near the bottom right of the screen to permit you to restart the computer).  This Start menu, Cortana, and Action Center functionality loss, if permanent and widespread, could cause a bit of havoc.

Windows10UpdateKillsStartMenu

Note that I am still fighting the Intel and AMD video card driver issues (Intel driver will not install, AMD driver installs with a blue screen).  I might have found a solution for the computer waking up unexpectedly in the middle of the night, but I will wait to post what I found until I confirm that the fix works.

Update 2 August 5, 2015:

Fixing the issue where the Intel HD Graphics 4000 device is listed as “Microsoft Basic Display Adapter” in Device Manager and Windows returns an error when installing the correct driver through Windows Update and Device Manager is a bit of a hassle.  The actual error message is not displayed when the driver fails to install.  If you experience this problem, immediately navigate to the C:\Windows\SoftwareDistribution\Download\Install folder, and locate a file that ends with .inf.  Right-click that file and select Install.  If Windows returns an error “There is no driver selected for the device information set or element.” that likely means that the company that set up the driver’s .inf file made an error in the file.

Windows10WindowsUpdateCannotInstallIntelDrivers2

The short-term solution for this problem is to download the Windows 7, 8, and 8.1 graphics driver package from the Intel website.  After a bit of searching, I found a suitable driver for the Intel HD Graphics 4000 device here.  After the installer completed, the computer had to restart – Device Manager then showed the HD Graphics 4000 device in place of the “Microsoft Basic Display Adapter” in Device Manager.  After the driver installation Windows Update still shows that the Windows 10 driver for the Intel graphics device fails to install, so there is still an issue that Microsoft/Intel need to rectify with the driver update.

Windows10FixIntel4000Driver

Now to tackle the blue screen of death caused by the AMD Radeon video driver…

Update August 10, 2015:

Peer-to-peer Windows Updates – potentially OK if the computer is connected to a trusted network, and the default Windows Update settings are adjusted; potentially dangerous at the default setting.  The default settings for Windows Updates essentially make your computer part of a world-wide Windows Update torrent.   If your computer is connected to a pay-as-you-go Internet provider, or if your Internet provider charges substantial fees for exceeding a certain monthly data cap (6GB per month, for instance), you should definitely modify the default update settings.  If you are concerned about your computer retrieving and automatically installing Windows updates from computers that have no association with Microsoft, other than the computers are running Windows 10, you should definitely modify the default update settings.  I was made aware of this particular issue when discussing Windows 10 with a relative who is an IT expert.  I also read a couple of articles today that described the torrent-like behavior of Windows 10 updates.

To fix the default setting, select Settings from the Start menu, then click Update & security.  Click Advanced options, then click Choose how updates are delivered.

Windows10DisableWindowsUpdateSharing1\

If your computer is always connected to a trusted network (never taken to a coffee shop, hotel, LAN party, etc.), consider changing the default setting to PCs on my local network – this setting could be beneficial if you have a pay-as-you-go or monthly capped Internet connection.  If you ever connect the computer to an untrusted network, consider changing the default On setting to Off.

Windows10DisableWindowsUpdateSharing2

Update August 29, 2015:

On August 5, 2015 I stated, ” I might have found a solution for the computer waking up unexpectedly in the middle of the night, but I will wait to post what I found until I confirm that the fix works.”  The fix that I implemented seems to have corrected this problem that has plagued the laptop for quite some time (possibly since new, nearly three years ago).  If your computer wakes up unexpectedly in the middle of the night, and you would like to end that behavior, click the Windows 10 Start Menu, then click Control Panel.  In the search box at the right type power and then click Edit power plan at the left.

Windows10DisableWakeUp1

In blue lettering near the top of the window you should see the words “Change settings for the plan: ” followed by the selected power plan name (High performance in the picture below) – make note of the selected power plan name.  Click Change advanced power settings.

Windows10DisableWakeUp2

Check to make certain that the same power plan appears selected in the list – if not, select the power plan that appeared in the previous window.  Click the + next to Sleep, then the + next to Allow wake timers.  Change both the On battery and Plugged in settings to Disable.  Finally, click OK to save the changed settings.  Note: it may be necessary to click the Change settings that are currently disabled link prior to making these changes.

Windows10DisableWakeUp3

I have now upgraded a computer from Windows 8.1 Pro (Ultimate?) to Windows 10 Pro (Sony laptop), another from Windows 8.1 Home to Windows 10 Home, another from Windows 7 Ultimate to Windows 10 Pro, and a fourth (and fifth) from Windows 7 Home to Windows 10 Home.  The Sony laptop has at least twice had a panic attack a couple of minutes after coming out of sleep.  During the panic attack the fan ramps up to maximum speed, the touch pad and keyboard stop responding, and the power button must be held in for 10 second to shut the computer off (the laptop does not go to sleep with a quick press of the power button).  The Sony computer has also crashed a couple of times with an irql_not_less_or_equal blue screen at least once in the last week.  The computer that was upgraded from Windows 7 Pro to Windows 10 Pro has spontaneously rebooted three times (twice within 30 minutes) while building Windows large icon thumbnails for a number of Apple Quicktime .mov video files.  The computer is connected to a true sign wave UPS, so I know that the reboot is not caused by a power problem (I was next to the computer each time the reboot happened, no blue screen, no notification entries in the Windows event logs except that the previous shut down was unexpected).  One of the computers upgraded from Windows 7 Home to Windows 10 Home is a Dell laptop where the G and H keys did not work correctly before or after the upgrade (some people have reported that this is a known intermittent problem with this particular Dell laptop model) – BIOS upgrades for the laptop refused to install following the upgrade to Windows 10 Home.  I do not yet have a status update from the other two upgraded computers.