In this post, I’ll detail a step by step process to set up WordPress on a fresh install of CentOS 7. SELinux will be enabled and in a virtual host.

After changing providers, I had to migrate my site and couldn’t find a guide that I was happy with. Some were not complete, some directly disabled SELinux, … so I chose to write my own. Also, disabling SELinux is a big NO-NO in my book.

The steps to follow are:

  1. Add users
  2. Install Apache
  3. Check and configure the firewall
  4. Install MySQL and PHP
  5. Create virtual hosts
  6. Configure SELinux
  7. Install WordPress

ADD USERS

First things first, we’ll add a user and add him to the sudoers. This way, we won’t need to be root.

As root user:

# adduser dummyuser
# passwd dummyuser

Add dummyuser to sudoers:

visudo

Find the following part:

## Allow root to run any commands anywhere
root ALL=(ALL) ALL

Add the following line after that:

dummyuser ALL=(ALL) ALL

Log off and log in again as dummyuser, and go on.

INSTALL APACHE

sudo yum install httpd

After the installation is complete, start Apache and check if it’s working or not.

sudo systemctl start httpd.service

Launch a browser and visit the root page of the server. You should see a welcome page.

SETTING UP THE FIREWALL

Whether it worked or not, it’s time to check firewall status:

firewall-cmd --state

If the result is running and you can see apache’s test site, you can skip the following steps up to the next section. Otherwise, we’ll have to configure and enable it.

A little safeguard in case we do something wrong and end up locked out of our server (can be safely skipped when working locally)

sudo shutdown -r 30

This will reboot the server in 30 minutes, losing every change that wasn’t permanent. In case you mess up and get locked out, go grab a coffe and carry on later. To cancel the reboot:

sudo shutdown -c

This safeguard is not strictly necessary, but I prefer to be safe when working remotely, and a reboot is allowed. You should be careful with timing. When it runs out, the server will reboot losing not saved changes. Adjust the timing to your needs or refresh the timing by cancelling the reboot and programming a new one.
I’ve taken up this practice when working with remote equipment (not your typical server room) Being looked out means someone commutes for 2 hours to work revert changes. Better safe than sorry.

If the firewall service was not running, we’ll check again with it running:

sudo systemctl start firewalld.service
firewall-cmd --state

Now we should have it in a running state.

firewall-cmd --get-default-zone

Returns public which is fine, as it’s the least trustworthy zone that actually allows some connections from the outside.

firewall-cmd --list-all

Returns the services allowed, that on a fresh install shoudl be few.

Now we’ll add http service:

sudo firewall-cmd --zone=public --add-service=http

This command should return success

Just to recheck, if we repeat firewall-cmd --list-all, it should have added http service.

As a bonus, firewall-cmd --get-services lists currently predefined services

Once tested that the configuration is correct, make all changes permanent so they’ll remain after a reboot:

sudo firewall-cmd --permanent --zone=public --add-service=http

This command should return success

Finally, enable the firewall so it starts on boot:

sudo systemctl enable firewalld

ENABLE APACHE ON BOOT

Now that apache is finaly working and accesible from the outside, enable it so it starts on boot:

sudo systemctl enable httpd.service

INSTALL MYSQL AND PHP

Now we’ll install the database and PHP.

sudo yum install mariadb-server mariadb
sudo systemctl start mariadb
mysql_secure_installation

This last step takes care of securing some defaults.
As it’s freshly installled, mysql will probably not have a password for it’s user root.

Let’s enable mysql on system start:

sudo systemctl enable mariadb.service

Now we’ll install PHP;

sudo yum install php php-cli php-common php-gd php-intl php-mbstring php-mysql php-pear php-pdo php-pecl-memcache php-xml

And restart apache:

systemctl restart httpd.service

CHECK IF PHP IS WORKING

Time to check if PHP is working correctly. Create a simple page with your editor of choice:

vi /var/www/html/info.php

And the actual content:

<?php
  phpinfo();
?>

Browse test page, and check whether mod_rewrite is enabled or not (it will come handy later)

After checking everythings working, remove the test file:

rm /var/www/html/info.php

CREATE APACHE VIRTUAL HOSTS

Create directory structure for Virtual Hosts:

sudo mkdir -p /var/www/vhosts/itanddevelopment.com/public_html
sudo mkdir -p /var/www/vhosts/example.com/public_html
sudo mkdir -p /var/www/vhosts/example2.com/public_html

Set up permissions:

sudo chown -R $USER:$USER /var/www/vhosts/itanddevelopment.com/public_html
sudo chown -R $USER:$USER /var/www/vhosts/example.com/public_html
sudo chown -R $USER:$USER /var/www/vhosts/example2.com/public_html

$USER will take the currently logged user value (no need to change it)

sudo chmod -R 755 /var/www

TEST VIRTUAL HOSTS

Time to test Virtual Hosts:
Create a demo page for each virtual host

vi /var/www/vhosts/itanddevelopment.com/public_html/index.html

A simple demo page:

<html>
  <head>
    <title>Virtual Host for itanddevelopment.com!</title>
  </head>
  <body>
    <h1>The itanddevelopment.com virtual host is working!</h1>
  </body>
</html>

Create a different file for each virtualhost you want to create, replace the name accordingly.

sudo mkdir /etc/httpd/sites-available
sudo mkdir /etc/httpd/sites-enabled
sudo vi /etc/httpd/sites-available/itanddevelopment.com.conf

As a simple example, this .conf could look like:

<VirtualHost *:80>
ServerName www.itanddevelopment.com
ServerAlias itanddevelopment.com
DocumentRoot /var/www/vhosts/itanddevelopment.com/public_html
ErrorLog /var/www/vhosts/itanddevelopment.com/logs/error.log
CustomLog /var/www/vhosts/itanddevelopment.com/logs/requests.log combined
</VirtualHost>

For each virtual host, add the corresponding .conf file.

ENABLING VIRTUAL HOSTS

To enable virtual hosts:

sudo vi /etc/httpd/conf/httpd.conf

Add at the end:

# Include virtual hosts
IncludeOptional sites-enabled/*.conf

To make a site go live, make a link from sites-available to sites-enabled:

sudo ln -s /etc/httpd/sites-available/itanddevelopment.com.conf /etc/httpd/sites-enabled/itanddevelopment.com.conf

And restart Apache:

sudo systemctl restart httpd.service

For disabling a site, just remove the link and restart Apache.

SELINUX CONFIGURATION FOR VIRTUAL HOSTS

SELinux won’t allow httpd to write in our custom log location. Now we’ll configure it properly:

sudo yum install setools setroubleshoot

sudo semanage fcontext -a -t httpd_sys_content_t "itanddevelopment.com(/.*)?"
sudo semanage fcontext -a -t httpd_log_t "itanddevelopment.com/logs(/.*)?"
sudo restorecon -Rv itanddevelopment.com

sudo semanage fcontext -a -t httpd_sys_content_t "example.com(/.*)?"
sudo semanage fcontext -a -t httpd_log_t "example.com/logs(/.*)?"
sudo restorecon -Rv example.com

sudo semanage fcontext -a -t httpd_sys_content_t "example2(/.*)?"
sudo semanage fcontext -a -t httpd_log_t "example2/logs(/.*)?"
sudo restorecon -Rv example2.com

TROUBLESHOOTING SELINUX

If there’s any problem with SELinux policies

sealert -a /var/log/audit/audit.log

will parse the log and provide hints at problems and possible solutions.

If you are note sure whether the problem is SELinux related or not, the policy can be set to permissive, so the system will not enforce the SE policies but will log them.

sudo setenforce permissive

To check those logs:

sealert -a /var/log/audit/audit.log

will parse SELinux related problems and propose solutions.
Finally, remember to ALWAYS set to enforcing mode.

sudo setenforce enforcing

INSTALL WORDPRESS

Finally, it’s time to install WordPress. The first step will be to create the database:

mysql -u root -p
CREATE DATABASE wordpress;
CREATE USER wordpressuser@localhost IDENTIFIED BY 'wpuserpassword';
GRANT ALL PRIVILEGES ON wordpress.* TO wordpressuser@localhost IDENTIFIED BY 'wpuserpassword';
FLUSH PRIVILEGES;
EXIT;

This will ask for root password that was set up while securing the database. wordpress wordpressuser wpuserpassword can should be changed to your needs.

Now, we’ll download the latest WordPress version:

cd
wget http://wordpress.org/latest.tar.gz
tar -zxvf latest.tar.gz

Next, create the final directory, copy WordPress and create Uploads directory:

sudo mkdir /var/www/vhosts/itanddevelopment.com/blog.itanddevelopment.com
sudo chown dummyuser:apache blog.itanddevelopment.com

sudo rsync -avP /home/dummyuser/wordpress/ /var/www/vhosts/itanddevelopment.com/blog.itanddevelopment.com/

mkdir /var/www/html/wp-content/uploads

FINAL TOUCHES TO CONFIGURATION

cd /var/www/vhosts/itanddevelopment.com/blog.itanddevelopment.com
cp wp-config-sample.php wp-config.php
vi wp-config.php

Change DB_NAME DB_USER DB_PASSWORD to your version of wordpress wordpressuser wpuserpassword
Change the authentication keys and salts to the values you get in: WordPress API Salt

Optionally, $table_prefix can be modified. It will usually not be neccesary, but if you intend on sharing the same database with multiple wordpress installations, this identifier must be unique and different for all of them.

Apache should be allowed to write on wp-content so SELinux context should be modified:

sudo semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/vhosts/itanddevelopment.com/blog.itanddevelopment.com/wp-content(/.*)?"
sudo restorecon -Rv ./itanddevelopment.com

During the installation, Apache should be able to modify wp-config.php

sudo semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/vhosts/itanddevelopment.com/blog.itanddevelopment.com/wp-config.php"
sudo restorecon -Rv ./itanddevelopment.com

Now, WordPress should be up and running, ready to configure from the web interface with your preferred theme, plugins, users, … Hint: If an error appears asking for FTP user / password credentials when installing new plugins or themes, modify wp-config.php and add the following:


/** Error installing plugins and themes. System asks for FTP user / password
*/
define('FS_METHOD', 'direct');

Optionally (but I would recommend it), after your changes have taken place, you should return wp-config.php to it’s previous context, so no writing will happen without your knowledge.

sudo semanage fcontext -a -t httpd_sys_content_t "/var/www/vhosts/itanddevelopment.com/blog.itanddevelopment.com/wp-config.php"
sudo restorecon -Rv ./itanddevelopment.com

Happy Blogging

Did you find the post useful? Would you invite me to a nice coffe or offer some help running the site ad-free?
1EpMM5szeNSSYxgDKBvksyCCizKHoAZmBs

asterisk_logoDid you ever wanted an automatic failover solution for Asterisk? With just a few seconds downtime? Here’s how you do it.

This setup consists of the following steps:

  1. Download and instal AsteriskNow image (a bundle with Asterisk and FreePBX ready to deploy)
  2. Download and install keepalived daemon to get IP switchover
  3. Set up MySQL Master – Master replication
  4. Enjoy peace of mind 🙂

I assume your setup will be AsteriskNow based (which is Red Hat / CentOS based) but it should be painlessly applied to other Linux distributions. Also, reserve 3 or more IP addresses, one for each Asterisk instance and the third one for the cluster. Finally, the commands / edits will be done as root. This is NOT a best practice, but I want to show an easy setup and I won’t assume your user is configured as sudoer.

DOWNLOAD AND INSTALL ASTERISKNOW

We’ll go with AsteriskNow as it’s a “ready to go” Asterisk + FreePBX bundle which can be downloaded at the Asterisk’s official site: Download AsteriskNow

Burn and install / Boot from the iso and follow the installation procedure. In the installation, you’ll have to specify a fixed IP address (we’ll use it later for mysql replication), but remember not to use the one the one for the cluster. The third (cluster) IP must be unused.

Now, go on and install the two AsteriskNow instances.

SETTING UP FAILOVER WITH KEEPALIVED

For this step, we’ll use keepalived. It’s available in the repos, but in case you are on another setup or you want some more information, you can read more about keepalived here.

Now, login into your primary Asterisk instance and install and configure keepalived:

 

# yum install keepalived

# cd /etc/keepalived/

# vi keepalived.conf

Change the bold marked parameters (higher priority means it’ll be the primary server):

vrrp_instance VI_1 {

interface eth0

state MASTER

virtual_router_id 51

priority 100

authentication {

auth_type PASS

auth_pass Add-Your-Password-Here

}

virtual_ipaddress {

192.168.15.23

172.12.1.45

}

}

Hints:

  • Priority: Higher priority means the server will be used if alive. It must be different in the primary and the secondary server

  • auth_pass: It only takes into account 8 characters

  • virtual_ipaddress: Those will be the cluster addresses and must be the same for primary and secondary server. It can be one or more addresses which can be on different IP ranges or even on another network interface. There’s quite some info for more advanced setups (also, there’s always #man keepalived.conf). We are going for a simple working setup

A SIMPLE WORKING KEEPALIVED CONFIGURATION FOR PRIMARY SERVER

Remember to configure you SMTP server and change bold parameters to adapt to your particular setup.

! Configuration File for keepalived

global_defs {

notification_email {

voip@example.com

failover@example.com

sysadmin@example.com

}

notification_email_from server.asterisk@example.com

smtp_server xxx.yyy.zzz.aaa

smtp_connect_timeout 30

router_id LVS_DEVEL

}

vrrp_instance VI_1 {

state MASTER

interface eth0

virtual_router_id 51

priority 101

advert_int 1

authentication {

auth_type PASS

auth_pass hunter2

}

virtual_ipaddress {

192.168.143.200

}

}

auth_pass must be the same in both servers, but please, change it. You don’t want to be THAT guy with hunter2 as a password.

A SIMPLE WORKING KEEPALIVED CONFIGURATION FOR SECONDARY SERVER

Remember to configure you SMTP server and change bold parameters to adapt to your particular setup.

! Configuration File for keepalived

global_defs {

notification_email {

voip@example.com

failover@example.com

sysadmin@example.com

}

notification_email_from server.asterisk@example.com

smtp_server xxx.yyy.zzz.aaa

smtp_connect_timeout 30

router_id LVS_DEVEL

}

vrrp_instance VI_1 {

state MASTER

interface eth0

virtual_router_id 51

priority 100

advert_int 1

authentication {

auth_type PASS

auth_pass hunter2

}

virtual_ipaddress {

192.168.143.200

}

}

TESTING FAILOVER WITH KEEPALIVED

Starting with the secondary server, you’ll have to start the service manually and once it’s tested, configure it to start on boot.

Let’s start the service to check for startup errors

# service keepalived start

Optionally, to check virtual IP and switchover info:

# tail -f /var/log/messages

From a third PC, launch a ping to your virtual IP to see if it’s alive (it should be) and leave it running.

Now start the service on the primary server and optionally take a look at switchover information:

# service keepalived start

# tail -f /var/log/messages

The running ping might have lost a couple of packets during the switchover, but by now, the primary server should have the cluster IP assigned.

Finally, configure keepalive to automatically run on boot on both servers

# chkconfig keepalived on

CONFIGURING MYSQL MASTER MASTER REPLICATION

Now that we have an automatic failover mechanism in place, it’s time to set up MySQL redundancy.

MODIFY MYSQL CONFIGURATION (MY.CNF)

The first step will be to modify MySQL configuration following those steps:

  • Enable remote conections

  • Identify the server. As a default when it’s not otherwise said, it takes a value of 1 so it’s better to take another id

  • Location of binary log

  • Explicitly add which databases we want to replicate

  • Explicitly add which databases we don’t want to replicate. This step shouldn’t be necessary but I’ve had some passwords related problems because mysql system database got replicated.

The configuration file is llocated at /etc/my.cnf and you should have something similar to the following:

Initial configuration:

[mysqld]

bind-address = 127.0.0.1

general_log = 0

general_log_file = /var/log/mysql/mysql.log

datadir=/var/lib/mysql

socket=/var/lib/mysql/mysql.sock

user=mysql

# Disabling symbolic-links is recommended to prevent assorted security risks

symbolic-links=0

[mysqld_safe]

log-error=/var/log/mysqld.log

pid-file=/var/run/mysqld/mysqld.pid

Final working setup for primary server:

[mysqld]

#bind-address = 127.0.0.1

server-id = 2

log_bin = /var/log/mysql/mysql-bin.log

binlog_do_db = asterisk

binlog_do_db = asteriskcdrdb

binlog_ignore_db = mysql

general_log = 0

general_log_file = /var/log/mysql/mysql.log

datadir=/var/lib/mysql

socket=/var/lib/mysql/mysql.sock

user=mysql

# Disabling symbolic-links is recommended to prevent assorted security risks

symbolic-links=0

[mysqld_safe]

log-error=/var/log/mysqld.log

pid-file=/var/run/mysqld/mysqld.pid

Restart mysql service:

# service mysqld restart

And we’re done with the primary server. For the secondary server, repeat the steps remembering to change server-id to 3 or any other value.

Final working setup for secondary server:

[mysqld]

#bind-address = 127.0.0.1

server-id = 3

log_bin = /var/log/mysql/mysql-bin.log

binlog_do_db = asterisk

binlog_do_db = asteriskcdrdb

binlog_ignore_db = mysql

general_log = 0

general_log_file = /var/log/mysql/mysql.log

datadir=/var/lib/mysql

socket=/var/lib/mysql/mysql.sock

user=mysql

# Disabling symbolic-links is recommended to prevent assorted security risks

symbolic-links=0

[mysqld_safe]

log-error=/var/log/mysqld.log

pid-file=/var/run/mysqld/mysqld.pid

Restart mysql service:

# service mysqld restart

ADDING AND CONFIGURING REPLICATION USER

Log into primary server and add the user which will do the replication:

# mysql -u root -p

mysql> create user 'replicator'@'%' identified by 'ChangeMePlease!!!!!';

Query OK, 0 rows affected (0.00 sec)

Add permission for replication to the user:

mysql> grant replication slave on *.* to 'replicator'@'%';

Query OK, 0 rows affected (0.00 sec)

As far as I can tell, it’s not possible to grant permissions to specific databases for this to work. Now, let’s get information from which point on the primary server we want to replicate on the secondary server:


mysql> show master status;

FilePositionBinlog_Do_DBBinlog_Ignore_DB
mysql-bin.000001254163asterisk,asteriskcdrdbmysql
1 row in set (0.00 sec)

The important parts are File and Position. Take note of them as we’ll need them later.

Log into secondary server and add the user which will do the replication:

# mysql -u root -p

mysql> create user 'replicator'@'%' identified by 'ChangeMePlease!!!!!';

Query OK, 0 rows affected (0.00 sec)

Add permission for replication to the user:


mysql> grant replication slave on *.* to 'replicator'@'%';

Query OK, 0 rows affected (0.00 sec)

SETTING UP MYSQL MASTER MASTER REPLICATION

Now, on the secondary server, we’ll configure replication from primary to secondary server (remember to use server’s fixed IP address and not the virtual one):

mysql> slave stop;

Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> CHANGE MASTER TO MASTER_HOST = '192.168.143.201', MASTER_USER = 'replicator', MASTER_PASSWORD = 'ChangeMePlease!!!!!', MASTER_LOG_FILE = 'mysql-bin.000001', MASTER_LOG_POS = 254163;

Query OK, 0 rows affected (0.03 sec)

mysql> slave start;

Query OK, 0 rows affected (0.00 sec)

Still on the secondary server, we’ll get information for setting up secondary to primary replication:

mysql> show master status;

FilePositionBinlog_Do_DBBinlog_Ignore_DB
mysql-bin.000001383123asterisk,asteriskcdrdbmysql
1 row in set (0.00 sec)

Finally, set up replication on the primary server from secondary to primary (remember to use server's fixed IP address and not the virtual one):

mysql> slave stop;

Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> CHANGE MASTER TO MASTER_HOST = '192.168.143.202', MASTER_USER = 'replicator', MASTER_PASSWORD = 'ChangeMePlease!!!!!', MASTER_LOG_FILE = 'mysql-bin.000001', MASTER_LOG_POS = 383123;

Query OK, 0 rows affected (0.02 sec)

mysql> slave start;

Query OK, 0 rows affected (0.00 sec)

That’s it. Easy, isn’t it? Now you can go on and configure your installation.

FINAL THOUGHTS, NEXT STEPS AND HELPFUL LINKS

  • Depending on the client used, I’ve got under 5 seconds downtime.
  • Passwords, too open configuration …. Please change the passwords stated here. Also, the configuration can (and should) be thightened.
  • You can configure the whole installation in one server and the configuration will be synchronized at FreePBX level. You must apply changes on the secondary server for Asterisk to notice them (there are workarounds but see next point)
  • If you can afford it (if you need HA, it’s probable you can afford this), please consider purchasing FreePBX HA module. This way, they get paid so they can go on developing and everyone benefits on the long run (Disclaimer: I have no relation with FreePBX. I just think they’re developing an amazing piece of software)
  • Recordings, if you use them, they are (still) not synchronized on the filesystem. They get stored on the server which is running in the moment the call is made. You’ll have to set up a synchronization mechanism

Did you find the post useful? Would you invite me to a nice coffe or offer some help running the site ad-free?
1EpMM5szeNSSYxgDKBvksyCCizKHoAZmBs

LINKS I USED FOR SETTING EVERYTHING UP

keepalived

https://raymii.org/s/tutorials/Keepalived-Simple-IP-failover-on-Ubuntu.html

http://www.cyberciti.biz/faq/rhel-centos-fedora-keepalived-lvs-cluster-configuration/

http://tecadmin.net/ip-failover-setup-using-keepalived-on-centos-redhat-6/#

http://vmhacks.com/ip-address-activepassive-failover-setup-in-linux-centos-6-rhel-6-using-keepalived/

MySQL

http://dba.stackexchange.com/questions/72058/master-master-replication-with-3-nodes

https://www.digitalocean.com/community/tutorials/how-to-set-up-mysql-master-master-replication

Asterisk

http://www.asteriskdocs.org/en/2nd_Edition/asterisk-book-html-chunk/asterisk-APP-A-SECT-2.html

Nov 1, 2016