📜 ⬆️ ⬇️

Creating Guest Access to the Internet with Web Authentication

Greetings, colleagues.

Prologue


Our task arose - to make guest Internet. Those. Guest comes, connects to the network (WiFi or cable) and tries to access the Internet. When you try to enter the site, he "must ask for a password."
It should be noted that similar questions about solving such a problem often arise in 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 on the forums did not make a choice, therefore it was decided to do this on my own in free minutes.

Basic requirements:


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

Implementation


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, we created a separate guest VLAN on the local network with the thought that, if necessary, any port would turn into a guest one. Since there were no extra servers, we used a virtual machine on Hyper-V, forwarding two VLANs to it — the guest and the Internet. CentOS 6.2 was installed as the OS - as the first one that came to hand. On it DHCP and named were regularly configured. Raised and configured httpd to work only on the internal interface. Well, the usual protection.
')

Customization


Actually, initially the idea was this: the router (Linux) makes DNAT on its internal interface for everyone except the IP addresses included in a certain 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 gives the opportunity, in particular, to tear off all current IP sessions. This should be done after changing the translation rules. The second one allows you 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 the Linux itself on the internal interface. Eth1 - external interface.
Those. SNAT we do to everyone. But for those who are not on the list of good, we say that in fact they do not want Yandex, but our local web server.
Actually, after that, by adding / removing the necessary addresses in the ipset “good” followed by a conntrack –F we can manipulate the permissions of IP addresses to go to the Internet.

Authentication and Authorization


Now it was necessary to do so in order to get on the Internet could only be a password. The idea of ​​implementation is as follows:
  1. Create an httpd index page that asks for a password. If the password is correct, put the IP address of the requester in the ipset “good”. Further, httpd is configured so that with a 404 error it displays an index page asking for a password. Those. Under these conditions, the internal IP address, when trying to access any HTTP address, will display the index page of the local server with a password request.
  2. We write a php script that generates 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 of it 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 a specific time (on the border of an hour, 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 an hour).
  3. We write a php script that checks the password and, in the case of the correct password, adds the IP address to the list of allowed ones and also resets the current sessions. There is one caveat - Apache works on behalf of a special user, and to manipulate the list of addresses and reset sessions, you need superuser rights. There are several options - either setting up the sudo system (which I didn’t really want to understand due to lack of time), or creating a pipe, on one side of which the script hangs on behalf of the superuser and reads commands, and on the other the php script writes the necessary commands to the script. I chose for myself just such a method. To create a pipe-type file, use the mknod pipename p

My scripts


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

Some nuances of scripts



Password Generator (index.php)

 <HTML> <FORM method="get" action=gen.php> <input name="PwdType" type="radio" value="Common" checked>Common Password</INPUT> <input name="PwdType" type="radio" value="CommonTill">Common Password till <select name="Till"> <option value=__8">8:00</option> ... <option value=__23">23:00</option> </select> </INPUT> <input name="PwdType" type="radio" value="Personal">Personal Password</INPUT> <select name="PersonalTill"> <option value=__8">8:00</option> ... <option value=__23">23:00</option> </SELECT> Client ID:<input name="ClientID" value=0> <INPUT type=submit value="Generate password"> </FORM> </HTML> 

Password Generator (gen.php)

 <? $Secret="123"; //Common secret $d=date("Ymd"); //Current Date $symbols=array(0,4,5,8,1,30); //Symbols in md5 hash for password. Numbers must be in 0..31 $ipnet="192.168.88."; if ( $PwdType == "Common" ) { $str=$d."-".$Secret; $r=md5($str); $res="*"; foreach ($symbols as &$i) {$res=$res.substr($r,$i,1);}; }; if ( $PwdType == "CommonTill" ) { $Till=utf8_decode($Till); $Till=substr($Till,1,strlen($Till)-2); $str=$d."-".$Till."-".$Secret; $r=md5($str); $res="#".$Till."-"; foreach ($symbols as &$i) {$res=$res.substr($r,$i,1);}; }; if ( $PwdType == "Personal" ) { $ip=$ipnet.$ClientID; $PersonalTill=utf8_decode($PersonalTill); $PersonalTill=substr($PersonalTill,1,strlen($PersonalTill)-2); $str=$d."-".$PersonalTill."-".$ip."-".$Secret; $r=md5($str); $res="$".$PersonalTill."-"; foreach ($symbols as &$i) {$res=$res.substr($r,$i,1);}; }; ?> <H2>Password="<?print $res;?>"</H2> <H1 align=center><a href="index.php">Return</a></H1> 

Password checker (index.php)

 <HTML> <!--0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF--> <? $addr="192.168.88.1"; $a=getenv("REMOTE_ADDR"); $s=getenv("SERVER_NAME"); $ipnet="192.168.88."; $p=strpos($a,$ipnet); if ($p === false) { print "<B>Internal error: <I>Wrong network (Network=$ipnet, Address=$a)</I></B> \n"; exit; }; if ($p > 0 ) { print "<B>Internal error: <I>Wrong network (Position=$p)</I></B> \n"; exit; }; ?> Please, call XXXX to get password. Your ID is <STRONG><? print substr($a,strlen($ipnet));?></STRONG> <FORM action=http://<?print $addr;?>/do.php method=post> <CENTER> Password <INPUT name=pwd type=password> <INPUT type=submit value="Activate Internet"> </CENTER> </FORM> </HTML> 

Password checker (do.php)

 <? $Secret="123"; //Common secret $d=date("Ymd"); //Current Date $symbols=array(0,4,5,8,1,30); //Symbols in md5 hash for password. Numbers must be in 0..31 $ipnet="192.168.88."; $ip=getenv("REMOTE_ADDR"); $pwd=$_POST["pwd"]; $fc=substr($pwd,0,1); //first charter is code of password type $PipeFile="./pipe"; if ($pwd == "") { print "<H1 align=center>Wrong empty password. <a href='/'>return</a></H1>"; 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 "<H1 align=center>Wrong password. <a href='/'>return</a></H1>"; exit; }; file_put_contents ( $PipeFile, substr($ip,strlen($ipnet))." $Till\n", FILE_APPEND); ?> <H1 align=center>Access to Internte granted</H1> 


A script that monitors the 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 

Source: https://habr.com/ru/post/163967/


All Articles