...making Linux just a little more fun!

<-- prev | next -->

# Dude, Where's my System?

By Lew Pitcher

Recently, I changed over to an "always on" ADSL[1] connection from an "on-demand" dialup one, and I now have an opportunity to host a number of internet services from my home server. The one problem I have is that, while my internet is "always on", my ISP occasionally renegotiates the IP address of my ADSL connection, making my internet services difficult to find. Up until now, I've played "hide and seek" with my server, searching for it among a multitude of alternate addresses each time my ISP changes the address.

Now, I could spend a bit of money to lease my own domain name, and subscribe to one of those 'dynamic DNS' services that let you point your domain name at an ever-changing IP address, but I don't need a permanent domain name right now, and I've got other options.

My ISP provides web site hosting for their subscribers, with the usual upload and download volume caps to discourage extensive web hosting. I don't need their host for my content, but it does make a handy 'rendezvous' site to gain access to my system. All I have to do is arrange for my PPPoE[2] client[3] to rewrite my personal web page on my ISP's server every time my ISP gives me a new IP address. If PPPoE can populate that web page with my system's new IP address, I'll always be able to find my system from the outside by looking at my webpage on my ISP's server.

Simple, yes?

### Simple, yes!

The first thing I needed to do was to arrange for my PPPoE client to be able to pass each new IP address on to something that would update my external webpage.

To do this, I modified my `/etc/ppp/ip-up`[4] script to execute a new script (`/etc/ppp/ip-up.webpage`), passing it the PPP 'local' address assigned by my ISP to my end of my PPPoE ADSL connection. The `/etc/ppp/ip-up` script would then write the webpage and get it to my ISP.

This was a simple change (a literal 'one-liner') in `/etc/ppp/ip-up`

>     #!/bin/sh
>     #
>     # ip-up interface-name tty-device speed local-IP remote-IP ipparm
>     #       $1             $2         $3    $4       $5        $6
>     #
>     # (NB: ipparm is string from ipparm parameter in pppd options)
>     #
>     # This file /etc/ppp/ip-up is run by pppd when there's a
>     # successful ppp connection.
>     #
>     # The environment is cleared before executing this script
>     # so the path must be reset.
>     #
>     PATH=/usr/bin:/usr/sbin:/usr/local/bin:/sbin:/bin
>     export PATH
>     #
>     umask 033
>     echo "$4" >/var/run/$1.ip
>     #
>     #
>     # Build redirect webpage, put it on ISP's webserver
>     /etc/ppp/ip-up.webpage "$4"
>     #
>     /usr/bin/logger -i -t /etc/ppp/ip-up "$1 [$4] connected to ISP [$5]"
>     # Done...

### The Harder Part

Now that I had arranged for my PPPoE client to run a script, I had to build the script it would run. The script would have to build an HTML document that would redirect the reader to my server's IP address, and FTP that document to my ISP's web server. It would have to preserve the internal security of my system by refraining from exposing my ISP userid and password to local users, and should do this with simple tools and a minimum of programming.

The security aspects were satisfied by making the script 'execute only'[5], so no local user could read it's contents, and by ensuring that no password was exposed to `ps(1)`[6,7] as a command line argument. The HTML was built through a simple "here document"[8] fed into a `cat(1)`[9] command that created a temporary file of HTML. The new IP address was written into the HTML through the shell variable substitution process that occurs with "here document" processing. Finally, the temporary file was transmitted to my ISP using a command line `ftp(1)`[10] command, with its parameters all passed through another "here document". This second "here document" permitted me to pass the user name and password into the FTP client without exposing them on a command line.

The `/etc/ppp/ip-up.webpage` script (below) is primitive and not very elegant, but it gets the job done.

>     #!/bin/bash

>     # Validate that we get 1 and only 1 parameter
>     case $# in
>       1) ;;
>       *) /usr/bin/echo Usage: $0 ip-address-or-dns-name
>          exit 1 ;;
>     esac
>     # the $1 parameter is the IP address assigned to our system

>     # Establish today's date (for html)
>     DATE=`/usr/bin/date`

>     # allocate empty tempfile, terminate if unable to allocate
>     TEMPFILE=`/usr/bin/mktemp /tmp/$1=XXXXXX` || exit 2

>     # build webpage (redirect) html into tempfile
>     # NB: $1 is our local IP address, passed in from ip-up
>     #     $DATE is the current date and time
>     #     With the "here document", these variables will
>     #     be substituted into the stream by the shell
>     /usr/bin/cat >$TEMPFILE <<END
>     <html>
>     <head>
>     <!-- $DATE -->
>     <meta http-equiv="refresh" content="0;url=http://$1/">;
>     </head>
>     </html>
>     END

>     # send webpage (redirect html) to webserver
>     #  ISP_ADDRESS is FTP address of ISP's web server
>     #  ISP_USERID  is my userid at ISP's FTP server
>     #  ISP_PASSWD  is my password at ISP's FTP server
>     #  NB: ISP_USERID, ISP_PASSWD set as local environment
>     #      vars so that they don't appear as parameters
>     #      in local 'ps ax' listings
>     #      With the "here document", these variables will
>     #      be substituted into the stream by the shell
>     ISP_ADDRESS=webserver.isp.com
>     ISP_USERID=username
>     ISP_PASSWD=password

>     /bin/ftp -n <<STOP
>     open $ISP_ADDRESS
>     user $ISP_USERID $ISP_PASSWD
>     ascii
>     put $TEMPFILE index.htm
>     bye
>     STOP

>     # delete tempfile
>     /bin/rm -f $TEMPFILE

>     # terminate
>     exit 0

### Olly-olly, Outs in free!

Now, when my ISP changes my IP address, my PPPoE server invokes `/etc/ppp/ip-up`, giving it my new IP address. This script invokes my `/etc/ppp/ip-up.webpage` script, which builds and installs a redirect webpage at my ISP's webserver that points at my new IP address. All I have to do is browse a specific web page on my ISP's webserver, and I'll be redirected to my webpage on my server.

So, with a little scripting, and the resident automation in my Linux system, I've now got a way to find my server from the outside, no matter which IP address my ISP gives it. I guess you could say that my server has given up the game of "hide and seek", and is playing other games now.

#### Footnotes

[1] - ADSL
  ADSL (or "Asymmetrical Digital Subscriber Line") is a technology that permits an ISP to offer high speed internet connectivity over regular phone lines without impacting the regular use of the phone line. In other words, I can surf the internet while my wife chats with her friends over the phone.
[2] - PPPoE
  PPPoE (or "PPP over Ethernet") is a low level protocol that some ISPs use to deliver TCP/IP connectivity on ADSL lines. Since PPPoE uses PPP (the regular 'dial up' services) to manage the ADSL connection, all the PPP facilities and management tools work with a PPPoE-enabled ADSL line. This includes things like the `/etc/ppp/ip-up` script.
[3] - my PPPoE client
  is the "Roaring Penguin" PPPoE daemon that can be found at http://www.roaringpenguin.com/penguin/open_source_rp-pppoe.php. In the Slackware 9.0 Linux distribution that I run, the Roaring Penguin PPPoE daemon is contained in the `rp-pppoe-3.5-i386-1.tgz` package. However, to use this PPPoE daemon, you also need a PPP daemon; Slackware 9.0 uses the ANU PPP daemon found at ftp://cs.anu.edu.au/pub/software/ppp/, and in Slackware package `ppp-2.4.1-i386-2.tgz`.
[4] - `/etc/ppp/ip-up`
  The `/etc/ppp/ip-up` script is executed by the PPP daemon when ever it establishes a TCP/IP environment over the PPP link. Several parameters are given to the `ip-up` script, including the IP address assigned to our end of the PPP connection. I use this script to trigger the construction of the web page that gets placed on my ISP's server, and I use the supplied IP address in the contents of the web page.
[5] - "execute only"
  Scripts, like other executable files, can be set to be executable without being readable by using a "`chmod ug=x scriptname`" command. I want this because I don't want users on my system to be able to snoop through the text of the script to find my logon and password.
[6] - `xx(y)` notation
  Ancient Unix notation that indicates the documentation for topic xx can be found in section y of the printed or online manual. Most Linux users can read this documentation using the "`man y xx`" syntax from the command line, as in "`man 1 ps`".
[7] - `ps(1)`
  `ps` lists the particulars of all running processes. If given the correct options, it will show the entire command line used to invoke the process. I avoid placing userids and passwords into command lines as someone could snoop them out by running `ps` at the proper moment. Call me paranoid, but even paranoids have enemies. ;-)
[8] - "here document"
  A "here document" is a special form of redirection that routes text embedded in a script directly into a program's input. The shell can perform parameter expansion, command substitution, and arithmetic substitution on the contents of the "here document" before the resulting text is given to the program. This makes "here documents" ideal for my use, because I can have the shell customize the text as necessary without depending on complex editor commands.
[9] - `cat(1)`
  `cat` is a utility that concatenates files together, and outputs the resulting file to stdout. If no input files are named, `cat` will read it's input from stdin. This feature makes `cat` a handy, but dumb, text editor, and that's how I use it here.
[10] - `ftp(1)`
  The command line FTP client works in this environment, but it would have been better if it had some scripting ability. I use a "here document" to pass in the FTP commands, so as to avoid exposing my ISP access password on the command line. However, with the "here document" approach, the `ftp(1)` client won't abort the interaction if an FTP command goes wrong, and there is no way to detect or act on a command failure from within the "here document". So, I take a chance that the FTP script will work each and every time without fail.

Canadian by birth, and living in Brampton, Ontario, I am a career techie working at a major Canadian bank. For over 25 years, I've programmed on all sorts of systems, from Z80 CP/M up to OS/390. Primarily, I develop OS/390 MVS applications for banking services, and have incorporated Linux into my development environment.

Copyright © 2004, Lew Pitcher. Released under the Open Publication license

Published in Issue 105 of Linux Gazette, August 2004

<-- prev | next -->