Archive for the ‘Wordpress’ category

Nginx as a Proxy to your Blog

July 23rd, 2010

Some time ago, back when Google Biz Ops were really popular I’ve had quite a number of clients use this method of changing the IP address of their auto-blogs. I once had a server hosted by ServerPronto*. The server was physically located in Panama (despite being advertised as a US hosting provider), the IPs were cheap though.

The Idea

The client had a good number of auto-blogs, landing pages and so forth on the same hosting provider (in this case his own dedicated server with multiple non-class-c IPs). The problem was making one or more of those domains appear not to be on the same server, or datacenter in this case. The solution was to use various class-c and class-b IPs on my own server, and use Nginx to proxy any web request to his own existing websites thus making those sites appear on a server in Panama as opposed to their actual location. The same could work if you wanted to use US IPs on a cheap provider and proxy to an off shore provider with cheaper hardware.

The Solution

For this we’ll need a Dedicated server or VPS to host Nginx on. (I’ll have an article later on how to install Nginx from scratch). You’ll probably want a couple extra IPs on hand, in case you want to proxy multiple sites of the same niche.

For every hostname you wish to proxy you’ll need a server block similar to this in your nginx configuration.

server { 
	#Turns access log off
	access_log off;
 
	#Set this to the IP on the nginx server you want to represent this proxy
	listen nginx-ip-here:80;
 
	#Matches the requested hostname
	server_name your-hostname.com www.your-hostname.com;
 
	location / {
		# Tells the destination server the IP of the visitor (some take X-Real-IP, 
		# some take X-Forwarded-For as their header of choice
		proxy_set_header X-Real-IP $remote_addr;
		proxy_set_header X-Forwarded-For $remote_addr;
		# Tells the destination server which hostname you want
		proxy_set_header Host $host;
		# Performs a Proxy Pass to that server
		proxy_pass http://ip-address-of-your-server;
	}
}

From here you would point your domain name to the Nginx server. If your domain currently uses your existing hosting’s nameservers, the easiest way to change this would be to use your registrar’s DNS such as namecheap or godaddy (via total domain control), or a free DNS provider such as everydns.com. In any of those you would simply change the A record of your domain name to the IP address of the Nginx server.

Once that is setup any request on port 80 (standard http port) will be proxied to the server actually hosting the website or blog.

The Problems

A couple of downsides may exist with this method.

Control panel access via your domain name
When you point your domain to the Nginx IP, you not only direct web traffic there, but all other forms of traffic such as SSH, Cpanel/whm, FTP, and so forth. While you can in theory forward via iptables and other methods for those additional ports, it would be difficult and not without problems. So to get around this you’ll either want to access SSH, Cpanel/DirectAdmin, FTP, and so forth via your actual server’s IP address, or use a non-proxied domain. You can use any of your other domains on the same server especially if using a control panel, since the login is what determines which account you need access to, not the domain itself.

Emails
Email access of course will cease if you don’t setup Nginx as mail proxy to that specific domain name. Though the work around would be to add an A record for mail.yourdomain.com in the DNS settings, and then change your MX records to point to mail.yourdomain.com. Course the better alternative if you must have email at that domain would be to use google apps, that way a simple whois or DNS lookup won’t reveal your actual server’s IP address. The best alternative is to simply not use email at proxied domains.

Speed
Normally a visitor takes their provided route from their location directly to your server over whatever backbone connects them. But with a proxied domain the visitor is then sent to the nginx server where ever it may be, and then the time is increased by how long it takes for the nginx server to communicate with your own webserver and then back. If the latency between the Nginx server and your destination is short, then the speed difference to visitors may not even be noticeable.

Security Concerns
Some of you no doubt are looking at this configuration and wondering if it could be changed so you don’t have to make a server block for each hostname you have. While it is possible to make server_name and the proxy_pass destination dynamic to the requested hostname, this is a very bad idea. Doing so will allow not only your own domains to be proxied but any domain pointed to your nginx server. This can result in abuse or even bandwidth overload.

For example, don’t do this:

server { 
	access_log off;
	listen nginx-ip-here:80;
 
	location / {
		proxy_set_header X-Real-IP $remote_addr;
		proxy_set_header X-Forwarded-For $remote_addr;
		proxy_set_header Host $host;
		proxy_pass http://$host;
	}
}

While it may be easy for you, it opens your server up for a floodgate of abuse using your server as some kind of public proxy point. Instead to tighten security of the nginx box, we should deny (or redirect) any traffic to the nginx server that either does not provide a host name, or does not provide one that has been defined. You can do so like this:

    server {
	#visits not supplying hostname
	server_name _;
	location / { return 444; }
    }

The return 444, sends a forbidden error back to the browser.

* ServerPronto: While I did use them at one point of time for the proxy purpose, I would not recommend them for a hosting provider or dedi reseller.

Migrating Cpanel to DirectAdmin

March 28th, 2010

One of the most frustrating thing someone can do involving their websites is moving them from one hosting provider to another. It’s increasingly more difficult if your hosting was based on a control panel such as Cpanel, and try to migrate to a different kind of control panel or none at all.

Step One – Package your account

If you have root access to the server via SSH (such as if you have a VPS) you can do this quite simply by performing the following command.

/scripts/pkgacct username

The above command will place the cpmove-username.tar.gz file into the /home directory where you can download it via FTP/SCP.

For those who only have user-level cpanel access, you will need to log into your cpanel account. From there under the file section you will see a Backup Wizard icon.

Cpanel Files Section

When you click on that, you’ll be prompted to 1) Backup. 2) Full Backup 3) Provide email.

When the backup packaging is complete you will receive an email notification, and the generated file will be placed in your home directory. (usually the default root of your FTP client when you log in). You will want to download this .tar.gz file onto your desktop.

Step Two – Preparing to upload

For those of you who are windows users, you will need to download something such as 7-Zip in order to unpack the tar.gz file. Create a folder to place the file in and unpack it. The resulting files will be all the data from your Cpanel account (including your mail in MailDir format). Find the homedir.tar and pull it out of the folder and extract it.

Once extracted you’ll see various folders that was in your home directory. In /tmp you may even find some of your statistics such as webalizer which can be opened right from your desktop. The files you want to concentrare are in /public_html.

If you use any add-on domains, move them from the public_html folder. Once you’ve done that you can then compress the public_html folder content as a .zip file. Do the same for each one of the add-on domains. Don’t delete the uncompressed copies just yet.

Path_Info & PHP_SELF woes [NginX]

December 12th, 2009

Over the last couple of years I’ve been constantly researching for a way to get the PHP environment variables to show up correctly. My latest pains were with PATH_INFO and PHP_SELF, which are now finally solved.

My current configuration are PHP-FPM (5.2.10) and NginX (0.8.29) on a CentOS 5.4 x64 VPS.

Traditionally you would use a PHP configuration such as this:

	server {
		server_name  your-domain.com www.your-domain.com;
 
		location / {
			root html/default;
		}
 
		location ~ \.php$ {
			include fastcgi_params;
			fastcgi_param  SCRIPT_FILENAME  /usr/local/nginx/html/default$fastcgi_script_name;
			fastcgi_pass  127.0.0.1:9000;
		}
	}

Within fastcgi_params would be something like this:

fastcgi_param  QUERY_STRING       $query_string;
fastcgi_param  REQUEST_METHOD     $request_method;
fastcgi_param  CONTENT_TYPE       $content_type;
fastcgi_param  CONTENT_LENGTH     $content_length;
 
fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
fastcgi_param  REQUEST_URI        $request_uri;
fastcgi_param  DOCUMENT_URI       $document_uri;
fastcgi_param  DOCUMENT_ROOT      $document_root;
fastcgi_param  SERVER_PROTOCOL    $server_protocol;
 
fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;
 
fastcgi_param  REMOTE_ADDR        $remote_addr;
fastcgi_param  REMOTE_PORT        $remote_port;
fastcgi_param  SERVER_ADDR        $server_addr;
fastcgi_param  SERVER_PORT        $server_port;
fastcgi_param  SERVER_NAME        $server_name;
 
# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param  REDIRECT_STATUS    200;

While the above method may seem to work at first, you’ll quickly notice problems when it comes to using $_SERVER['PATH_INFO'] and $_PATH['PATH_TRANSLATED'], and often enough $_SERVER['PHP_SELF'] ends up being set incorrectly when you try to adjust for the two environment variables.

Here is a setup I’ve come to prefer, especially when it comes to having multiple virtual hosts that use PHP. Notable tips are commented below the line.

Simple Nginx configuration file with a single virtual host

worker_processes  1;
pid        logs/nginx.pid;
events { worker_connections  1024; }
http {
	include       mime.types;
	default_type  application/octet-stream;
	sendfile        on;
	keepalive_timeout  65;
 
	index index.html index.htm index.php;
	# Identical to Apache's DirectoryIndex, setting it in
	# the http block set it as a default for all server blocks within
 
	server {
		listen	80; 
		# since port 80 is set by default, you do not actually need
		# to set this, unless of course you are binding to a specific
		# address such as listen server-ip-address:80 or alternate port; 
 
		server_name  gyn0.com www.gyn0.com;
 
		root   html/default;
		# You will want to set your root here, since otherwise
		# $document_root within the php block will not work
		# if you set it in the location block you would also have 
		# to set the php block within that location as well
 
		location / {
			# Below are some wordpress-friendly rewrite rules
			if (-f $request_filename) {
				expires 30d;
				break;
			}
 
			if (!-e $request_filename) {   			
				rewrite ^/.+/?(/wp-.*) $1 last;
				rewrite ^/.+/?(/.*\.php)$ $1 last;
				rewrite ^/(.+)$ /index.php?q=$1 last;
			}
		}
 
		location = /favicon.ico { access_log off; log_not_found off; }
		# If you haven't created a favicon for your site, you can keep
		# your access and error logs clean by turning off the logs
		# when a browser requests the fav icon (its also a good way
		# to keep your logs from filling with useless information)
 
		include php;
		# I prefer to keep my php settings in one file, so I can simply
		# paste this single line for each of my virtual hosts
	}
}

Now the php file (which I’ve created in the /conf folder with nginx.conf)

fastcgi_intercept_errors on;
# this will allow Nginx to intercept 4xx/5xx error codes
# Nginx will only intercept if there are error page rules defined
# -- This is better placed in the http {} block as a default
# -- so that in the case of wordpress, you can turn it off specifically
# -- in that virtual host's server block
 
location ~ \.php {
	fastcgi_split_path_info ^(.+\.php)(/.+)$;
	# A handy function that became available in 0.7.31 that breaks down 
	# The path information based on the provided regex expression
	# This is handy for requests such as file.php/some/paths/here/ 
 
	include fastcgi_params;
	# Includes the modified fastcgi parameter file (see below)
 
	fastcgi_pass   127.0.0.1:9000;
	fastcgi_index  index.php;
}

The new modified fastcgi_params:

fastcgi_param  QUERY_STRING       $query_string;
fastcgi_param  REQUEST_METHOD     $request_method;
fastcgi_param  CONTENT_TYPE       $content_type;
fastcgi_param  CONTENT_LENGTH     $content_length;
 
fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
#SCRIPT_NAME needs to remain without the document_root on front
#Otherwise PHP_SELF breaks, the interpreter will still find the file
#because of the added SCRIPT_FILENAME parameter below
 
fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;
#This one was added right into the file, since with root moved, and path_info split
#It should no longer need to be set manually for each virtual host
 
fastcgi_param  REQUEST_URI        $request_uri;
fastcgi_param  DOCUMENT_URI       $document_uri;
fastcgi_param  DOCUMENT_ROOT      $document_root;
fastcgi_param  SERVER_PROTOCOL    $server_protocol;
 
#the two lines below are possible now because of the fastcgi_split_path_info function
fastcgi_param  PATH_INFO          $fastcgi_path_info;
fastcgi_param  PATH_TRANSLATED    $document_root$fastcgi_path_info;
 
fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
fastcgi_param  SERVER_SOFTWARE    nginx;
# I removed the version, as not to give away too much information
 
fastcgi_param  REMOTE_ADDR        $remote_addr;
fastcgi_param  REMOTE_PORT        $remote_port;
fastcgi_param  SERVER_ADDR        $server_addr;
fastcgi_param  SERVER_PORT        $server_port;
fastcgi_param  SERVER_NAME        $server_name;
 
#PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param  REDIRECT_STATUS    200;

There you have it, PHP should now have correct environment variables. For example http://www.kbeezie.com/php.php/a/path/string/?var=foo would render the following results (the url above doesn’t actually work, sorry :P ):

$_SERVER["QUERY_STRING"] -> var=foo
$_SERVER["SCRIPT_NAME"] -> /php.php
$_SERVER["SCRIPT_FILENAME"] -> /usr/local/nginx/html/default/php.php
$_SERVER["REQUEST_URI"] -> /php.php/a/path/string/?var=foo
$_SERVER["DOCUMENT_URI"] -> /php.php/a/path/string/
$_SERVER["DOCUMENT_ROOT"] -> /usr/local/nginx/html/default
$_SERVER["PATH_INFO"] -> /a/path/string/
$_SERVER["PATH_TRANSLATED"] -> /usr/local/nginx/html/default/a/path/string
$_SERVER["PHP_SELF"] -> /php.php/a/path/string/

So there you have it. A simple php block that will correctly assign the path environment variables, without having to use multiple blocks and patterns. And quite easy to simply assign to a new virtual host by simply pasting the include php; line.

WordPress Automatic Update with SSH

November 22nd, 2009

If you’re like me, you don’t even want the insecure FTP protocol running on your server, but by default wordpress doesn’t even give you the option of using SSH to automatically upgrade your plugins, or wordpress itself.

I’m using a barebone CentOS server for this site, running on the Nginx webserver. I do not have a FTP server installed, and would very much prefer not to have one. Right now the only way to get into the server is via SSH. Below is a working configuration added to the wp-config.php file.

define('FS_METHOD', 'direct'); // 'ssh' is also an option, but did not work for my setup
define('FTP_BASE', '/opt/local/nginx/html/domain.com/');
define('FTP_CONTENT_DIR', '/opt/local/nginx/html/domain.com/wp-content/');
define('FTP_PLUGIN_DIR ', '/opt/local/nginx/html/domain.com/wp-content/plugins/');
define('FTP_PUBKEY', '/home/username/.ssh/id_rsa.pub');
define('FTP_PRIKEY', '/home/username/.ssh/id_rsa');
define('FTP_USER', 'username');
define('FTP_HOST', 'your-domain.com:22');

You can generate a public/private key by executing the following:

$ ssh-keygen

It will ask you where you wish to save the key (the default path usually /home/username/.ssh/id_rsa should be fine), followed by a passphrase which you can just leave blank for this purpose, then the location of the public key which is fine at its default location (usually /home/username/.ssh/id_rsa.pub)

You’ll also want to create an authorized key file by copying the public key into authorized_keys. We also need to make sure to set the proper permissions.

$ cd ~/.ssh
$ cp id_rsa.pub authorized_keys
$ cd ~/
$ chmod 755 .ssh
$ chmod 644 .ssh/*

By using the key pair shown in the configuration above you only have to supply the SSH username, otherwise if you don’t want to use key pairs, you can instead provide your SSH password with the following line:

define('FTP_PASS', 'password');

Make sure that the folders where updates (such as plugins) will need to be performed are writable by wordpress. From there when you click “upgrade automatically” it should just simply happen.