Synology usage series 29 – Restricting WordPress accessible by home dynamic ip only


Update Aug 22 2011

The solution is redesigned so that the htaccess file is pushing from NAS to web hosting account. This greatly simplifed the overall process and resolved the deadlock situation.

I have a few private wordpress blog hosting on some web hosting provider and want to protect my private blog to be accessible only by home dynamic ip address.

This require rewriting htaccess file on web hosting account for every ip changes.

This may not related to Synology, but I need the help of DS207+ in order to perform the job by some automated scripts.

Solution

Firstly, a perl script sync-htaccess.pl is required to host on the NAS.

The script will

  1. Get External IP address of the NAS by calling the getip.php hosting on web hosting account
  2. Read the htaccess template, find the line @@@DYNAMIC@@@ and replace the line with ‘allow from 1.2.3.4’ (where 1.2.3.4 is the external IP address returned by getip.php)
  3. Write the actual .htaccess files to temporary directory.
  4. Upload the actual .htaccess files to web hosting account by synchronizing the temporary directory to the web hosting account. (using rsync over ssh)

The reason I am using rsync over ssh instead of scp/sftp to upload files because rsync and ssh is already provided by Synology, no addition ipkg package is required.

So, go ahead to create the perl script to /opt/usr/local/bin/sync-htaccess.pl

#!/usr/bin/perl

# v0.4
#	Aug 22 2011
#		Modified to run at NAS instead of webhost
# v0.31
#	May 15 2011
#		Add timeout and retries flag to wget command to prevent infinite lookup
# v0.3
# 	May 14 2011
#		host not returning ip address, using wget instead
# v0.2
# 	May 3 2011
#		Fix the host command to query type A record only

$version="0.4";

print "Executing sync-htaccess.pl version=$version\n";

##################################
### configuration begin here ###

# define sub domain 1
$site1output="/opt/tmp/sync-htaccess/.htaccess";
$site1template = "/opt/etc/sync/blog_htaccess_template";

# if you have more sub domain, copy the two lines and paste here
#$siteXXXoutput = "/opt/tmp/sync-htaccess/XXX/.htaccess";
#$siteXXXtemplate = "/opt/etc/sync/xxx_htaccess_template";


$logfile  = "/opt/var/log/rsync-htaccess.log";
$sshkey   = "/volume1/private/id_rsa";
$sshport  = "22";
$source   = "/opt/tmp/sync-htaccess/";
$remote   = "/home/account/public_html";

$getipurl = "http://www.mydomain/getip.php";
$accid    = "account_id";
$accdomain= "mydomain.com";

### END of CONFIGURATION, DO NOT MODIFY BELOW ####
##################################################

# get ip address
$ip=`wget --timeout=10 --tries=1 -qO -  $getipurl | sed 's/^ *\(.*\) *\$/\1/'`;


print "ip address $ip\n";

# write to output file, if you have more subdomain, duplicate the line below and modified the variables
writeHtaccess($ip,$site1template,$site1output);

# upload to webhosting
system("/usr/syno/bin/rsync -avz --log-file=$logfile -e 'ssh -i $sshkey -p $sshport' $source $accid\@$accdomain:$remote");

print "done\n";

sub writeHtaccess{
	my ($myip,$mytemplate,$myhtaccess) = @_;
	
	open(TEMPLATE, $mytemplate) || die("Could not open file!");
	@raw_data=<TEMPLATE>;
	close(TEMPLATE);

	print "Writing to $myhtaccess\n";
	open(NEW, ">$myhtaccess") || die("Could not create file!");

	foreach $line (@raw_data){
		if($line =~ /\@\@\@DYNAMIC\@\@\@/){
			if($myip eq ''){
				print "Skipping home ip\n";
			}else{
				print NEW "allow from $myip\n";
			}
		}else{
			print NEW $line;
		}
	}

	close(NEW);
}

To define the location of the htaccess template


$site1template = "/opt/etc/sync/blog_htaccess_template";

The script will read the template above, finding the word ‘@@@DYNAMIC@@@’ and replace the line by ‘allow from 1.2.3.4.’ where 1.2.3.4 will be the home ip provided by the giveip.php script.

To define the URL of the giveip.php script

$getipurl = "http://www.mydomain/getip.php";

After parsing, the script will then write the actual .htaccess file to a temporary directory which is about to be upload to the web hosting account.

The temporary directory is defined below

$source = "/opt/tmp/sync-htaccess/";

Make sure there is a ending slash ‘/’

Also Make sure the directory is existed, otherwise, create it

# mkdir /opt/tmp/sync-htaccess

** This is the only directory to be synchronized to the web hosting account. So all outputed .htaccess file MUST generated under this directory.

The .htaccess file generated is defined below


$site1output="/opt/tmp/sync-htaccess/.htaccess";

Once the htaccess files is generated, the script will fork to the shell and upload the htaccess files by rsync over ssh. In order to perform the upload task password-less, the script required to read the private ssh key of the web hosting account.

$sshkey = "/volume1/private/id_rsa";

Besides of the ssh key, below are the rest of the ssh parameters

$sshport = "22";
$accid = "account_id";
$accdomain= "mydomain.com";

$sshport defines the SSH port.
$accid defines the web hosting account login ID.
$accdomain defines the domain name of the web hosting account.

Once the ssh things were defined, the last thing to do is to tell the rsync command where to upload the .htaccess, which is usually the public_html directory on the web hosting account.

$remote = "/home/myaccount/public_html";

Create a cron job to run the script on NAS say per 5 minutes.

# vi /etc/crontab

*/5 * * * * root /usr/bin/perl /opt/usr/local/bin/sync-htaccess.pl

Sample htaccess template – htaccess_template

Below is a very simple htaccess template for private wordpress blog.

order deny,allow
deny from all
allow from @@@DYNAMIC@@@

Another script for webhosting account – getip.php (I forgot where I copied this script, thanks to the original author)

This script should upload to web hosting account.

<?=trim(getip())?>


<?


function validip($ip)
{


if (!empty($ip) && ip2long($ip)!=-1) {



  $reserved_ips = array (


  array('0.0.0.0','2.255.255.255'),


 array('10.0.0.0','10.255.255.255'),


 array('127.0.0.0','127.255.255.255'),


array('169.254.0.0','169.254.255.255'),

     array('172.16.0.0','172.31.255.255'),

     array('192.0.2.0','192.0.2.255'),

     array('192.168.0.0','192.168.255.255'),

     array('255.255.255.0','255.255.255.255')


  );




  foreach ($reserved_ips as $r) {

       $min = ip2long($r[0]);

       $max = ip2long($r[1]);


       if ((ip2long($ip) >= $min) && (ip2long($ip) <= $max))
          return false;

   }
   return true;



} else {

   return false;

}
 ## end if

}
 ## end function

function getip() {

       if (validip($_SERVER["HTTP_CLIENT_IP"])) {

               return $_SERVER["HTTP_CLIENT_IP"];

       }

       foreach (explode(",",$_SERVER["HTTP_X_FORWARDED_FOR"]) as $ip) {

               if (validip(trim($ip))) {

                       return $ip;
               }

               if (validip($_SERVER["HTTP_X_FORWARDED"])) {

                       return $_SERVER["HTTP_X_FORWARDED"];
               } elseif (validip($_SERVER["HTTP_FORWARDED_FOR"])) {

                       return $_SERVER["HTTP_FORWARDED_FOR"];
               } elseif (validip($_SERVER["HTTP_FORWARDED"])) {

                       return $_SERVER["HTTP_FORWARDED"];
               } elseif (validip($_SERVER["HTTP_X_FORWARDED"])) {
                       return $_SERVER["HTTP_X_FORWARDED"];
               } else {
                       return $_SERVER["REMOTE_ADDR"];

               }

        }

}

?>

Conclusion

Here is the files required for the tasks.

Webhost

  • getip.php

NAS

  • sync-htaccess.pl
  • htaccess template




Leave a Reply

Your email address will not be published. Required fields are marked *