Port knocking / web knocking musings

[ Note: This question is not really OS specific, nor application stack specific for that matter. Just some thoughts I'd like feedback on. ]

I'm toying around with the idea of deploying a simple "web knocking" (sort of similar in concept to port knocking) solution that I put together. Its purpose is to help restrict access to the sshd service on one of my hosts. The way a successful user session would progress is:
  1. Client connects to https.//my.host/knock
  2. Client is prompted for - and supplies - proper digest authentication credentials (which would be shared with allowed users in advance)
  3. Upon successful authentication, script runs in the background to add client IP to /etc/hosts.allow (for allowing sshd access)
  4. Client can now connect to sshd service on my.host without being blocked by tcp wrappers

I've done some testing, and I believe I have a sane way to implement all of this. In a nutshell, the script that adds the client IP elevates its privileges (via sudo) for just a single command -- i.e. to modify /etc/hosts.allow.

This would mean the (unprivileged) user running the web server would be a sudoer who is capable of running only one command -- e.g. /usr/local/bin/addme.sh.

-------

Any thoughts on this idea? It seems OK in theory, but it also occurs to me that the extra security layer may just be opening up more holes instead.

(I'd add: I am not soliciting suggestions on securing sshd through other means; we've had that discussion a few times before. ;) I am just curious to hear opinions on what I've outlined above.)
 
Sounds interesting. I'm assuming that you have to pass data between the authenticator and the script that modifies /etc/hosts.allow: those are being properly sanitised, I'd guess?
 
Thanks for bringing that up. The short answer is: sort of, but I was relying on a mod_python method to behave properly. (You are right; it needs to be sanitized.)

Without distracting too much from the concept itself, I'll break down some more specifics on how I have implemented this:
  • Apache web server is protecting the /knock/ directory with digest authentication
  • When /knock/ is successfully entered, Apache runs (via mod_python) a very simple python script, which determines the client IP address with a "request method"
  • The python script then runs "/usr/local/bin/addme.sh $ip", where $ip == the IP retrieved by python

I am thinking through sanity checks for addme.sh, and auditing for a valid IP is a good idea.
 
Well, FWIW I've got this implemented and tested in Fedora. (Chasing down and fixing selinux issues is a major hassle.)

I am going to port it to FreeBSD next -- very trivial, as there are only a couple (python and Bourne) scripts in my particular implementation.

-------

Any other comments on the idea here? Think it's dangerous / idiotic by design? Think it's worth further review and consideration? I may post a howto later in the week.
 
If you want to take it out of the web server's hands entirely (i.e. avoiding escalating privileges in an unsafe environment), you could run a repetitive script ("while true, do, sleep 10", etc.) as root, and let the web server only put the authenticated IP in a tmp file, to be picked up and sanitised/validated/processed by the root script, which performs all necessary actions on privileged files. Sort of a pick-up service.
 
Thanks -- I like that idea. I am going to think it through some more, and may end up using that approach. Having a root cronjob poll for new IPs would eliminate the web server user w/ sudo requirement, which is bothering me a little.
 
Running it from cron may involve 'longish' waiting times for those wanting to log in (a minute can seem long .. especially when authenticating), so running a background script with a short sleep cycle may be better. I run quite a few of those 'watchdog' scripts, and they consume next-to-zero resources, even after months of narcolepsy (a few seconds of CPU time per month, esp. when they do no more than 'if that file exists, do something with it, else sleep on').
 
In fact, the simplest approach (which I've used somewhere) is to tail -n 0 -F the https logfile and catch lines which signal a successful login, and feed the relevant fields (awk'ed out of the log line) to the access mechanism.
 
Thanks for the continued input. I am still leaning heavily toward cron so that I don't have to worry about keeping a background script alive. (It sounds like that hasn't been a problem, in your experience.)

I am still running through lots of different test cases. I hope to have a howto together in a day or two.
 
DutchDaemon said:
In fact, the simplest approach (which I've used somewhere) is to tail -n 0 -F the https logfile and catch lines which signal a successful login, and feed the relevant fields (awk'ed out of the log line) to the access mechanism.

That's a good idea for an alternative implementation. (It would eliminate the mod_python dependency altogether.)
 
Note that Apache has a very handy option to achieve this. Just make a second log entry in the relevant httpd config file (e.g. httpd-ssl.config):

Code:
CustomLog "|/path/to/script" combined

and let the script process each line it's being fed. It's the same mechanism as hanging a script on a syslog entry. Anyway, you'll figure out what's best for your situation. I just like getting freaky with scripts ;)
 
@anomie:
- are you using a static authentication? If so, there might be a risk of a replay attack. If so, you better use some kind of one time password.
OTOH, this might not be relevant for your solution at all.
 
@tingo: That is a good question / point.

As I have implemented this, I'll be using http digest authentication (which is supposed to prevent replay attacks). The username and password will be "pre-shared" in advance among sysadmins / shell users.
 
Well, I've finally completed round one of (my version of) this web knocking app: http://forums.freebsd.org/showthread.php?t=7267

Thanks again for all the good feedback. I ultimately opted to stay with mod_python (to create knock requests) and crond (to process those knock requests) for a number of reasons -- mostly just personal preference, TBH.

I'm not sure what I ultimately put together is a particularly good implementation of web knocking, but it's functional and - IMO - reasonably secure. (FWIW, it could also be modified to manipulate a PF table instead of hosts.allow.)
 
I know that anomie solved the problem, but just as an addition;
DutchDaemon said:
In fact, the simplest approach (which I've used somewhere) is to tail -n 0 -F the https logfile and catch lines which signal a successful login, and feed the relevant fields (awk'ed out of the log line) to the access mechanism.
anomie said:
That's a good idea for an alternative implementation. (It would eliminate the mod_python dependency altogether.)
You might wanna check this one out, its the same approach you mentioned, achived with a simple and short perl script:

Introduction

This utility dynamically adds a source address to be approved to access the SSH port on my home computer. It uses a property similar to "port knocking" that I will call "page knocking". For this example, a client accesses http:<site>/access.html and this causes the source address of the http page request to be added to an approved list. (Of course, the page 'access.html' is not the same as the page on my production computer). This client can then access my SSH port.

http://www.otterhole.ca/knock/
 
@s0xxx: Thanks for mentioning that. His implementation is pretty similar (a little closer to what DutchDaemon was suggesting WRT watching the access log).

He is not using any authentication, exactly. It's probably still sufficiently secure, though, so long as it's over https. (i.e. a visit to https.//host.here/secret-foo-page-name.html will not be sniffed on the wire).
 
Back
Top