Running a Web Server on a Home RouterPosted: October 15, 2007
You know that little blue box sitting in the corner of your home office, providing you with access to the glorious World Wide Web? Yes – I mean your wireless router. Did you know that you can serve web pages directly off of it? I bet you didn’t. Well I’m going to show you how.
Before we get started, however, I would like to remind you that you’re taking a risk (albeit small one if you know what you’re doing) here by voiding your warranty and modifying your router. Make sure you have a spare router just in case you screw up and brick it
First off, I’m going to assume that you’re a self-respecting geek who’s already flashed your Linksys, Buffalo, ASUS, Belkin, D-Link, Motorola, Netgear, or ZyXel router with some variant of homebrew Linux firmware. If not, I suggest you pick up a copy of Tomato, OpenWrt, DD-WRT, or the like and load it up. For this tutorial, I’m going to use Tomato 1.10 build 1188, since it happens to leave a decent amount of free space on the flash to host your own content, and is fairly feature rich.
Once you’ve got your router all configured and running an SSH server, go ahead and connect to it. If you run ps you should notice that there is an instance of httpd already running on port 80! Don’t get yourself excited though – this is the special web server used to admin the router, and it’s pointed at the read-only directory /www. Actually, if you notice, everything on the router is mounted read-only (with the exception of /tmp which is mounted tmpfs).
Unlocking Flash for Writing
Nevertheless, it is easy to unlock a portion of the flash memory for our own use. Point your browser at the internal webserver, click Administration and JFFS2, and then format/enable the journaled filesystem. It will be automatically mounted read-write on /jffs. As for the box about executing a script, tell it to run /jffs/start.sh – I’ll explain why in a minute.
Back in your open shell, change directory over into /jffs and type mkdir web. Ok, so now you’re thinking that we’re just going to point the existing /usr/sbin/httpd at /jffs/web – but you’re wrong! After a quick email with Jon, the creator of Tomato, I found out that the password prompt and default filename are statically compiled into the binary. Can you guess what that means? We get to compile our own special httpd with our own modifications – how exciting!
Compiling the Server
Grab a copy of the Tomato source code along with the official Linksys code. Don’t worry if you don’t have a Linksys router, we’re not compiling anything model-specific – in fact, I’m doing this with a Buffalo WBR2-G54. Extract everything in the way the Tomato README explains, and change to tomato/release/src/router/httpd.
Bring up httpd.c in your favorite *nix code editor (*cough* vim *cough*) and find and remove the line that says sprintf(header, "WWW-Authenticate: Basic realm=\"%s\"", realm);. Do the same for send_error(401, header, NULL);. This will remove the code that would have instructed the browser that it sent an invalid username/password. Next you have to remove the two lines that check the authentication on the router side. In the same file, locate and neutralize the two instances of if (!check_auth(authorization, cl)) return;. Finally, do a search-and-replace for status-overview.asp and replace it with whatever you want to call your default page (I picked index.asp). Save the file and close your editor.
Make sure your distro’s compilers are set up properly and make httpd, or just build the whole entire thing and copy the resulting binary file out of httpd. If you’re using Debian or Ubuntu as your host, you’ll want to apt-get install build-essential as root beforehand.
If something gets messed up and you’re getting build errors, you can just download and uncompress my prebuilt binary to your router.
# cd /jffs
# wget http://192.168.1.51/tomato110-custom-httpd.gz
# mv tomato110-custom-httpd.gz httpd.gz
# gunzip httpd.gz
Creating Boot Scripts
Remember that shell script we referenced a while ago? Now it is time to create it.
# cd /jffs
# cat << EOF > start.sh
/jffs/httpd -p 90
# chmod +x start.sh
Notice how the script changes into the directory that httpd will be serving up, and specifies port 90 as to not interfere with the internal web server used for configuration. (Don’t worry, we’ll map port 80 later on.)
In order to enable access from the outside, you will need to go to poke two holes in the NAT/firewall. First, go to Port Forwarding / Basic and forward external port 80 to internal port 90 on the internal IP assigned to your router. Second, go to Administration / Scripts, click the Firewall tab, and add this line of code to allow direct connections to port 90 on the router.
iptables -A INPUT -p tcp -m tcp --dport 90 -j ACCEPT
You can now reboot your router and do a quick test that your special instance of our new mini web server is running. Assuming the test went well, you can go ahead and fill up the tiny flash partition with some static HTML (remember to name the home page index.asp) for your own use — whatever that may be. Happy hacking