i had to expose some web-based application hosted on a windows server to the internet. i don’t put too much trust in the developers of that particular application so i did not want to make it reachable from the public internet. while i could not use ip address based whitelist i could count on the end-user of the application to do a simple routine at the beginning of her/his work: visit another page and only then try to access the actual web-app. security through obscurity? perhaps, but for me it that’s one more layer of protection.
i’ve found out that i don’t need to dynamically rewrite apache2 config and add/remove ip addresses that are allowed to access the protected resource. this stackoverflow answer suggested that mod rewrite’s RewriteMap can be a solution. so here’s my solution:
apache2 vhost:
<VirtualHost *:443> DocumentRoot /var/www/ ServerName some.host.com ProxyPass /ProtectedResource http://10.0.0.5/ProtectedResource ProxyPassReverse /ProtectedResource http://10.0.0.5/ProtectedResource RewriteEngine On RewriteMap ipslist txt:/var/www/gatekeeper/list.txt <Location /ProtectedResource> AddOutputFilterByType INFLATE;SUBSTITUTE;DEFLATE text/html text/plain text/xml Substitute "s|http://10.0.0.5|https://some.host.com|i" RewriteCond %{REMOTE_ADDR} ^(.*)$ RewriteCond ${ipslist:%1|black} ^black$ [NC] RewriteRule ^ - [F] </Location> # ssl related stuff + logging </Virtualhost>
did you see what i did there? not just allowed reverse-proxying of request arriving to /ProtectedResource to an internal Windows-based 10.0.0.5 but also protecting access to it via https and re-writing on the fly references in html/xml to http://10.0.0.5 into https://ssl.host.com ; access to the /ProtectedResource is only possible for hosts that are listed in the text file /var/www/gatekeeper/list.txt
that file is updated by two scripts:
/var/www/gatekeeper/index.php – invoked by the user at the beginning of the working day by visiting https://some.host.com/gatekeeper/:
<?php $ip = $_SERVER['REMOTE_ADDR']; $x=file_get_contents('list.txt'); file_put_contents('list.txt',$x."\n".$ip.' '.time()); echo "hi $ip";
and /var/www/gatekeeper/cron.php – invoked from cron by visiting https://some.host.com/gatekeeper/cron.php – it’s responsible for clean-up of the stale entries older than few hours:
<?php $res = ""; foreach(explode("\n",file_get_contents('list.txt')) as $row){ $row = explode(' ',$row); if (sizeof($row)!=2)continue; if ($row[1]<time()-3600*8)continue; $res.=$row[0]." ".$row[1]."\n"; } file_put_contents('list.txt',$res);
there’s a risk of race conditions when during the execution of cron someone visits the gatekeeper page. in my case – where number of users is very small, i’m comfortable with it.