Tag Archives: wordpress

Fail2ban notes

General notes about Fail2ban

### Fail2Ban ###

Best practise:
- do NOT edit /etc/fail2ban/jail.conf BUT create a new /etc/fail2ban/jail.local file

=============================================================
# Test fail2ban regex:
example: fail2ban-regex /var/log/secure /etc/fail2ban/filter.d/sshd.conf
example2: fail2ban-regex --print-all-matched/var/log/secure /etc/fail2ban/filter.d/sshd.conf

=============================================================
# Remove email notifications:

comment out 'sendmail-whois' from action in [ssh-iptables]
FYI, comment with # at the BEGINNING of the line like this or it won't work!!!

[ssh-iptables]

enabled  = true
filter   = sshd
action   = iptables[name=SSH, port=ssh, protocol=tcp]
#           sendmail-whois[name=SSH, dest=root, [email protected], sendername="Fail2Ban"]
logpath  = /var/log/secure
maxretry = 5


=============================================================
# Wordpress wp-login - block POST attacks

/etc/fail2ban/jail.local

[apache-wp-login]
enabled = true
port = http,https
filter = apache-wp-login
logpath = /var/log/httpd/blog.tian.it-access.log
maxretry = 3
bantime = 604800 ; 1 week
findtime = 120

----------------------------------------------------------------------------------------------------------------------

/etc/fail2ban/filter.d/apache-wp-login.conf
[Definition]
failregex = <HOST>.*POST.*wp-login.php HTTP/1.1
ignoreregex =

=============================================================

# Manually ban an IP:
fail2ban-client -vvv set <CHAIN> banip <IP>

# Check status of sshd chain
fail2ban-client status sshd

How to “SSH” brute force

If you want to make safer your remote server, it is good practise to use a good combination of sshd setup and fail2ban.

Firstly, you should setup your server to allow only key auth, and no passwords. This will drastically reduce the risk. This means anyway that you need to keep your ssh key safe and you won’t be able to access your server unless you have this key. Most of the time is something possible 🙂

For this reason, I’m explaining here how I configured my server.

SSHD

/etc/ssh/sshd_config

Have these settings in the config file (NOTE: the verbosity is for Fail2ban)

LogLevel VERBOSE

PasswordAuthentication no

(restart sshd)

/etc/fail2ban/jail.local

[DEFAULT]
# Ban hosts for 
# one hour:
#bantime = 3600

# one day:
bantime = 86400

# A host is banned if it has generated "maxretry" during the last "findtime"
# # seconds.
findtime  = 30

# # "maxretry" is the number of failures before a host get banned.
maxretry = 5

# Override /etc/fail2ban/jail.d/00-firewalld.conf:
banaction = iptables-multiport

[sshd]
enabled = true
filter = sshd-aggressive
port     = ssh
logpath  = /var/log/secure
maxretry = 3
findtime = 30
bantime  = 86400

/etc/fail2ban/filter.d/sshd.conf

Add a custom section after the ddos one:

custom = ^%(__prefix_line_sl)sDisconnected from <HOST> port [0-9]+ \[preauth\]$

This line matches whoever tries to connect without a proper ssh key.

Add this line to include custom to the sshd-aggressive setup:

aggressive = %(normal)s
             %(ddos)s
             %(custom)s

 

Apache ProxyPass for WordPress master-slave setup

Simple way

Ensure certain traffic goes to a certain server (master), you can use this:

<LocationMatch "^/wordpress/wp-admin/?.*>
ProxyPreserveHost On
ProxyPass http://ip.of.master.server/
</LocationMatch>

 


For a better setup with Variables, just follow the… following steps 🙂

Step One: Configure Environment

We need to setup some environment variables to get this to work correctly.
Add the following to your environment on the slave server(s):

RHEL/CentOS: /etc/sysconfig/httpdi

OPTIONS="-DSLAVE"
export MASTER_SERVER="SERVERIP HERE"

Ubuntu: /etc/apache2/envvars

export APACHE_ARGUMENTS="-DSLAVE"
export MASTER_SERVER="SERVERIP HERE"

Step Two: Configure your VirtualHost

In your VirtualHost configuration do something like the following.

<IfDefine SLAVE>
RewriteEngine On
ProxyPreserveHost On
ProxyPass /wp-admin/http://${MASTER_SERVER}/wp-admin/
ProxyPassReverse /wp-admin/http://${MASTER_SERVER}/wp-admin/

RewriteCond %{REQUEST_METHOD} =POST
RewriteRule . http://${MASTER_SERVER}%{REQUEST_URI} [P]
</IfDefine>

 

WordPress notes

Reset Admin Password

UPDATE wp_users SET user_pass=MD5('newpassword123') WHERE ID = 1;

Create New Admin account

mysql> INSERT INTO `wp_users` (`user_login`, `user_pass`, `user_nicename`, `user_email`, `user_url`, `user_status`, `display_name`) VALUES ('username', MD5('password'), 'friendly-name', '[email protected]', 'http://example.com', '0', 'Your Name');
mysql> SELECT LAST_INSERT_ID() INTO @userid;INSERT INTO `wp_usermeta` (`umeta_id`, `user_id`, `meta_key`, `meta_value`) VALUES (NULL, @userid, 'wp_capabilities', 'a:1:{s:13:"administrator";s:1:"1";}'), (NULL, @userid, 'wp_user_level', '10');

Show error in case white screen appears
Try adding this line to wp-config.php to see the errors on the page:

define( 'WP_DEBUG', true );

Change the site URL

mysql> SELECT * FROM wp_options WHERE option_name = 'siteurl' OR option_name = 'home' ;
mysql> UPDATE wp_options SET option_value = 'http://staging.mysite.com' WHERE option_name = 'siteurl' OR option_name = 'home' ;

Disable all plugins

mysql> UPDATE wp_options SET option_value = 'a:0:{}' WHERE option_name = 'active_plugins';

Show users and Privileges

mysql> SELECT user_login,user_registered,meta_value FROM wp_users INNER JOIN wp_usermeta ON wp_users.id = wp_usermeta.user_id and meta_key = 'wp_capabilities';
+---------------+---------------------+---------------------------------+
| user_login | user_registered | meta_value |
+---------------+---------------------+---------------------------------+
| administrator | 2013-12-21 10:36:30 | a:1:{s:13:"administrator";b:1;} |
| author | 2014-11-25 15:50:34 | a:1:{s:6:"author";b:1;} |
| editor | 2014-11-25 15:51:18 | a:1:{s:6:"editor";b:1;} |
| contributor | 2014-11-25 15:51:48 | a:1:{s:11:"contributor";b:1;} |
| subscriber | 2014-11-25 15:52:11 | a:1:{s:10:"subscriber";b:1;} |
+---------------+---------------------+---------------------------------+
5 rows in set (0.01 sec)

 

Update theme to Twenty Fourteen

mysql> UPDATE wp_options SET option_value = 'twentyfourteen' WHERE option_name = 'template' OR option_name = 'stylesheet';
mysql> UPDATE wp_options SET option_value = 'Twenty Fourteen' WHERE option_name = 'current_theme';

 

Administration Over SSL
Add the below lines to the wp-config.php file above the ‘/* That’s all, stop editing! Happy blogging. */’ line

define('FORCE_SSL_ADMIN', true);
define('FORCE_SSL_LOGIN', true);

This ensures the login AND the administration is done over SSL

You could also use the below .htaccess:

RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /(.*)\ HTTP/ [NC]
RewriteCond %{HTTPS} !=on [NC]
RewriteRule ^/?(wp-admin/|wp-login\.php) https://mysite.com%{REQUEST_URI}%{QUERY_STRING} [R=301,QSA,L]

 

Find out how many SQL queries are executed every time a page is loaded.
Add the below to one of the theme files, I usually add to footer.php

if ( current_user_can( 'manage_options' ) ) {
echo $wpdb->num_queries . " SQL queries performed.";
} else {
// Uncomment the below line to show SQL queries to everybody
// echo $wpdb->num_queries . " SQL queries performed.";
}

 

Here are some configuration parameters you can add to your wp-config.php file for FTP.

define('FS_METHOD', 'direct'); 
/*
forces the filesystem method. It should only be "direct", "ssh2", "ftpext", or "ftpsockets". Generally, you should only change this if you are experiencing update problems. If you change it and it doesn't help, change it back/remove it. Under most circumstances, setting it to 'ftpsockets' will work if the automatically chosen method does not.

(Primary Preference) "direct" forces it to use Direct File I/O requests from within PHP, this is fraught with opening up security issues on poorly configured hosts, This is chosen automatically when appropriate.
(Secondary Preference) "ssh2" is to force the usage of the SSH PHP Extension if installed
(3rd Preference) "ftpext" is to force the usage of the FTP PHP Extension for FTP Access, and finally
(4th Preference) "ftpsockets" utilises the PHP Sockets Class for FTP Access.
*/
define('FTP_BASE', '/var/www/vhosts/example.com/httpdocs/'); // is the full path to the "base"(ABSPATH) folder of the WordPress installation. 
define('FTP_CONTENT_DIR', '/var/www/vhosts/example.com/httpdocs/wp-content/'); // is the full path to the wp-content folder of the WordPress installation.
define('FTP_PLUGIN_DIR ', '/var/www/vhosts/example.com/httpdocs/plugins/'); // is the full path to the plugins folder of the WordPress installation. 
define('FTP_PUBKEY', '/var/www/vhosts/example.com/httpdocs/.ssh/id_rsa.pub'); // is the full path to your SSH public key. 
define('FTP_PRIKEY', '/var/www/vhosts/example.com/httpdocs/.ssh/id_rsa'); // is the full path to your SSH private key. 
define('FTP_USER', 'FTPusername'); // is the FTP username
define('FTP_PASS', 'FTPpassword'); // is the password for the FTP User
define('FTP_HOST', 'localhost'); // FTP Host - usually localhost.
define('FTP_SSL', false); // This is for "Secure FTP" not for SFTP.

xmlrpc.php

I’d recommend restricting xmlrpc.php POSTs to only IPs that need it by adding the following rules to the top of your .htaccess file, updating accordingly the line ‘allow from’ with a list of IPs space separated or simply completely remove that line to block its execution:

# ----------------------------------------------------
# Block WordPress xmlrpc.php requests
<Files xmlrpc.php>
order deny,allow
deny from all
allow from 123.123.123.123
</Files>
# ----------------------------------------------------

 

Linux ACL examples

Group permissions are NO LONGER related to group. It’s a MASK!

# setfacl -R -m u:apache:rwx html/
# getfacl html/
# file: html/
# owner: root
# group: root
user::rwx
user:alphausr:rwx
user:caesar:rwx
group::r-x
mask::rwx
other::r-x

To remove ACL as this is a temporary user and reinstate alphausr;

cd /var/www/; setfacl -R -b html/; setfacl -R -m u:alphausr:rwx html/


DEFAULT ACL
# setfacl -m d:u:apache:rwx html/

BACKUP
# getfacl -R /var/www/html/ > /root/html.perm

RESTORE (need to be in / )
# cd /
# setfacl –restore=/root/html.perm


ACL for WordPress

APACHE_ROOT=/var/www/vhosts/
SITE=mydomain.com
USERNAME=ftpuser

cd $APACHE_ROOT
setfacl -m d:u:apache:rwx .
setfacl -R -m u:apache:rwx .

find . -type d | xargs chmod 775
find . -type f | xargs chmod 664

chown -R $USERNAME $SITE

getfacl $SITE
# file: document_root
# owner: <username> <<<<<<< check this
# group: root
user::rwx <<<<<<< this
user:apache:rwx <<<<<<< and this 🙂
group::rwx
mask::rwx
other::r-x

Apache loop with WordPress and SSL cert installed on a Cloud Load Balancer

  • Terminate SSL onto the CLB
  • Change the main site URL to use HTTPS in the WordPress configuration
  • Add “SetEnvIf x-forwarded-proto https HTTPS=on” in the vhost configuration
  • add these in wp-config: [OPTIONAL]
    define(‘FORCE_SSL_ADMIN’, false);define(‘FORCE_SSL_LOGIN’, false);
    if (strpos($_SERVER[‘HTTP_X_FORWARDED_PROTO’], ‘https’) !== false)
    $_SERVER[‘HTTPS’]=’on’;


  • a good test to make sure PHP is receiving HTTPS are these lines in a test.php file. If should return “on” if PHP is getting HTTPS properly, or if it returns no value, PHP is not aware it’s being called over HTTPS.
    <?php
    printf($_SERVER['HTTPS'])
    ?>

     

WordPress Apache ProxyPass

Option 1

Ensure certain traffic goes to a certain server, you can use this:

<LocationMatch "^/wordpress/wp-admin/?.*>
        ProxyPreserveHost On
        ProxyPass http://ip.of.master.server/
</LocationMatch>

Option 2

Step One: Configure Environment

We need to setup some environment variables to get this to work correctly.
Add the following to your environment on the slave server(s):

RHEL/CentOS: /etc/sysconfig/httpd

OPTIONS="-DSLAVE"
export MASTER_SERVER="SERVERIP HERE"

Ubuntu: /etc/apache2/envvars

OPTIONS="-DSLAVE"
export MASTER_SERVER="SERVERIP HERE"

Step Two: Configure your VirtualHost

In your VirtualHost configuration do something like the following.

RewriteEngine On
ProxyPreserveHost On
ProxyPass /wp-admin/ http://${MASTER_SERVER}/wp-admin/
ProxyPassReverse /wp-admin/ http://${MASTER_SERVER}/wp-admin/

RewriteCond %{REQUEST_METHOD} =POST
RewriteRule . http://${MASTER_SERVER}%{REQUEST_URI} [P]

WordPress Useful commands

Reset Admin Password
UPDATE wp_users SET user_pass=MD5(‘newpassword123’) WHERE ID = 1;

Create New Admin account
mysql> INSERT INTO `wp_users` (`user_login`, `user_pass`, `user_nicename`, `user_email`, `user_url`, `user_status`, `display_name`) VALUES (‘username’, MD5(‘password’), ‘friendly-name’, ‘[email protected]’, ‘http://example.com’, ‘0’, ‘Your Name’);
mysql> SELECT LAST_INSERT_ID() INTO @userid;INSERT INTO `wp_usermeta` (`umeta_id`, `user_id`, `meta_key`, `meta_value`) VALUES (NULL, @userid, ‘wp_capabilities’, ‘a:1:{s:13:”administrator”;s:1:”1″;}’), (NULL, @userid, ‘wp_user_level’, ’10’);

Show error in case white screen appears
Try adding this line to wp-config.php to see the errors on the page:
define( ‘WP_DEBUG’, true );

Change the site URL
mysql> SELECT * FROM wp_options WHERE option_name = ‘siteurl’ OR option_name = ‘home’ ;
mysql> UPDATE wp_options SET option_value = ‘http://staging.walacea.com’ WHERE option_name = ‘siteurl’ OR option_name = ‘home’ ;

Disable all plugins
mysql> UPDATE wp_options SET option_value = ‘a:0:{}’ WHERE option_name = ‘active_plugins’;

Show users and Privileges
mysql> SELECT user_login,user_registered,meta_value FROM wp_users INNER JOIN wp_usermeta ON wp_users.id = wp_usermeta.user_id and meta_key = ‘wp_capabilities’;
+—————+———————+———————————+
| user_login    | user_registered     | meta_value                      |
+—————+———————+———————————+
| administrator | 2013-12-21 10:36:30 | a:1:{s:13:”administrator”;b:1;} |
| author        | 2014-11-25 15:50:34 | a:1:{s:6:”author”;b:1;}         |
| editor        | 2014-11-25 15:51:18 | a:1:{s:6:”editor”;b:1;}         |
| contributor   | 2014-11-25 15:51:48 | a:1:{s:11:”contributor”;b:1;}   |
| subscriber    | 2014-11-25 15:52:11 | a:1:{s:10:”subscriber”;b:1;}    |
+—————+———————+———————————+
5 rows in set (0.01 sec)

Update theme to Twenty Fourteen
mysql> UPDATE wp_options SET option_value = ‘twentyfourteen’ WHERE option_name = ‘template’ OR option_name = ‘stylesheet’;
mysql> UPDATE wp_options SET option_value = ‘Twenty Fourteen’ WHERE option_name = ‘current_theme’;

Administration Over SSL
Add the below lines to the wp-config.php file above the ‘/* That’s all, stop editing! Happy blogging. */’ line
define(‘FORCE_SSL_ADMIN’, true);
define(‘FORCE_SSL_LOGIN’, true);
This ensures the login AND the administration is done over SSL

You could also use the below htaccess:
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /(.*)\ HTTP/ [NC]
RewriteCond %{HTTPS} !=on [NC]
RewriteRule ^/?(wp-admin/|wp-login\.php) https://mysite.com%{REQUEST_URI}%{QUERY_STRING} [R=301,QSA,L]

Find out how many SQL queries are executed every time a page is loaded.
Add the below to one of the theme files, I usually add to footer.php
if ( current_user_can( ‘manage_options’ ) ) {
echo $wpdb->num_queries . ” SQL queries performed.”;
} else {
// Uncomment the below line to show SQL queries to everybody
// echo $wpdb->num_queries . ” SQL queries performed.”;
}

e.g. on my site when I’m logged in…

Here are some configuration parameters you can add to your wp-config.php file for FTP.
define(‘FS_METHOD’, ‘direct’);
/*
forces the filesystem method. It should only be “direct”, “ssh2”, “ftpext”, or “ftpsockets”. Generally, you should only change this if you are experiencing update problems. If you change it and it doesn’t help, change it back/remove it. Under most circumstances, setting it to ‘ftpsockets’ will work if the automatically chosen method does not.

(Primary Preference) “direct” forces it to use Direct File I/O requests from within PHP, this is fraught with opening up security issues on poorly configured hosts, This is chosen automatically when appropriate.
(Secondary Preference) “ssh2” is to force the usage of the SSH PHP Extension if installed
(3rd Preference) “ftpext” is to force the usage of the FTP PHP Extension for FTP Access, and finally
(4th Preference) “ftpsockets” utilises the PHP Sockets Class for FTP Access.
*/
define(‘FTP_BASE’, ‘/var/www/vhosts/example.com/httpdocs/’); // is the full path to the “base”(ABSPATH) folder of the WordPress installation.
define(‘FTP_CONTENT_DIR’, ‘/var/www/vhosts/example.com/httpdocs/wp-content/’); // is the full path to the wp-content folder of the WordPress installation.
define(‘FTP_PLUGIN_DIR ‘, ‘/var/www/vhosts/example.com/httpdocs/plugins/’); // is the full path to the plugins folder of the WordPress installation.
define(‘FTP_PUBKEY’, ‘/var/www/vhosts/example.com/httpdocs/.ssh/id_rsa.pub’); // is the full path to your SSH public key.
define(‘FTP_PRIKEY’, ‘/var/www/vhosts/example.com/httpdocs/.ssh/id_rsa’); // is the full path to your SSH private key.
define(‘FTP_USER’, ‘FTPusername’); // is the FTP username
define(‘FTP_PASS’, ‘FTPpassword’); // is the password for the FTP User
define(‘FTP_HOST’, ‘localhost’); // FTP Host – usually localhost.
define(‘FTP_SSL’, false); // This is for “Secure FTP” not for  SFTP.