A few weeks ago, I was trying to figure out a way that I could remotely trigger a computer in my home to perform an automated task. For those that know me, you already know that I am extremely paranoid about providing remote access to anything, since it is very easy to misconfigure remote access and create large security holes.
I thought about trying to use port knocking as the trigger. For those not familiar with port knocking, the basic idea is that you can detect incoming packets (either TCP or UDP) to specific ports. If you receive the correct sequence of port connection requests, even if the packets do not ever get received by a server, then you can trigger automated tasks.
Port triggering might be used to open a port on a firewall, for example. The idea being that if you are away from your network and want to access a server, you send packets to ports 1234/udp, 5678/tcp, 8442/udp (or whatever sequence you like) and then a scripts allows access to port 443/tcp for your remote IP address. The theory is that since only you know the correct sequence of ports, you should be the only one to be able to gain access to your server.
But I ran into a problem with port knocking. I quickly found out that depending on the network I was on, I could not always send packets destined for the ports in my port knocking sequence. This could be due to the airport proxy system, a corporate network restriction, or a host of other network limiting techniques.
So I found a work-around to my problem which I am calling "web knocking" The basic idea is the same, except I am using mod_security within an Apache server to be the receiver of my incoming requests. In mod_security, I wrote a rule that looked like:
SecRule REQUEST_FILENAME "^/trigger.php" "phase:1,ID:'32100',drop,msg:'Automation triggered',exec:/home/my/automatic/script.sh"Let's take a quick look at this rule to see exactly what it does.
- The REQUEST_FILENAME clause tells us to match on just the filename portion of the URL requested
- The "^trigger.php" is a regex which is matched against the filename requested. For those not familiar with regex's, the ^ means "beginning of the string" - so this would match /trigger.php but not /my_trigger.php
- The "phase:1" portion tells mod_security that we want this rule executed in the early stages of the HTTP connection
- The ID number is whatever you choose to appear in the logs
- The "drop" tells mod_security to immediately drop the connection with no further reason provided to the client. This is key. Nobody will receive confirmation that the file does or does not exist or that any action was taken based on their connection attempt.
- The "exec" section tells mod_security to execute a specific script to take whatever action you desired.
It is also important to recognize that the script will be run under the ID used to run the web server. This could be www, nobody, or something else depending on your configuration. You need to make sure that the web server has proper permissions to run the script and whatever is inside the script.
You will also have access to portions of the HTTP request data as environment variables. To determine exactly what your web server provides to the script, I would suggest adding "env > /dev/shm/vars" (or something similar) so you can see all of the environment variables that exist for your use.
You can make the scripts as complex as you want, including chaining them together. For example, you could have /trigger1 run a script that creates a temp file. The script that triggers when you request /trigger2 could check for the existence of that temporary file and not run if the temp file does not exist.
Just remember that even a successful request from you will result in mod_security dropping the connection. Therefore, you won't get confirmation that your request was received.......but you could have your script send your cell phone an SMS or any other action so you know that your automation triggered properly.
Happy web knocking!