Бесплатный Wi-Fi, с небольшой изюминкой

Данная статья повествует о небольшом проекте бесплатной Wi-Fi сети, об основных технических проблемах и решениях. Цель – просто рассказать о достаточно оригинальном проекте.

Чуть меньше года назад, моё руководство решило на территории организации развернуть сеть бесплатного Wi-Fi для посетителей. Все было бы просто и прозаично, если бы не одно интересное условие: прежде чем пользователь попадет в Интернет, он должен посмотреть информационную страницу, с нашей «рекламой».

После небольшого анализа пришли к выводу, что подобное можно реализовать только с помощью отдельного сервера- шлюза, скорее всего на Linux-е. Краткое ТЗ с общим описанием задачи разосланное по компаниям, занимающимся разработкой/внедрением решений хотя бы приближенных к подобному, показало, что при заказе внешнего решения стоимость проекта взлетит за облака.

После долгих раздумий и фантазий на тему: как же это реализовать (с погружением в глубины протоколов, и десятками эротических фантазий как же подменить пакеты, заголовки и т.д.) было найдено простое и логичное решение.

Гениальное – просто



Краткий принцип работы:
  1. Пользователь подключаясь к сети получает ip-адрес с помощью обычного dhcp-сервера. В настройках указывается наш сервер в качестве шлюза по умолчанию.
  2. На сервере по умолчанию прописан такой набор правил iptables:

    [root@wifi ~]# iptables -L -t nat
    Chain PREROUTING (policy ACCEPT)
    target prot opt source destination
    REDIRECT tcp -- anywhere !192.168.143.1
    DROP udp -- anywhere !192.168.143.1 udp dpt:!domain

    Благодаря этому все запросы (кроме DNS- пакетов) от пользователя перенаправляются на локальный сервер.
  3. При первой попытке пользователя загрузить какую либо web-страницу он попадает на локальный nginx – сервер где ему демонстрируется html- страница содержащаястроку:
    <?php
    $run = "ts /usr/share/nginx/html/script.sh {$_SERVER['REMOTE_ADDR']}";
    exec($run);
    ?>

  4. И вот тут начинается самое интересное! Данная страница запускает лежащий на сервере скрипт (передавая ему в качестве параметра запуска ip-адрес клиента):

    #!/bin/sh
    LOG_FILE=/var/log/script_log
    {
    date | tr "\n" " "
    echo -n '|IP:'
    echo -n $1
    echo -n '|MAC:'
    /sbin/arp $1 -n | grep $1 | tr -s ' ' | cut -d ' ' -f 3 | tr "\n" " "
    mac=`/sbin/arp $1 -n | grep $1 | tr -s ' ' | cut -d ' ' -f 3 | tr "\n" " "`
    if ( sudo /sbin/iptables -n -L -t nat --line-numbers | grep $1 > /dev/null )
    then
    echo '|Rule exists!!'
    else
    echo -n '|Add rule '
    sudo /sbin/iptables -t nat --insert PREROUTING -s $1 -j ACCEPT
    echo -n '|Creat shedule:'
    echo "sudo /sbin/iptables -t nat -D PREROUTING -s $1 -j ACCEPT " | at -m now + 45 minutes
    sudo /usr/bin/python /usr/share/nginx/html/stat.py ${mac}
    fi
    } >> $LOG_FILE 2>&1


    Который делает два основных действия: добавляет правило в iptables, и создает отложенную задачу в at.

    Правило:

    sudo /sbin/iptables -t nat --insert PREROUTING -s $1 -j ACCEPT

    Помещается в цепочке PREROUTING на первую позицию, тем самым располагаясь ДО правил редиректа. В результате пользователь с данным адресом получает полный доступ в Интернет.

    Второе основное действие, создание правила в at:
    echo "sudo /sbin/iptables -t nat -D PREROUTING -s $1 -j ACCEPT " | at -m now + 45

    По сути ограничивает сессию пользователя 45-ю минутами, после чего правило будет удалено и пользователь снова попадет на редирект.


Схема была реализована, протестирована, и доказал свою жизнеспособность.

На этом стоило бы наверное закончить рассказ, но за «Чуть меньше года» было проделано работ по доводке проекта, устранению мелких косяков и добавлению достаточно оригинальных фич.

Менталитет



Первая возникшая проблема была в «злоупотребляющих». Было замечено, что некоторые особо жаждущие до халявы личности пользуются сетью чрезмерно долго. Для выявления подобных было решено вести статистику. Основная проблема в том что все биллинг- системы (или простенькие модули для ведения статистики) считают базовым элементом либо логин, либо ip-адрес. В нашем же случае первого просто нет, а ко второму привязываться нельзя, т.к. ip-адреса не статичны. Нужная была статистика опирающаяся на mac- адрес. После долгих изысканий пригодных решений найдено не было. Поэтому было решено изобрести очередной велосипед написать свое. Поскольку по сути необходимо, и достаточно, было считать кол-во сессий проведенных mac- адресом был написан простенький скрипт на python (к сожалению мои познания bash-а не смогли вытянуть данную задачу):

import sys
file = open('/var/log/stat.file','r')
line = file.readlines()
line2 = ''
i = 0
while len(line) > i :
line2 = str(line[i])
line2 = line2.rstrip()
line2 = line2.split()
if line2[0] == str(sys.argv[1]) :
line2[1] = str(int(line2[1])+1 )
del line[i]
line.insert(i,line2[0]+' '+line2[1]+'\n')
break
i = i + 1
else :
line.append(str(sys.argv[1])+' '+'1'+'\n')
###########
file.close()
file = open('/var/log/stat.file','w')
file.writelines(line)
file.close()


Который вызывался из основного скрипта. В logrotate была добавлена секция для данного файла с периодом в неделю. И через две недели бы получен список «постоянных клиентов» mac-адреса которых были занесены в черные списки на коммутаторе установленном перед сервером.

Велосипеды не пройдут!



Следующая проблема была более оригинальна: при одновременном запуске нескольких скриптов один из них выдавал ошибку iptables (чтото из серии “ iptables: Unknown error 4294967295 ” ), не выполняясь. Если это был скрипт на создание правила, то ничего страшного не происходило – пользователь просто обновлял страницу и запустившийся заново скрипт отрабатывал без ошибок (конечно же если опять не попал на одновременный запуск, судя по логам однажды одному бедолаге сильно не везло, и он 5 раз попадал на одновременный запуск ).
Если же с такой ошибкой вылетала задача удаления правила, то ip-адрес оставался в списках, и пользователь мог бесконечно долго и безнаказанно сидеть в Интернете.

Первое временное решение пришло в виде простенького bash-скрипта сверяющего список адресов в iptables и пуле задач at:

#!/bin/bash
LOG_FILE=/var/log/script_log
{
echo -n "Checking rules: "
date | tr "\n" " "
ar_ip=(`sudo iptables -L -t nat -n | grep ACCEPT | grep -v policy | cut -d ' ' -f 10 | tr '\n' ' '`)
ar_at=(`grep 168.143. /var/spool/at/* | cut -d ' ' -f 9 | tr '\n' ' '`)
count=${#ar_ip[@]}
for ((i=0; i <= count ; i++ ))
do
for h in "${ar_at[@]}"
do
if [[ ${ar_ip[$i]} == $h ]]
then
unset ar_ip[$i]
continue 2
fi
done
done
echo -n "|Delete rules: "
for i in ${!ar_ip[*]}
do
sudo /sbin/iptables -t nat -D PREROUTING -s ${ar_ip[$i]} -j ACCEPT
echo -n ${ar_ip[$i]} " "
done
echo "|Checking done"
} >> $LOG_FILE 2>&1


Скрипт был помещен в /etc/cron.hourly, и исправно удалял последствия ошибок iptables.В принципе решение было достаточным, но «нет предела совершенству» и я начал бороздить бескрайние просторы в поисках лучшего решения. И оно было найдено. На одном из «уютненьких» linux- форумов, один из мастодонтов указал мне в сторону данного проекта



По сути, данный проект – это небольшая разработка для запуска bash-скриптов, помещающая их в пул и выполняющая в порядке строгой очередности, один за другим.
Строчка запуска скрипта на веб- странице была модифицирована до вида

ts /usr/share/nginx/html/script.sh {$_SERVER['REMOTE_ADDR']}

И ошибки iptables исчезли.

Проблема третья, или как я помог open-source сообществу



Когда проект уже работал около полу года, и люди уже достаточно о нем узнали, появилась еще одна серьезная проблема: точки доступа (Lynksys wap54g) начали самопроизвольно отключатся/виснуть/перезагружаться. Естественно первое что пришло в голову это посмотреть логи. Как оказалось Lynksys не зря относят к «домашнему» сегменту, ведь домохозяйкам читать логи ни к чему. Единственное что содержалось в логах это когда какой mac- адрес подключился. К тому же формат логов был уникальный и обычный syslog- сервер его не понимал. Как всегда Lynksys предлагал какую- то подозрительную утилиту содержащее что- то волшебное в свое названии, якобы способную самостоятельно и мигом настроить вашу домашнюю сеть. После краткого изучения утилиты был сделан вывод о её непригодности, и опять началось долгое путешествие по бескрайним просторам. Где то в бескрайних степях На sf.net был найден маленький загнувшийся в 2009 проект (http://sourceforge.net/projects/wap54g-log/) который понимал формат логов точек и умел складывать их в файлик. «А большего нам и не надо!» ©

Оказалось, что рано радовались. Запись в логах имела вид:

May 20 12:04:42 /usr/local/bin/wap54g-log[17544] Wireless PC connected 00:26:C7:61:BB:26

И понять к какой же из точек подсоединился клиент, было невозможно. А следовательно и сделать вывод о нагрузке, как одной и самых возможных причин недостойного поведения точек.
И тут пришлось вспоминать С. Как оказалось не вспоминать, а изучать заново, т.к. то, что писалось на втором курсе в институте в Visual Studio, лишь отдаленно напоминало код этой небольшой утилиты

После вдумчивого чтения была обнаружена структура theiraddr, относящаяся к типу sockaddr _in (стандартный для unix тип сокета) который содержит в себе еще одну структуру in_addr которая содержит в себе единственную переменную s_addr типа in_addr_t. «Яйцо в утке, утка в зайце, заяц в шоке» ©. Самым веселым был комментарий «То, что используется такая структура, а не просто переменная типа in_addr_t, сложилось исторически».

Углубляясь все дальше и дальше, была обнаружена библиотека <arpa/inet.h>(О господи! Призрак арпы!) содержащая в себе функции для работы с данными структурами. Путем преобразования функцией inet_ntoa(strcu) и помещение всего это в вывод утилиты, получилось примерно так:

May 20 12:23:38 192.168.143.114 /usr/local/bin/wap54g-log[17544] Wireless PC connected 00:26:C7:61:BB:26

Ключевое отличие, в наличии адреса точки. Т.е. теперь можно судить о распределении нагрузки по точкам.
Как оказалось интуиция/логика/5-ая точка, меня не подвела. И после небольших наблюдений выяснилось, что Wap54g не выдерживает даже 30-ти одновременно подключенных пользователей.

Эту проблему еще предстоит решить. Пока вариантов кроме замены точек на что-то более «промышленное» я не вижу. Руководство решает что же делать, а разработчику утилиты было отправлено письмо с благодарностями, и предложениями под доработке.

Были и более мелкие проблемы: nginx выдавал 404 страницу, если адрес с которого происходил редирект, содержал название страницы и get-запрос в с троке адреса. Решилось одной строчкой в конфиге виртуального сервера «rewrite ^/(.*) /index.php last;». Но о подобных мелочах здесь не стоит рассказывать т.к. все они банальщина и повседневность.

Вместо заключения



Окончательное решение о написании данной стать утвердилось после прочтения пары статей «Как стать системным администратором — пособие для начинающих». Я не считаю себя каким- либо гуру, трезво понимаю что я – системный администратор linux- направленности средней руки. В это статье показана лишь малая часть того объема знаний что необходима для повседневной работы. Всего лишь html,bash,python, да небольшое знание С. Ну и как обязательная база: знание теории (всего ИТ, от сетей до программирования), и ОС (утилиты, системные приложения) с которой в основном приходится работать. Думаю, это будет неплохим дополнением.

Средняя зарплата в IT

113 000 ₽/мес.
Средняя зарплата по всем IT-специализациям на основании 5 444 анкет, за 2-ое пол. 2020 года Узнать свою зарплату
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 40

    0
    Интересно, насколько это законно.
      0
      Простите, что законно?
        –2
        Вы предоставляете услугу доступа к интернет.
        Но при этом Вы фальсифицируете ответы удаленных серверов.
        Почему я спросил: подобным бизнесом раньше занималась NebuAd и еще несколько компаний, но NebuAd теперь закрылась, а про других у меня сейчас нет точной информации.

          0
          Я думаю в современном законодательстве РФ сложно будет найти статью, или прецедент подходящий для этого случая. Но на офертой снимающей с нас любую ответственность, и разрешаю вопреки все законам собирать любую информацию о пользователе надо подумать.
          А что, будем брать пример с гугла.
        • НЛО прилетело и опубликовало эту надпись здесь
            0
            Лицензия на телематику есть.
            Единственное к чему действительно можно докопаться, это к тому что мы предоставляя услугу доступа в интернет заглядываем дальше ip- заголовков, что запрещено законом о провайдерах.
            Но это только на первый взгляд. По сути ни один из модулей (кроме nginx, за него не ручаюсь) не анализирует пакеты дальше ip- заголовка
              0
              Скажите пожалуйста, а где в каком законе/нормативном акте это сказано?

              Или есть какие-то прецеденты с наездом связьнадзора?

              Просто недавно у друзей в офисе делал гостевую wifi сеть и сделал я её без пароля и шифрования.
                0
                Де-юро, даже устанавливая дома роутер, и выпуская в Интернет через него компьютер брата/папы. и т.д. вы становитесь «провайдером телематических услуг». Т.е. предоставляете телематические услуги третьим лицам. Отсюда и требованье лицензии на телематику.

                И если к физ. лицам это применить сложно, то к юр. запросто. Прецендентов не знаю.

                А вообще :«Был бы клиент, а статья найдется».
                0
                Эм. А можно как-то аргументировать это, и желательно про отличие между предоставлением бесплатного вайфая и просто его наличием.
            –2
            > «было найдено простое и логичное решение.»
            Простое и логичное решение — это не показывать ненужную пользователю информацию.
              0
              Изначальным условием было демонстрирование страницы. В этом и есть изюминка.
              Просить изменить условия задачи нужно тогда, когда вы не в состоянии решить задачу в поставленных условиях.
              0
              vvh1te,
              Поставьте на точки OpenWRT и сделайте RADIUS-авторизацию. Жить станет проще, жить станет веселей! ;)

              В качестве RADIUS-сервера могу предложить nrsd из Net::Radius::Server. Единственное «но» — там нужно поверху на модуль из CPAN патч накатить, вот здесь лежит: rt.cpan.org/Public/Bug/Display.html?id=67812.
                +2
                Сейчас собираемся установить точку с dd-wrt, и посмотреть как она будет себя вести под такой нагрузкой. Но что-то мне подсказывает что будет не лучше.Если не хуже.

                Не совсем уловил мысль с RADIUS- сервером. А зачем в данной схеме RADIUS?
                  0
                  Во-первых, спасибо за статью.

                  Во-вторых, получилось что-нибудь через dd-wrt?
                0
                Посмотрите coova.org/ (прежде называлось chillispot). Возможно это как раз то, что нужно было использовать с самого начала.
                • НЛО прилетело и опубликовало эту надпись здесь
                    +1
                    post-запрос в с троке адреса

                    Может, таки GET?
                      0
                      Извиняюсь спутал, исправил.
                      Но, думаю суть все уловили.
                      0
                      Я тоже писал свой велосипед для почти такой же задачи.
                      У администратора «кафе» была своя админка, где он мог установить пароль для доступа в сеть (или без пароля), а также пароль после часа работы с одного mac.
                      Использовал в своём случае squid + php-скрипт.
                        +1
                        У микротика www.mikrotik.com/ есть готовый хотспот и мощный инструмент в виде скриптов. Для небольшой сети будет очень недорого стоить и весь описанный функционал можно поднять за день, включая время на изучение синтаксиса скриптов. Давно использую, очень доволен.
                          0
                          мне интересно, почему некоторые старые топики вдруг вновь всплывают на главной? Думаю щас почитаю интересную статью, уже настроился, а тут оп — облом! Уже читал…
                            0
                            Отдавать свои страницы можно намного проще и легче с помощью dns сервера, например, bind9.
                            Адреса можно собирать из arp таблицы.
                            Вы меня простите, но тонна скриптов на bash видятся мне костылем.
                              0
                              У меня прописан dns 8.8.8.8 всегда.
                                0
                                Вы знаете, в любой задаче при любых решениях будут какие то частности. Например, vpn.
                                Таких как Вы будет меньше одного процента. В данном случае скорее всего можно принебречь.
                                Но точно также можно заворачивать запросы на самые популярные днс на свой через тот же самый iptables.
                                  0
                                  Тогда у меня вообще не будет инета.
                                0
                                Минус механизма с DNS в том что полученный адрес будет храниться какое-то время, и последующие обращения к данному имени будут приводить на внутренний сервер.

                                Заставить неуправляемого пользователя нажать Ctrl+F5, нереально.
                                  0
                                  Единственная проблема которую я тут вижу — это кеш браузера.
                                  Но если Вы сконфигурите правильно веб сервер со страницей которую должно всем показываться…
                                  Все просто — клиент запршивает google.com, его запрос приходит на dns — dns говорит что google.com = 10.0.0.1, а на 10.0.0.1 конфигом редиректится на site.com.
                                    0
                                    Единственная проблема которую я тут вижу — это кеш браузера.

                                    Вот имменно он. Даже в данной системе была проблема с тем что браузер считал подложный сайт оригинальным, и не хотел уходить с иформационной страницы.
                                0
                                Как ни странно, меня совсем недавно посещала мысль организовать очень близкую по сути к вашему проекту «вайфай-халяву для народа». Но механизм размещения рекламы виделся мне в виде узкой полоски сверху/сбоку, висящей постоянно по время серфа, и ессесно меняющийся в зависимости от контекста просматриваемой страницы.
                                  0
                                  А можно узнать как планировалось релизовать подобное с тех. точки зрения?

                                  Модификация веб-трафика «налету» через прокси, с помещением основного сайта в фрейм?
                                    0
                                    Удалось узнать, как это реализуется?
                                      0
                                      Реализовывал через прозрачный прокси — privoxy, регулярными выражениями, делал вставку на лету в тело страницы. После тега вставлял div блок.
                                      0
                                      Присоединяюсь к вопросу :-)
                                        0
                                        Боюсь разочаровать вас, но дальше мысли это никуда с тех пор не ушло ((
                                      0
                                      Слушайте, ну вы странные. Есть же готовые софтинки ровно для этой задачи, уже со статистикой, учетом, отключениями и проверенные временем. Зачем городить такой ужас? :(
                                        0
                                        Можно пару примеров?
                                        С обязательным выполнением изначальной задачи:

                                        «прежде чем пользователь попадет в Интернет, он должен посмотреть информационную страницу, с нашей «рекламой».»

                                          0
                                          Вы никогда не пользовались публичным wifi, я так понимаю?
                                            0
                                            А пример?
                                        0
                                        Вы никогда не пользовались публичным wifi, я так понимаю?

                                        Хм, интересный вывод. Пользовался но подобно функционала не видел.

                                        Можно все таки пример системы?
                                          0
                                          Спасибо за статью. А какие устройства использовались?

                                          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                          Самое читаемое