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 http://ftp.sunet.se/pub/gnu/gmp/gmp-5.1.0a.tar.bz2
# 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:

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

gmp
gmp support => enabled
GMP version => 4.3.1  
 
Done!

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>

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

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

# 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
</FilesMatch>
</IfModule>

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

MySQL

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:
[mysqld]
    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
    open_files_limit=2528

[mysqld_safe]

[mysqldump]
    quick
    max_allowed_packet = 16M
[myisamchk]
    key_buffer = 64M
    sort_buffer = 64M
    read_buffer = 16M
    write_buffer = 16M
[mysqlhotcopy]
    interactive-timeout

Best config base on 8 core & 12GB memory (Shared server) MySQL 5.5:
[mysqld]
local-infile=0
max_connections = 600
max_user_connections=1000
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
slow_query_log
log-error
external-locking=FALSE
open_files_limit=50000

[mysqld_safe]

[mysqldump]
quick
max_allowed_packet = 16M

[isamchk]
key_buffer = 384M
sort_buffer = 384M
read_buffer = 256M
write_buffer = 256M

[myisamchk]
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: http://configserver.com/free/csf/install.txt
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:
PT_USERMEM=180
PT_USERTIME=180
PT_USERKILL=1
PT_USERKILL_ALERT=1 (Optional)

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:

# SSL
ssl-ca=/etc/mysql-ssl/ca-cert.pem
ssl-cert=/etc/mysql-ssl/server-cert.pem
ssl-key=/etc/mysql-ssl/server-key.pem



4. Restart MySQL.
5. Create an user to permit only SSL-encrypted connection:
GRANT ALL PRIVILEGES ON *.* TO ‘ssluser’@’%’ IDENTIFIED BY ‘pass’ REQUIRE SSL;

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]:

# SSL
ssl-cert=/etc/mysql-ssl/client-cert.pem
ssl-key=/etc/mysql-ssl/client-key.pem



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:
GRANT REPLICATION SLAVE ON *.* to ‘repl’@’%’ REQUIRE SSL;
3. Change master options and restart slave:
STOP SLAVE;
CHANGE MASTER MASTER_SSL=1,
MASTER_SSL_CA=’/etc/mysql-ssl/ca-cert.pem’,
MASTER_SSL_CERT=’/etc/mysql-ssl/client-cert.pem’,
MASTER_SSL_KEY=’/etc/mysql-ssl/client-key.pem';
SHOW SLAVE STATUSG
START SLAVE;
SHOW SLAVE STATUSG

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
$conn=mysqli_init();
mysqli_ssl_set($conn, ‘/etc/mysql-ssl/client-key.pem’, ‘/etc/mysql-ssl/client-cert.pem’, NULL, NULL, NULL);
if (!mysqli_real_connect($conn, ‘127.0.0.1’, ‘ssluser’, ‘pass’)) { die(); }
$res = mysqli_query($conn, ‘SHOW STATUS like “Ssl_cipher”‘);
print_r(mysqli_fetch_row($res));
mysqli_close($conn);
3. Test it:
[root@centos6 ~]# php mysqli-ssl.php
Array
(
[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;

Conclusion

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 https://code.launchpad.net/duplicity/0.6-series/0.6.20/+download/duplicity-0.6.20.tar.gz
$ sudo tar xvzf duplicity-0.6.20.tar.gz
$ cd duplicity-0.6.20
$ python sudo setup.py 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.

Features

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

Duptools

#!/bin/bash
export AWS_ACCESS_KEY_ID=YOUR_ACCESS_KEY
export AWS_SECRET_ACCESS_KEY=YOUR_SECRET_ACCESS_KEY
export PASSPHRASE=YOU_PASSHRASE

# directories, space separated
SOURCE="/home/yegorg/backup /home/yegorg/bin /home/yegorg/documents"
BUCKET=s3+http://mybucket
LOGFILE=/home/yegorg/tmp/duplicity.log
# set email to receive a backup report
EMAIL=""

backup() {
  INCLUDE=""
  for CDIR in $SOURCE
  do
    TMP=" --include  ${CDIR}"
    INCLUDE=${INCLUDE}${TMP}
  done
  # 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
  fi
}

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

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

status() {
  duplicity collection-status $BUCKET
}

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

  USAGE:

  ./duptools.sh backup 
  ./duptools.sh list
  ./duptools.sh status
  ./duptools.sh restore file [time] dest
  "
fi

export AWS_ACCESS_KEY_ID=
export AWS_SECRET_ACCESS_KEY=
export PASSPHRASE=

Installation

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

Backup

$ ./duptools.sh backup

List/Status

$ ./duptools.sh list
$ ./duptools.sh status

Restore

Be careful while restoring not to preprend a slash to the path.
Restoring a single file to tmp
$ ./duptools.sh restore home/yegorg/bin/setupscreen tmp/setupscreen
Restoring an older version of a directory to tmp (interval or full date)
$ ./duptools.sh  restore home/yegorg/bin 1D3h5s tmp/bin
$ ./duptools.sh  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 domain.com

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

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:

/scripts/whoowns domain.com

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:

/scripts/killdns domain.com

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.

/scripts/whoowns domain.com

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

grep domain.com /var/cpanel/users/*

grep -R domain.com /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

/scripts/updateuserdomains

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

/scripts/rebuildhttpdconf 
# 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.