Update .htaccess with Dynamic DNS IP Address to Prevent Password Protection

by Brett on March 9, 2010

I was working on a password protected site that needed to allow one specific user access without requiring a login/password to access it. The site was already using .htaccess to password protect the entire site so the quickest solution was to use the following type of setup in the htaccess file:

Order deny,allow
Deny from all
AuthGroupFile /dev/null
AuthName "A Blog"
AuthType Basic
AuthUserFile /home/admin/domains/domain.com/.htpasswd/public_html/.htpasswd
require valid-user
Allow from person.getmyip.com
Satisfy Any

The main addition that I added to the password protection is line 8 “Allow from”. This line allows a specific IP address or host to have access without requiring password protection.

However, the host that needed to be used was a Dynamic DNS hostname. This creates a problem as Apache takes the following steps when the user requests access.

  1. Grab IP from user requesting access
  2. Do a reverse DNS lookup
  3. Compare the results to the host in the Allow from line (person.getmyip.com)

In this case when a reverse DNS lookup is completed on the users IP address it will not find the Dynamic DNS hostname. Instead, it will find the hostname that is associated with your ISP which might look something like this 8.sub-79-231-223.myvzw.com.

There are a number of ways to get around this issue. In my case I wanted to use a small script to dynamically populate the .htaccess file with the correct IP address.

Below is the following PHP script that handles updating .htaccess with the latest IP address. The main requirement is that there is a comment “# Allow from person.getmyip.com” somewhere in the .htaccess file. This line tells the script where to insert the IP address on the very next line.

<?php
// Rewrites the entire htaccess file. When a line starts with '# Allow from brett.getmyip.com' the
// very next line will be replaced with the actual ip associated with brett.getmyip.com
$htaccessFile = "/home/admin/domains/batie.com/public_html/.htaccess";
$handle = fopen($htaccessFile, "r");
if ($handle) {
	$previous_line = $content = '';
	while (!feof($handle)) {
		$current_line = fgets($handle);
		if(stripos($previous_line,'# Allow from person.getmyip.com') !== FALSE)
		{
			$output = shell_exec('host person.getmyip.com');
			if(preg_match('#([0-9]{1,3}\.){3}[0-9]{1,3}#',$output,$matches))
			{
				$content .= 'Allow from '.$matches[0]."\n";
			}
		}else{
			$content .= $current_line;
		}
		$previous_line = $current_line;
	}
	fclose($handle);
	$tempFile = tempnam('/tmp','allow_');
	$fp = fopen($tempFile, 'w');
	fwrite($fp, $content);
	fclose($fp);
	rename($tempFile,$htaccessFile);
	chown($htaccessFile,'admin');
	chmod($htaccessFile,'0644');
}
?>

I quickly wrote this script and realize that there is room for improvement. However, this meet the need and solved the problem.

After the script was completed adding a simple line to the crontab (crontab -e) file got it running on a regular basis to automatically update the file with the current IP.

# Script to update ip access for dynamic dns host - it allows person.getmyip.com
*/5 * * * * /usr/local/bin/php /home/admin/scripts/allow_person.php >/dev/null 2>&1
  • I’ve having exactly the same problem as yours, and I’m updating my dynamic IP manually for a few years using FTP lol.

    Your great blog enlightened me, but sadly my webhost doesn’t allow shell_exec so I wrote a simple perl script instead, hope reader here find it useful.

    My perl doing exactly the same as your php, however, instead of reading a live .htaccess file, my perl read from a template instead, of course, if you assign a live .htaccess as a template, it works as well.

    Once it found the template file, it looks for any line(s) contains the word @@@DYNAMIC@@@ and then replace it with a new line below

    allow from 1.2.3.4

    The perl script is already running on my shared web hosting account :)

    #!/usr/bin/perl
    $htaccess = "/volume1/web/test/.htaccess";
    $template = "/volume1/web/test/template";
    # get ip address
    $result=`host myhome.somedomain.com`;
    @sp = split(/ /,$result);
    $size = @sp;
    $ip = $sp[$size-1];
    open(TEMPLATE, $template) || die("Could not open file!");
    @raw_data=;
    close(TEMPLATE);
    open(NEW, ">$htaccess") || die("Could not create file!");
    foreach $line (@raw_data){
    	if($line =~ /\@\@\@DYNAMIC\@\@\@/){
    		print NEW "allow from $ip\n";
    	}else{
    		print NEW $line;
    	}
    }
    close(NEW);
    
  • Man, I LOVE YOU!!!

  • Hi Brett,

    thank you very much! That’s exactly what I was looking for :-) Just one questions: would it be possible add a function that checks if the ip has actually changed? I’m just curious because every time the cronjob runs it writes to the .htaccess file – even if it doesn’t change anything.

    Best regards,

    Florian

  • Jeremiah

    Wouldn’t it be possible to have a script on a linux box (dd-wrt linux router even?) that loads the php file when the ip gets renewed?

  • jon

    Your post gave me this idea, so thank you.
    Instead of using php you could run a cron job as root (to have access to .htaccess). The sh script uses a command to translate domain to ip and an echo to write to .htaccess.
    That’s it, much simpler.

  • Aveesh

    Hi,

    Great solution if you are into PHP

    I found this also – hope it benefits some of you

    http://unix.stackexchange.com/questions/91701/ufw-allow-traffic-only-from-a-domain-with-dynamic-ip-address

  • Chris

    I often have many .htaccess files in various subdirectories, so the solution in this article is cumbersome for me since I would need to explicitly specify every .htaccess file. Using PHP is also overkill.

    Here is a short bash script which will handle recursion. Any Allow directives with a dynamic dns should be appended with #DDNS.
    The script looks for lines ending with #DDNS, extracts the domain name, looks up the ip and inserts a new Allow directive for the ip.

    EG.
    Allow from domain.name #DDNS
    Allow from 255.255.255.255 #DDNS-IP

    By tagging the lines, the script is saner and more efficient.

    #!/bin/sh

    srcpath=”/”

    grep -lr ‘#DDNS’ $srcpath | while read i; do
    sed -i ‘/#DDNS-IP$/d’ $i
    grep -i ‘#DDNS$’ $i | while read j; do
    words=( $j )
    ddns=”${words[2]}”
    ip=”$(host $ddns)”
    if [ “$ip” == “${ip%% has address *}” ]; then
    continue;
    fi
    ip=”${ip##* has address }”
    sed -i ‘s/^\(‘”$j”‘\)$/\1\nAllow from ‘”$ip”‘ #DDNS-IP/’ $i
    done
    done

Previous post:

Next post: