If you changed your data location for mysql / mariadb, using a new disk and get an error like this after a start or restart:

# systemctl start mariadb
Job for mariadb.service failed because the control process exited with error code.
See "systemctl status mariadb.service" and "journalctl -xe" for details.

Here’s the extended status:

# systemctl status mariadb
 mariadb.service - MariaDB 10.1 database server
Loaded: loaded (/usr/lib/systemd/system/mariadb.service; enabled; vendor preset: disabled)
Active: failed (Result: exit-code) since Tue 2016-03-29 11:49:49 CDT; 3s ago
Process: 5233 ExecStopPost=/usr/libexec/mysql-wait-stop (code=exited, status=0/SUCCESS)
Process: 5219 ExecStart=/usr/libexec/mysqld --basedir=/usr $MYSQLD_OPTS $_WSREP_NEW_CLUSTER (code=exited, status=1/FAILURE)
Process: 5185 ExecStartPre=/usr/libexec/mysql-prepare-db-dir %n (code=exited, status=0/SUCCESS)
Process: 5162 ExecStartPre=/usr/libexec/mysql-check-socket (code=exited, status=0/SUCCESS)
Main PID: 5219 (code=exited, status=1/FAILURE)
Status: "MariaDB server is down"

Mar 29 11:49:48 db1 systemd[1]: Starting MariaDB 10.1 database server...
Mar 29 11:49:49 db1 mysqld[5219]: 2016-03-29 11:49:49 140524370049152 [Note] /usr/libexec/mysqld (mysqld 10.1.12-MariaDB) starting as process 5219 ...
Mar 29 11:49:49 db1 mysqld[5219]: 2016-03-29 11:49:49 140524370049152 [Warning] Can't create test file /var/lib/mysql/db1.lower-test
Mar 29 11:49:49 db1 systemd[1]: mariadb.service: main process exited, code=exited, status=1/FAILURE
Mar 29 11:49:49 db1 systemd[1]: Failed to start MariaDB 10.1 database server.
Mar 29 11:49:49 db1 systemd[1]: Unit mariadb.service entered failed state.
Mar 29 11:49:49 db1 systemd[1]: mariadb.service failed

It more than likely has everything to do with SELinux running.  To check, do this:

# getenforce
Enforcing

Check to see if this is the issue and temporarily allow us to allow access:

root@db1 /etc/my.cnf.d# semanage permissive -a mysqld_t

Start MariaDB:

root@db1 /etc/my.cnf.d# systemctl start mariadb

Looks good, let’s check the status:

root@db1 /etc/my.cnf.d# systemctl -l status mariadb
 mariadb.service - MariaDB 10.1 database server
Loaded: loaded (/usr/lib/systemd/system/mariadb.service; enabled; vendor preset: disabled)
Active: active (running) since Tue 2016-03-29 11:52:07 CDT; 13s ago
Process: 5233 ExecStopPost=/usr/libexec/mysql-wait-stop (code=exited, status=0/SUCCESS)
Process: 5493 ExecStartPost=/usr/libexec/mysql-check-upgrade (code=exited, status=0/SUCCESS)
Process: 5430 ExecStartPre=/usr/libexec/mysql-prepare-db-dir %n (code=exited, status=0/SUCCESS)
Process: 5407 ExecStartPre=/usr/libexec/mysql-check-socket (code=exited, status=0/SUCCESS)
Main PID: 5464 (mysqld)
Status: "Taking your SQL requests now..."
CGroup: /system.slice/mariadb.service
└─5464 /usr/libexec/mysqld --basedir=/usr

Mar 29 11:52:06 db1 systemd[1]: Starting MariaDB 10.1 database server...
Mar 29 11:52:07 db1 mysqld[5464]: 2016-03-29 11:52:07 140638769318016 [Note] /usr/libexec/mysqld (mysqld 10.1.12-MariaDB) starting as process 5464 ...
Mar 29 11:52:07 db1 mysql-check-upgrade[5493]: The datadir located at /var/lib/mysql needs to be upgraded using 'mysql_upgrade' tool. This can be done using the following steps:
Mar 29 11:52:07 db1 mysql-check-upgrade[5493]: 1. Back-up your data before with 'mysql_upgrade'
Mar 29 11:52:07 db1 mysql-check-upgrade[5493]: 2. Start the database daemon using 'service mariadb start'
Mar 29 11:52:07 db1 mysql-check-upgrade[5493]: 3. Run 'mysql_upgrade' with a database user that has sufficient privileges
Mar 29 11:52:07 db1 mysql-check-upgrade[5493]: Read more about 'mysql_upgrade' usage at:
Mar 29 11:52:07 db1 mysql-check-upgrade[5493]: https://mariadb.com/kb/en/mariadb/documentation/sql-commands/table-commands/mysql_upgrade/
Mar 29 11:52:07 db1 systemd[1]: Started MariaDB 10.1 database server.

SIDE NOTE:  Looks like we need to upgrade our disk mounted at /var/lib/mysql now:

# mysql_upgrade -hlocalhost -uroot -pxxxxxxxxxxxxxx
Phase 1/6: Checking and upgrading mysql database
Processing databases
mysql
mysql.columns_priv OK
mysql.db OK
mysql.event OK
mysql.func OK
mysql.help_category OK
mysql.help_keyword OK
mysql.help_relation OK
mysql.help_topic OK
mysql.host OK
mysql.ndb_binlog_index OK
mysql.plugin OK
mysql.proc OK
mysql.procs_priv OK
mysql.proxies_priv OK
mysql.servers OK
mysql.tables_priv OK
mysql.time_zone OK
mysql.time_zone_leap_second OK
mysql.time_zone_name OK
mysql.time_zone_transition OK
mysql.time_zone_transition_type OK
mysql.user OK
Phase 2/6: Fixing views
Phase 3/6: Running 'mysql_fix_privilege_tables'
Phase 4/6: Fixing table and database names
Phase 5/6: Checking and upgrading tables
Processing databases
information_schema
performance_schema
Phase 6/6: Running 'FLUSH PRIVILEGES'
OK

Now, let’s permanently & persistently allow for SELinux to allow us to write to the new disk:

Start by temporarily putting mysqld_t into permissive mode (should already be there):

# semanage permissive -a mysqld_t

Now, we record what MariaDB is doing and to create a policy to allow that (but nothing else).

Switch SELinux to permissive mode and remove dontaudits from the policy:

# semodule -DB
# semanage permissive -a mysqld_t

Start MariaDB, then use the generated audit log to create a policy:

# grep mysqld /var/log/audit/audit.log | audit2allow -M mariadb_local
# semodule -i mariadb_local.pp

Let’s adjust the file system labelling so that SELinux knows this is MariaDB’s datadir. Check with:

# ls -ldaZ /datadir
drwxr-xr-x. root root unconfined_u:object_r:var_t:s0
# semanage fcontext -a -t mysqld_db_t "/var/lib/mysql(/.*)?"
# restorecon -Rv /var/lib/mysql

You can check what’s in the policy here:

# semanage fcontext -l -C
SELinux fcontext                                   type               Context

/var/lib/mysql(/.*)?                               all files          system_u:object_r:mysqld_db_t:s0 

SELinux Local fcontext Equivalence 

/opt/remi/php70/root = /
/var/opt/remi/php70 = /var
/etc/opt/remi/php70 = /etc

Now, remove the permissive mode for mysqld_t and restore dontaudits:

# semodule -B
# semanage permissive -d mysqld_t

 

This list of rules by no means is a sure bet to secure your web services, but it will help in preventing script-kiddings from doing some basic browsing around.

MySQL injection attempts are one of the most common hacking attacks against PHP websites. If your website is hosted on a dedicated or virtual server, the best solution is to your server hardened with proper mod_security rules. However, if you’re on shared hosting, this is not an option. If you now think that it’s not possible to protect your website against various hacking methods on shared hosting, you’re wrong. Although it’s not possible to use advanced strategies to protect your website, you’re still able to protect it against hacking attempts using .htaccess rules. To implement such a protection, append your current .htaccess file with the following code, or create a new file called .htaccess, if you don’t use any yet, and place it in your website’s main folder):

Beginning of your .htaccess file to set the basics up

# Block access to the .htaccess file
<files .htaccess>
order allow,deny
deny from all
</files>

# No web server version and indexes
ServerSignature Off
Options -Indexes
Options FollowSymLinks

HTTP Headers to Help Secure Your Website

Preventing cross-site request forgery (CSRF) attacks is hard and web applications must be built to prevent CSRF vulnerabilities. The first vulnerability is cross-site scripting (XSS).

Around 70.000 web sites have been catalogued by XSSed as being vulnerable to cross-site scripting (XSS). These attacks leave your users open to cookie theft, information theft, account hijacking, clickjacking and more.

Modern web browsers have some powerful protection build in nowadays but you need to tell the browser that you want those protection mechanisms used for your website. This can be archived by setting specific HTTP headers.

X-Frame-Options

The X-Frame-Options HTTP response header can be used to indicate whether or not a browser should be allowed to render a page in a <frame> or <iframe>. This can be used to avoid clickjacking attacks, by ensuring that your content is not embedded into other sites.

This directive is pretty similar to the frame buster code explained in “Double Trouble on Google Images” except that it is only supported in the following browsers:

  • Internet Explorer 8+
  • Opera 10.50+
  • Safari 4+
  • Chrome 4.1.249.1042+
  • Firefox 3.6.9+ (or earlier with NoScript)

There are three possible values for this header:

  1. DENY – This setting prevents any pages served from being placed in a frame even if it is on the same website it originates from. should be used if you never intend for your pages to be used inside of a frame.
  2. SAMEORIGIN – This setting allows pages to be served in a frame of a page on the same website. If an external site attempts to load the page in a frame the request will be denied.
  3. ALLOW-FROM origin – If the value contains the token ALLOW-FROM origin, the browser will block rendering only if the origin of the top-level browsing context is different than the origin value supplied with the Allow-From directive.

The code below sets the directive to DENY, preventing our pages from being served in any frames, even from our own website.

# drop Range header when more than 5 ranges.
# CVE-2011-3192
SetEnvIf Range (,.*?){5,} bad-range=1
RequestHeader unset Range env=bad-range
# optional logging.
#CustomLog insert-path-and-name-of-log common env=bad-range

# Don't allow any pages to be framed - Defends against CSRF
Header set X-Frame-Options DENY

# prevent mime based attacks
Header set X-Content-Type-Options "nosniff"

# Only allow JavaScript from the same domain to be run.
# Don't allow inline JavaScript to run.
Header set X-Content-Security-Policy "allow 'self';"

# Turn on IE8-IE9 XSS prevention tools
Header set X-XSS-Protection "1; mode=block"

MySQL Injection Prevention:

MySQL injection attempts are one of the most common hacking attacks against PHP websites. If your website is hosted on a dedicated or virtual server, the best solution is to your server hardened with proper mod_security rules. However, if you’re on shared hosting, this is not an option. If you now think that it’s not possible to protect your website against various hacking methods on shared hosting, you’re wrong. Although it’s not possible to use advanced strategies to protect your website, you’re still able to protect it against hacking attempts using .htaccess rules. To implement such a protection, append your current .htaccess file with the following code, or create a new file called .htaccess, if you don’t use any yet, and place it in your website’s main folder):

<IfModule mod_rewrite.c>
# Enable rewrite engine
RewriteEngine On

# Block suspicious request methods
RewriteCond %{REQUEST_METHOD} ^(HEAD|TRACE|DELETE|TRACK|DEBUG) [NC]
RewriteRule ^(.*)$ - [F,L]

# Block WP timthumb hack
RewriteCond %{REQUEST_URI} (timthumb\.php|phpthumb\.php|thumb\.php|thumbs\.php) [NC]
RewriteRule . - [S=1]

# Block suspicious user agents and requests
RewriteCond %{HTTP_USER_AGENT} (libwww-perl|wget|python|nikto|curl|scan|java|winhttp|clshttp|loader) [NC,OR]
RewriteCond %{HTTP_USER_AGENT} (<|>|'|%0A|%0D|%27|%3C|%3E|%00) [NC,OR]
RewriteCond %{HTTP_USER_AGENT} (;|<|>|'|"|\)|\(|%0A|%0D|%22|%27|%28|%3C|%3E|%00).*(libwww-perl|wget|python|nikto|curl|scan|java|winhttp|HTTrack|clshttp|archiver|loader|email|harvest|extract|grab|miner) [NC,OR]
RewriteCond %{THE_REQUEST} \?\ HTTP/ [NC,OR]
RewriteCond %{THE_REQUEST} \/\*\ HTTP/ [NC,OR]
RewriteCond %{THE_REQUEST} etc/passwd [NC,OR]
RewriteCond %{THE_REQUEST} cgi-bin [NC,OR]
RewriteCond %{THE_REQUEST} (%0A|%0D) [NC,OR]

# Block MySQL injections, RFI, base64, etc.
RewriteCond %{QUERY_STRING} [a-zA-Z0-9_]=http:// [OR]
RewriteCond %{QUERY_STRING} [a-zA-Z0-9_]=http%3A%2F%2F [OR]
RewriteCond %{QUERY_STRING} [a-zA-Z0-9_]=(\.\.//?)+ [OR]
RewriteCond %{QUERY_STRING} [a-zA-Z0-9_]=/([a-z0-9_.]//?)+ [NC,OR]
RewriteCond %{QUERY_STRING} \=PHP[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} [NC,OR]
RewriteCond %{QUERY_STRING} (\.\./|\.\.) [OR]
RewriteCond %{QUERY_STRING} ftp\: [NC,OR]
RewriteCond %{QUERY_STRING} http\: [NC,OR]
RewriteCond %{QUERY_STRING} https\: [NC,OR]
RewriteCond %{QUERY_STRING} \=\|w\| [NC,OR]
RewriteCond %{QUERY_STRING} ^(.*)/self/(.*)$ [NC,OR]
RewriteCond %{QUERY_STRING} ^(.*)cPath=http://(.*)$ [NC,OR]
RewriteCond %{QUERY_STRING} (\<|%3C).*script.*(\>|%3E) [NC,OR]
RewriteCond %{QUERY_STRING} (<|%3C)([^s]*s)+cript.*(>|%3E) [NC,OR]
RewriteCond %{QUERY_STRING} (\<|%3C).*iframe.*(\>|%3E) [NC,OR]
RewriteCond %{QUERY_STRING} (<|%3C)([^i]*i)+frame.*(>|%3E) [NC,OR]
RewriteCond %{QUERY_STRING} base64_encode.*\(.*\) [NC,OR]
RewriteCond %{QUERY_STRING} base64_(en|de)code[^(]*\([^)]*\) [NC,OR]
RewriteCond %{QUERY_STRING} GLOBALS(=|\[|\%[0-9A-Z]{0,2}) [OR]
RewriteCond %{QUERY_STRING} _REQUEST(=|\[|\%[0-9A-Z]{0,2}) [OR]
RewriteCond %{QUERY_STRING} ^.*(\[|\]|\(|\)|<|>).* [NC,OR]
RewriteCond %{QUERY_STRING} (NULL|OUTFILE|LOAD_FILE) [OR]
RewriteCond %{QUERY_STRING} (\./|\../|\.../)+(motd|etc|bin) [NC,OR]
RewriteCond %{QUERY_STRING} (localhost|loopback|127\.0\.0\.1) [NC,OR]
RewriteCond %{QUERY_STRING} (<|>|'|%0A|%0D|%27|%3C|%3E|%00) [NC,OR]
RewriteCond %{QUERY_STRING} concat[^\(]*\( [NC,OR]
RewriteCond %{QUERY_STRING} union([^s]*s)+elect [NC,OR]
RewriteCond %{QUERY_STRING} union([^a]*a)+ll([^s]*s)+elect [NC,OR]
RewriteCond %{QUERY_STRING} (;|<|>|'|"|\)|%0A|%0D|%22|%27|%3C|%3E|%00).*(/\*|union|select|insert|drop|delete|update|cast|create|char|convert|alter|declare|order|script|set|md5|benchmark|encode) [NC,OR]

# PHP-CGI Vulnerability
RewriteCond %{QUERY_STRING} ^(%2d|\-)[^=]+$ [NC,OR]

#proc/self/environ? no way!
RewriteCond %{QUERY_STRING} proc\/self\/environ [NC,OR]

RewriteCond %{QUERY_STRING} (sp_executesql) [NC]
RewriteRule ^(.*)$ - [F,L]

</IfModule>