Creating Guest Internet Access with Web Authentication

Greetings, colleagues.

Prologue


We had a task - to make guest internet. Those. the guest arrives, connects to the network (WiFi or cable) and tries to access the Internet. When trying to enter the site, he should "ask for a password."
It should be noted that similar questions about solving such a problem often arise on the forums. There is a bunch of paid software (not considered in principle, because the task is low priority) and some free solutions. The search in the forums did not allow to make a choice, therefore it was decided to do this on their own in their free minutes.

The main requirements:


Resources for creation - both human (working time) and material - are minimal.
The load is small. Typically, no more than 10 people per day will use such a system.
The criticality of accessibility is low. If the system is broken, they will be repaired last. The loss of several packets is not fundamental.
Isolation is complete. If any parts are broken, the local network should not suffer.
Platform independence - customers will be all sorts of gadgets - from the phone to a large computer.

Implementation


After looking at the requirements and existing resources, it was decided to use a Linux router with one real IP address. Since the location of the guests was not exactly specified, they created a separate guest VLAN in the local network with the idea that, if necessary, any port becomes a guest port. Since there were no unnecessary servers, we used a virtual machine on Hyper-V, forwarding two VLANs to it - guest and Internet. As the OS they installed CentOS 6.2 - as the first one that came to hand. DHCP and named were regularly configured on it. Raised and configured httpd to work only on the internal interface. Well, the protection is ordinary.

Customization


Actually, the initial idea was this: a router (Linux) does DNAT on its internal interface for all but the IP addresses included in a list. And those who are on the list - they make SNAT out. After studying the docks, I had to deliver two packages - conntrack and ipset. The first makes it possible, in particular, to interrupt all current IP-sessions. This must be done after changing the translation rules. The second makes it possible to operate with lists of IP addresses that iptables understands.
This is implemented by the commands placed in rc.local:

echo 1> / proc / sys / net / ipv4 / ip_forward
ipset -N good iphash
iptables -t nat -A POSTROUTING -o eth1 -j MASQUERADE
iptables -t nat -A PREROUTING -m set! --set good src -j DNAT --to 192.168.88.1
conntrack -F


Here 192.168.88.1 is the address of Linux itself on the internal interface. Eth1 - external interface.
Those. We do SNAT for everyone. But for those who are not on the good list, we say that in fact they do not want Yandex, but our local web server.
Actually, after that, by adding / removing the necessary addresses to ipset “good” and then conntrack –Fwe can manipulate the permissions of IP addresses to go to the Internet.

Authentication and Authorization


Now it was necessary to make sure that access to the Internet was possible only with a password. The idea of ​​implementation is as follows:
  1. Create an index page in httpd that asks for a password. If the password is correct - puts the IP address of the requestor in ipset “good”. Further, httpd is configured so that with a 404 error, it displays an index page with a password request. Those. under these conditions, the internal IP address when trying to get to any HTTP address will display the index page of the local server with a password request.
  2. We are writing a php script generating a password. This script must be on a separate server. Access to the script should be limited. For example, the secretary may have access. How exactly to generate a password - everyone can think for himself. I made three types of passwords - a password that is valid for everyone until the end of the day, a password that is valid for everyone, but until some specific time (at the hour’s border, for example, 14:00, 17:00) and a password that is valid for a specific IP addresses to a specific time (on the border of the hour).
  3. We write a php script that checks the password and, if the password is correct, adds the IP address to the allowed list, and also resets the current sessions. There is one caveat - Apache works on behalf of a special user, and superuser rights are required to manipulate the address list and reset sessions. There are several options - either setting up the sudo system (which I didn’t really want to deal with due to lack of time), or creating a pipe, on the one hand of which the script hangs on behalf of the superuser and reads the commands, and on the other, the php script writes the necessary command script. I chose this method for myself. To create a file of type pipe, use the commandmknod pipename p

My scripts


In principle, creativity goes on. Everyone can do as he wants / can. I will give the sources of my scripts to give integrity to the article.
In my scripts, as described above, there are three types of password. Passwords are identified by the first character. The password, which is valid for everyone until the end of the day, starts with an asterisk. The password, which is for everyone, but with a time limit, begins with “# NN-“, where NN is the password validity period. The password for a specific IP with a time limit starts with “$ NN-“, where NN is the password expiration time. As the password, individual characters of the md5 hash of the string obtained from concatenating the date, secret (for all types), time (for the 2nd and 3rd types) and IP address (for the third type) are used. The number, the order of selection of characters from the hash, as well as the secret - must be set and, obviously, match on the password generator and on the checking script.

Some nuances of scripts

  • The page requesting the password displays a certain ID, which the client must inform the secretary. This is actually the last octet of the IP address. It is used only in password generation with binding to IP address.
  • The array $symbolsspecifies which characters from the hash will be used as a password. I use six characters, you can use any number greater than zero.
  • The command debugsent to pipe leads to the output of the table of current IP addresses into the debug file with the access validity time. Example:echo debug >pipe
  • In order for everything to work properly, you need to write a word in pipe once per hour (on the crown, preferably at: 00 or: 01 minute) update, and at midnightreload


Password Generator (index.php)

Common Password Common Password till Personal Password Client ID:

Password Generator (gen.php)

Password=""

Return


Password Checker (index.php)



Internal error: Wrong network (Network=$ipnet, Address=$a)
\n";
  exit;
 };
if ($p > 0 ) 
 {
  print "Internal error: Wrong network (Position=$p)
\n";
  exit;
 };
?>
Please, call XXXX to get password. Your ID is 
/do.php method=post>
Password

Password Checker (do.php)

Wrong empty password.
return";
  exit;
 };
if ( $fc == "*" )
 {
  $str=$d."-".$Secret;
  $r=md5($str);
  $res="*";
  foreach ($symbols as &$i) {$res=$res.substr($r,$i,1);};
  $Till=25;
 };
if ( $fc == "#" )
 {
  $p=strpos($pwd,"-");
  $Till=substr($pwd,1,$p-1);
  $str=$d."-".$Till."-".$Secret;
  $r=md5($str);
  $res="#".$Till."-";
  foreach ($symbols as &$i) {$res=$res.substr($r,$i,1);};
 };
if ( $fc == "$" )
 {
  $p=strpos($pwd,"-");
  $PersonalTill=substr($pwd,1,$p-1);
  $str=$d."-".$PersonalTill."-".$ip."-".$Secret;
  $r=md5($str);
  $res="$".$PersonalTill."-";
  foreach ($symbols as &$i) {$res=$res.substr($r,$i,1);};
  $Till=$PersonalTill;
 };
if ($pwd != $res)
 {
  print "

Wrong password. return

"; exit; }; file_put_contents ( $PipeFile, substr($ip,strlen($ipnet))." $Till\n", FILE_APPEND); ?>

Access to Internte granted



A script that monitors pipe and makes changes.

#!/bin/bash
PipeFile="/var/www/html/pipe"
LogFile="/var/log/script.log"
ErrFile="/var/log/script.err"
DebugFile="/var/log/script.debug"
ipnet="192.168.88."
ipset_prog="/usr/sbin/ipset"
ipset_setname="good"
ctr_prog="/usr/sbin/conntrack"
function debug()
{
local d=`date`
echo "$d Debuging started" >>$DebugFile
for i in `seq 1 254`; 
do
 if [ ${b[$i]} != "0" ]; then
  echo "b[$i] = ${b[$i]}" >>$DebugFile
 fi
done;
echo "==================== Debuging finised =================" >>$DebugFile
}
function initfunc()
{
#Init
for i in `seq 1 254`; 
do
 b[$i]="0"
done;
(
d=`date`
echo "$d Init function called" 
$ipset_prog -F $ipsetname
$ctr_prog -F
) &>>$LogFile
};
function update()
{
(  
local d1=`date`
local d=`date +%H`
echo "$d1 Update function called" 
for i in `seq 1 254`; 
do
 if [ ${b[$i]} -gt "0" ] && [ ${b[$i]} -le "$d" ]; then
  b[$i]=0
  ip2="$ipnet$i" 
  echo "Deleting address $ip2" &>>$LogFile
  $ipset_prog -D $ipset_setname $ip2 &>>$LogFile
  $ctr_prog -F
 fi
done
) &>>$LogFile
} #update
################# Start program #################
initfunc
while true;
do
while read line;
do
 a=( $line )
 ip=${a[0]}
 tm=${a[1]}
 if [ "$ip" == "reload" ]; then
  initfunc
  continue
 fi
 if [ "$ip" == "update" ]; then
  update
  continue
 fi
 if [ "$ip" == "debug" ]; then
  debug
  continue
 fi
 b[$ip]=$tm
 ip2="$ipnet$ip"
(  d=`date`
  echo "$d Added address $ip2 with time:$tm"
  $ipset_prog -A $ipset_setname $ip2 &>>$LogFile
  $ctr_prog -F
) &>>$LogFile
update
done <$PipeFile
done

Also popular now: