Yegor's blog

Small blog about system administration.

344.688.ORG - Secure Pastebin-like service

344.688.ORG is a minimalist, opensource online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted in the browser using 256 bits AES. More information on the project page.

Install PHP-GMP5 Library on WHM/cPanel server

GNU MP or GMP is a free library for arbitrary precision arithmetic, operating on signed integers, rational numbers, and floating point numbers. There is no practical limit to the precision except the ones implied by the available memory in the machine GMP runs on. GMP has a rich set of functions, and the functions have a regular interface.
The main target applications for GMP are cryptography applications and research, Internet security applications, algebra systems, computational algebra research, etc.
GMP is carefully designed to be as fast as possible, both for small operands and for huge operands. The speed is achieved by using fullwords as the basic arithmetic type, by using fast algorithms, with highly optimised assembly code for the most common inner loops for a lot of CPUs, and by a general emphasis on speed.
For install GMP library in cPanel server follow this instruction:

# cd /usr/local/src
# wget
# tar -xjvf gmp-5.1.0a
# cd gmp-5.1.0a
# ./configure
# make && make check && make install
Edit or create new file /var/cpanel/easy/apache/rawopts/all_php5

# vi /var/cpanel/easy/apache/rawopts/all_php5

and add this line:

Rebuild apache using this command:
# /scripts/easyapache --build
Check php-gmp is loaded using this command:
# php -i | grep -i gmp

gmp support => enabled
GMP version => 4.3.1  

Optimize MySQL & Apache on cPanel/WHM server

On this optimization process we will go over the Apache core configuration and modules that are part of Apache core. We think that with the correct settings of Apache and MySQL you can get excellent results and the correct level of resource use without installing third-party proxy and cache modules. So let’s start,

Apache & PHP

In the first stage we run the Easy Apache and selected the following:
* Apache Version 2.4+
* PHP Version 5.4+
* In step 5 “Exhaustive Options List” select
– Deflate
– Expires
– MPM Prefork
– MPM Worker
After Easy Apache finished go to your WHM » Service Configuration » Apache Configuration » “Global Configuration” and set the values by the level of resources available on your server.
Apache Directive    (From 2GB memory or less and up to 12GB memory)    

StartServers      4    8    16  
MinSpareServers    4    8    16  
MaxSpareServers    8    16    32  
ServerLimit      64    128    256  
MaxRequestWorkers    50    120    250  
MaxConnectionsPerChild    1000    2500    5000 
Keep-Alive   On  On  On
Keep-Alive Timeout   5   5    5
Max Keep-Alive Requests  50   120   120
Timeout    30  60  60

Now go to WHM » Service Configuration » Apache Configuration » Include Editor » “Pre VirtualHost Include” and allow users minimal cache and data compression to allow the server to work less for the same things by pasting the code below into the text field.
# Cache Control Settings for one hour cache
<FilesMatch ".(ico|pdf|flv|jpg|jpeg|png|gif|js|css|swf)$">
Header set Cache-Control "max-age=3600, public"

<FilesMatch ".(xml|txt)$">
Header set Cache-Control "max-age=3600, public, must-revalidate"

<FilesMatch ".(html|htm)$">
Header set Cache-Control "max-age=3600, must-revalidate"

# Mod Deflate performs data compression
<IfModule mod_deflate.c>
<FilesMatch ".(js|css|html|php|xml|jpg|png|gif)$">
SetOutputFilter DEFLATE
BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4.0[678] no-gzip
BrowserMatch bMSIE no-gzip

Go to WHM » Service Configuration » “PHP Configuration Editor” and set the parameters according to your needs:
– memory_limit
– max_execution_time
– max_input_time


For MySQL you need to update the configuration file that usually in /etc/my.cnf
Best config base on 1 core & 2GB memory MySQL 5.5:
    local-infile = 0
    max_connections = 250
    key_buffer = 64M
    myisam_sort_buffer_size = 64M
    join_buffer_size = 1M
    read_buffer_size = 1M
    sort_buffer_size = 2M
    max_heap_table_size = 16M
    table_cache = 5000
    thread_cache_size = 286
    interactive_timeout = 25
    wait_timeout = 7000
    connect_timeout = 15
    max_allowed_packet = 16M
    max_connect_errors = 10
    query_cache_limit = 2M
    query_cache_size = 32M
    query_cache_type = 1
    tmp_table_size = 16M


    max_allowed_packet = 16M
    key_buffer = 64M
    sort_buffer = 64M
    read_buffer = 16M
    write_buffer = 16M

Best config base on 8 core & 12GB memory (Shared server) MySQL 5.5:
max_connections = 600
key_buffer_size = 512M
myisam_sort_buffer_size = 64M
read_buffer_size = 1M
table_open_cache = 5000
thread_cache_size = 384
wait_timeout = 20
connect_timeout = 10
tmp_table_size = 256M
max_heap_table_size = 128M
max_allowed_packet = 64M
net_buffer_length = 16384
max_connect_errors = 10
concurrent_insert = 2
read_rnd_buffer_size = 786432
bulk_insert_buffer_size = 8M
query_cache_limit = 5M
query_cache_size = 128M
query_cache_type = 1
query_prealloc_size = 262144
query_alloc_block_size = 65535
transaction_alloc_block_size = 8192
transaction_prealloc_size = 4096
max_write_lock_count = 8


max_allowed_packet = 16M

key_buffer = 384M
sort_buffer = 384M
read_buffer = 256M
write_buffer = 256M

key_buffer = 384M
sort_buffer = 384M
read_buffer = 256M
write_buffer = 256M

#### Per connection configuration ####
sort_buffer_size = 1M
join_buffer_size = 1M
thread_stack = 192K

Repair & optimize databases then restart MySQL:
mysqlcheck --check --auto-repair --all-databases
mysqlcheck --optimize --all-databases
/etc/init.d/mysql restart

Security & Limit Resources

Install CSF (ConfigServer Security & Firewall) at:
1) Go to WHM » Plugins » ConfigServer Security & Firewall » “Check Server Security” And pass on what appears as required to repair:
2) Go to WHM » Plugins » ConfigServer Security & Firewall » “Firewall Configuration” and set the parameters according to your needs:

Now enjoy your new fast and more effective server.

HOWTO: setup MySQL with SSL, SSL replication and how to establish secure connections from the console

Setup SSL on MySQL

1. Generate SSL certificates. Use the different Common Name for server and client certificates.
2. For the reference, I store the generated files under /etc/mysql-ssl/
3. Add the following lines to /etc/my.cnf under [mysqld] section:


4. Restart MySQL.
5. Create an user to permit only SSL-encrypted connection:

Establish secure connection from console

1. If the client is on a different node, copy /etc/mysql-ssl/ from the server to that node.
2. Add the following lines to /etc/my.cnf under [client]:


3. Test a secure connection:
[root@centos6 ~]# mysql -u ssluser -p -sss -e ‘\s’ | grep SSL
SSL: Cipher in use is DHE-RSA-AES256-SHA

Setup SSL replication

1. Establish a secure connection from the console on slave like described above, to make sure SSL works fine.
2. On Master add “REQUIRE SSL” to the replication user:
3. Change master options and restart slave:

Establish secure connection from PHP

1. Install php and php-mysql packages. I use the version >=5.4.x, otherwise, it may not work.
2. Create the script:
[root@centos6 ~]# cat mysqli-ssl.php
mysqli_ssl_set($conn, ‘/etc/mysql-ssl/client-key.pem’, ‘/etc/mysql-ssl/client-cert.pem’, NULL, NULL, NULL);
if (!mysqli_real_connect($conn, ‘’, ‘ssluser’, ‘pass’)) { die(); }
$res = mysqli_query($conn, ‘SHOW STATUS like “Ssl_cipher”‘);
3. Test it:
[root@centos6 ~]# php mysqli-ssl.php
[0] => Ssl_cipher
[1] => DHE-RSA-AES256-SHA

HOWTO: Configure Logging and Log Rotation in Nginx

One of the easiest ways to save yourself trouble with your web server is to configure appropriate logging today. Logging information on your server gives you access to the data that will help you troubleshoot and assess situations as they arise.

The Error_log Directive

Nginx uses a few different directives to control system logging. The one included in the core module is called "error_log".

Error_log Syntax

The "error_log" directive is used to handle logging general error messages. If you are coming from Apache, this is very similar to Apache's "ErrorLog" directive.
The error_log directive takes the following syntax:
error_log log_file [ log_level ]
The "log_file" in the example specifies the file where the logs will be written. The "log_level" specifies the lowest level of logging that you would like to record.

The Access_log Directive

The access_log directive uses some similar syntax to the error_log directive, but is more flexible. It is used to configure custom logging.
The access_log directive uses the following syntax:
access_log /path/to/log/location [ format_of_log buffer_size ];
The default value for access_log is the "combined" format we saw in the log_format section. You can use any format defined by a log_format definition.
The buffer size is the maximum size of data that Nginx will hold before writing it all to the log. You can also specify compression of the log file by adding "gzip" into the definition:
access_log location format gzip;
Unlike the error_log directive, if you do not want logging, you can turn it off by specifying:
access_log off;
It is not necessary to write to "/dev/null" in this case.

Log Rotation

As log files grow, it becomes necessary to manage the logging mechanisms to avoid filling up disk space. Log rotation is the process of switching out log files and possibly archiving old files for a set amount of time.
Nginx does not provide tools to manage log files, but it does include mechanisms that make log rotation simple.
if ($time_iso8601 ~ "^(\d{4})-(\d{2})-(\d{2})") {
    set $year $1;
    set $month $2;
    set $day $3;
access_log /var/log/nginx/$year-$month-$day-access.log;


Proper log configuration and management can save you time and energy in the event of a problem with your server. Having easy access to the information that will help you diagnose a problem can be the difference between a trivial fix and a persistent headache.
It is important to keep an eye on server logs in order to maintain a functional site and ensure that you are not exposing sensitive information. This guide should serve only as an introduction to your experience with logging.

Ubuntu/Debian - Encrypted incremental backups with duplicity on Amazon S3

An example on how to use duplicity to perform encrypted incremental backups on Amazon S3.

Getting started

If you've never heard about duplicity before, you should check the documentation.

Install duplicity

First, you need to install duplicity, I always install it from source since the duplicity package is not often updated.
$ sudo apt-get install python-dev librsync-dev
$ cd /opt
$ sudo wget
$ sudo tar xvzf duplicity-0.6.20.tar.gz
$ cd duplicity-0.6.20
$ python sudo install
But you can install it with apt-get
$ sudo apt-get install duplicity
Next you can also install s3cmd from S3 Tools, it's a command line tool for managing your S3 buckets, but it's not required.
$ sudo apt-get install s3cmd
$ s3cmd --configure

Encrypted Backups

Before backing up the data, you need to think about encryption, duplicity makes use of gpg and handles both private/public key pair (a gpg key) and symmetric encryption (a passphrase).
I use passsphrases since I'll never lose it and I don't have to backup a gpg key.

My backup script

Since you need to specify many args to perform the differents actions, I crafted a bash script that make working with duplicity easier, duptools.


  • Backup multiple directories
  • Send email report on backup
  • Quickly list file and show bucket status
  • Restore your files easily



# directories, space separated
SOURCE="/home/yegorg/backup /home/yegorg/bin /home/yegorg/documents"
# set email to receive a backup report

backup() {
  for CDIR in $SOURCE
    TMP=" --include  ${CDIR}"
  # perform an incremental backup to root, include directories, exclude everything else, / as reference.
  duplicity --full-if-older-than 30D $INCLUDE --exclude '**' / $BUCKET > $LOGFILE
  if [ -n "$EMAIL" ]; then
    mail -s "backup report" $EMAIL < $LOGFILE

list() {
  duplicity list-current-files $BUCKET

restore() {
  if [ $# = 2 ]; then
    duplicity restore --file-to-restore $1 $BUCKET $2
    duplicity restore --file-to-restore $1 --time $2 $BUCKET $3

status() {
  duplicity collection-status $BUCKET

if [ "$1" = "backup" ]; then
elif [ "$1" = "list" ]; then
elif [ "$1" = "restore" ]; then
  if [ $# = 3 ]; then
    restore $2 $3
    restore $2 $3 $4
elif [ "$1" = "status" ]; then
  echo "
  duptools - manage duplicity backup


  ./ backup 
  ./ list
  ./ status
  ./ restore file [time] dest



Set up config vars at the top of the script and make the script executable.


$ ./ backup


$ ./ list
$ ./ status


Be careful while restoring not to preprend a slash to the path.
Restoring a single file to tmp
$ ./ restore home/yegorg/bin/setupscreen tmp/setupscreen
Restoring an older version of a directory to tmp (interval or full date)
$ ./  restore home/yegorg/bin 1D3h5s tmp/bin
$ ./  restore home/yegorg/bin 2012/7/5 tmp/bin

cPanel - Domain already exists error while adding a subdomain or addon domain

A very common error  when adding an addon or parked domain via cpanel. Unfortunately, the error they see is usually very generic and doesn’t provide any information such as “can not create domain”. This is usually due to the domain name existing anywhere in the cPanel configuration and checking these locations/steps out should help you find the domain that is lurking somewhere and causing the issues.

Reason 1: There is an existing zone file on the server.

1) You can use the following command to check whether zone exists or not,

# dig @server_ip

If there is a zone file exists, this will show the A record of the

2) If there is a zone file that exists, log into the server that the zone file is pointing to and make sure the domain doesn’t exist:


If it does, you would need to remove this prior to adding their new addon domain. If it does not, continue on

3) Remove the zone file from the server by running the following command:


This will remove the DNS zone and will help you add the addon or parked domain again.

Reason 2: There are old traces of the domain on the server

1) Log into the server where the customer is seeing problems adding the domain and confirm that the domain does not exist on the server.


2) Check Cpanel files for traces of the problem domain name

grep /var/cpanel/users/*

grep -R /var/cpanel/userdata/*

3) Edit any files that are found and remove the traces of the domain name the customer is trying to add. You also may need to remove the entire file for the domain in the /var/cpanel/userdata/USERNAME/ directory.

 4) Rebuild the user domains database


5) Rebuild the Apache configuration and make sure apache is running with all traces of the bad domain removed.

# service httpd restart

This should have all traces that were left behind from when this domain name was removed in the past and then will no longer cause a conflict when the customer tries to add the domain again.