[Из песочницы] Настройка wifi авторизации через sms под ubuntu 16.04

Привет Хабр! Не так давно, в нашей организации встала задача узаконить wifi доступ, но чтобы в дальнейшем использование системы было бесплатно. (Согласно постановлению Правительства №758 от 31 июля 2014 г. и №801 от 12 августа 2014 г. — все публичные WIFI сети обязаны производить идентификацию пользователей). У нас 10 залов для мероприятий (от 30 до 400 человек), а в день в среднем проходит от 4 до 12, плюс постоянная текучка народа и капризные пользователи.
image

Для начала расскажу, как у нас организована сеть. Если не вдаваться в подробности, то развешены точки доступа HP MSM430(J9651), управление через HP MSM760(J9420A) и шлюз HP F1000-EI (JG214A). Выбор оборудования не удачен, но работаем с тем, что имеем.

Так сложилось, что по жизни я больше полюбил Windows системы, но получив задачу, начитавшись кучу статей, пришел к выводу, что для этой цели лучше всего подойдет *nix система. Выбор пал на Ubuntu сервер 16.04. Дальше было несколько дней мучений, но в итоге все получилось.

Эта статья для тех, кто любит Windows, смотрит на Ubuntu и ставит с нуля кучу софта.

Как я решил все организовать:

Имеется открытая сеть, назовем ее Free. Пользователь при подключении переадресуется на хотспот с страницей авторизации. Там ему выдается код доступа к интернету и номер телефона, на которое он должен отправить сообщение (немного необычно, но стояла задача экономии, в том числе и на смс). Как только сообщение приходит к нам, доступ в интернет сразу открывается. Оговорюсь, симку и номер в модеме лучше использовать МТС (не реклама), так как только у них не нашел возможности отправить СМС с сайта без указания левого номера (хотя это проблема, но в процессе решения).

Что нам потребуется:

  • лошадка Ubuntu Server 16.04
  • прокси Squid в прозрачном режиме
  • Mysql (MariaDB)
  • Nginx
  • PHP FPM
  • USB redirector (виртуальная машинка под HyperV)
  • модем Huawei E153 МТС
  • SMS tools для чтения смс
  • и тд. и тп.

Начнем.

Установка Ubuntu Server 16.04


Здесь все довольно банально:
  1. Создаем виртуальную машинку (2 ядра, 4 Гб памяти, 2 сетевых интерфейса, 30 Гб диск)
  2. Качаем последний дистрибутив, подключаем, ставим…
  3. Настраиваем сетевые интерфейсы, один смотрит в vlan сети wifi, второй в сторону шлюза

Настройка сети и форвардинг пакетов


Редактируем файл настройки сетевых интерфейсов:
nano /etc/network/interfaces

auto eth0
iface eth0 inet static
        # в сторону интернет шлюза
        address 10.66.66.6
        netmask 255.255.255.240
        network 10.66.66.0
        broadcast 10.66.66.15
        gateway 10.66.66.1
        dns-nameservers 10.66.66.1
auto eth1
iface eth1 inet static
        # в сторону wifi пользователей
        address 10.0.87.254
        netmask 255.255.248.0
        network 10.0.80.0
        broadcast 10.0.87.255

Убедимся в том, что в системе действительно присутствуют IPv6 интерфейсы:
ip a | grep inet

Также можно увидеть, что некоторые приложения вывешивают TCP прослушиватели на интерфейсах IPv6. Посмотреть все прослушиваемые в системе порты можно командой:
sudo ss -lnptu | sort

Чтобы выключить поддержку IPv6 на всех сетевых интерфейсах сразу, открываем на редактирование файл sysctl.conf
sudo nano -Y sh /etc/sysctl.conf

В конец файла добавляем строки для включения форвардинга и отключения IPv6:
net.ipv4.ip_forward=1
net.ipv6.conf.all.disable_ipv6 = 1

Для проверки того, что наша опция сможет быть прочитана sysctl во время загрузки выполним:
sudo sysctl -p

/etc/init.d/networking restart

Установка MySQL


В качестве сервера баз данных, выбрал MaridDB. По функционалу в чем-то даже лучше MySQL, но статья не об этом.
apt-get install mariadb-server
#настраиваем пароли на доступ
mysql_secure_installation
#запускаем mysql и сбрасываем привилегии
 mysql -u root
 use mysql;
 update user set plugin='' where User='root';
 flush privileges;

Проверяем, все ли хорошо у нас запустилось:
service mysql status

Установка Squid с поддержкой SSL и отключением IPv6


Есть много статей, как собирать и устанавливать прокси, но этот этап оказался наверное самый муторный. По умолчанию, в репозитарии убунты лежит Squid без поддержки SSL. Решил пересобрать, и так прошло 2 дня… В итоге получился свой мануал, как собрать под x64 последнюю версию 3.5.20.

Ставим необходимый софт для сборки:

apt-get install git fakeroot checkinstall build-essential devscripts patch libssl-dev libgnutls28-dev
apt-cache policy squid3
apt-get build-dep squid3

Расcкомментируем репозитарии исходников и добавим новый:
nano /etc/apt/sources.list
deb-src http://ftp.de.debian.org/debian/ testing main contrib non-free

Новый репозитарий, будет ругаться на ключи, поэтому сразу их получим:
gpg --keyserver keyserver.ubuntu.com --recv 8B48AD6246925553
gpg --export --armor 8B48AD6246925553 | sudo apt-key add -
gpg --keyserver keyserver.ubuntu.com --recv 7638D0442B90D010
gpg --export --armor 7638D0442B90D010 | sudo apt-key add -

Не забываем обновить информацию о репозитариях:
apt-get update

Чтобы не захламить рабочую папку, переходим в tmp и скачиваем из testing последнюю версию squid с правилами для сборки под debian
cd /tmp/
apt-get source squid3

Текущая версия 3.5.19, обновляемся до последней 3.5.20
wget http://www.squid-cache.org/Versions/v3/3.5/squid-3.5.20.tar.gz
tar -xf squid-3.5.20.tar.gz
mkdir ./squid-3.5.20/debian/
cp -r ./squid3-3.5.19/debian/*  ./squid-3.5.20/debian/
cd squid-3.5.20/
nano debian/rules

Добавляем строчки (не забываем указать путь к openssl.cnf, у меня это /etc/ssl)
--disable-ipv6 \
--enable-icap-client \
--enable-ssl-crtd \
--with-openssl=/etc/ssl \

Подтверждаем патч и собираем (ждем примерно 10–15 минут)
dpkg-source --commit
    #patch update to squid 3.5.20
debuild

Смотрим, какие пакеты у нас собрались:
ls -l /tmp/ | grep .deb$

И начинаем установку Squid:
apt-get install squid-langpack  libdbi-perl
dpkg -i squid-common_3.5.19-1_all.deb
dpkg -i squid_3.5.19-1_amd64.deb
dpkg -i squid3_3.5.19-1_all.deb
dpkg -i squidclient_3.5.19-1_amd64.deb

если по каким-либо причинам, подвис установщик, то сбрасываем блокировку:
fuser -vki /var/lib/dpkg/lock

запускаем и проверяем статус
service squid start
systemctl status -l squid

в ответ должны увидеть что-то похожее
squid.service - LSB: Squid HTTP Proxy version 3.x
   Loaded: loaded (/etc/init.d/squid; bad; vendor preset: enabled)
   Active: active (running)

Теперь займемся его настройкой, для начала создадим SSL сертификат и сохраним конфиг по умолчанию:
cd /etc/squid
openssl req -new -newkey rsa:1024 -days 365 -nodes -x509 -keyout squidCA.pem -out squidCA.pem

mv ./squid.conf ./squid.conf.default
nano ./squid.conf

Файл конфигурации, почти без изменений взят из статьи.
acl localnet src 10.0.80.0/21
acl SSL_ports port 443
acl Safe_ports port 80          # http
acl Safe_ports port 21          # ftp
acl Safe_ports port 443         # https
acl Safe_ports port 70          # gopher
acl Safe_ports port 210         # wais
acl Safe_ports port 1025-65535  # unregistered ports
acl Safe_ports port 280         # http-mgmt
acl Safe_ports port 488         # gss-http
acl Safe_ports port 591         # filemaker
acl Safe_ports port 777         # multiling http
acl CONNECT method CONNECT

dns_nameservers 10.66.66.1
http_access deny !Safe_ports

http_access deny CONNECT !SSL_ports

http_access allow localhost manager
http_access deny manager

http_access allow localnet
http_access allow localhost
http_access deny all

#http_port 3128
#прозрачный порт указывается опцией intercept
http_port 10.0.87.254:3128 intercept options=NO_SSLv3:NO_SSLv2

#также нужно указать непрозрачный порт, ибо если захотите вручную указать адрес
#прокси в браузере, указав прозрачный порт, вы получите ошибку доступа, поэтому нужно
#указывать непрозрачный порт в браузере, если конечно такое желание будет, к тому же в логах #сыпятся ошибки о том, что непрохрачный порт не указан=)
http_port 10.0.87.254:3130 options=NO_SSLv3:NO_SSLv2

#и наконец, указываем HTTPS порт с нужными опциями
https_port 10.0.87.254:3129 intercept ssl-bump options=ALL:NO_SSLv3:NO_SSLv2 connection-auth=off cert=/etc/squid/squidCA.pem

always_direct allow all
sslproxy_cert_error allow all
sslproxy_flags DONT_VERIFY_PEER

#укажем правило со списком блокируемых ресурсов (в файле домены вида .domain.com)
acl blocked ssl::server_name  "/etc/squid/blocked_https.txt"
acl step1 at_step SslBump1
ssl_bump peek step1

#терминируем соединение, если клиент заходит на запрещенный ресурс
ssl_bump terminate blocked
ssl_bump splice all

sslcrtd_program /usr/lib/squid/ssl_crtd -s /var/lib/ssl_db -M 4MB

coredump_dir /var/spool/squid
refresh_pattern ^ftp:           1440    20%     10080
refresh_pattern ^gopher:        1440    0%      1440
refresh_pattern -i (/cgi-bin/|\?) 0     0%      0
refresh_pattern .               0       20%     4320
cache_dir aufs /var/spool/squid 2048 49 256
maximum_object_size 61440 KB
minimum_object_size 3 KB

cache_swap_low 90
cache_swap_high 95
maximum_object_size_in_memory 512 KB
memory_replacement_policy lru

#logfile_rotate 31
logfile_daemon /usr/lib/squid/log_db_daemon
access_log daemon:/127.0.0.1:3306/base/table/user/password squid

Создаем файл со списком блокируемых ресурсов
nano ./blocked_https.txt

Обращаю внимание, что логи будем писать в базу данных. Для этого создаем таблицу:
CREATE TABLE `access_log` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `time_since_epoch` decimal(15,3) DEFAULT NULL,
  `time_response` int(11) DEFAULT NULL,
  `ip_client` char(15) DEFAULT NULL,
  `ip_server` char(15) DEFAULT NULL,
  `http_status_code` varchar(10) DEFAULT NULL,
  `http_reply_size` int(11) DEFAULT NULL,
  `http_method` varchar(20) DEFAULT NULL,
  `http_url` varchar(500) DEFAULT NULL,
  `http_username` varchar(20) DEFAULT NULL,
  `http_mime_type` varchar(50) DEFAULT NULL,
  `squid_request_status` varchar(50) DEFAULT NULL,
  `squid_hier_status` varchar(20) DEFAULT NULL,
 PRIMARY KEY (`id`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

запускаем и проверяем статус
service squid start
systemctl status -l squid

в ответ должны увидеть что-то похожее
 squid.service - LSB: Squid HTTP Proxy version 3.x
   Loaded: loaded (/etc/init.d/squid; bad; vendor preset: enabled)
   Active: active (running) 

Проверяем используемые порты и версию установленного squid
sudo ss -lnptu | grep :3128 
sudo ss -lnptu | grep :3129
sudo ss -lnptu | grep :3130

squid -version
Squid Cache: Version 3.5.20

Ставим USB-Redirector и модем


Так как, используется кластер под HyperV 2012R2, то возникает проблема, а именно как нам воткнуть модем. В нашей организации довольно успешно используется: USB-Redirector. Ставим его под Ubuntu
cd /tmp/
wget http://www.incentivespro.com/usb-redirector-linux-x86_64.tar.gz
tar -xf usb-redirector-linux-x86_64.tar.gz
./usb-redirector-linux-x86_64/installer.sh install-client

подключаемся к серверу, куда подключили модем

usbclnt -addserver 10.X.X.X:32032
usbclnt -autoconnect on 1

смотрим перечень всех usb устройств
usbclnt -l
================= USB CLIENT OPERATION SUCCESSFUL ===============
List of USB servers and devices:

   1: USB server at 10.X.X.X:32032
      Mode: auto-connect     Status: connected
   -   7: HUAWEI Mobile
           Vid: 12d1   Pid: 1001   Port: 3-2
           Mode: manual-connect   Status: disconnected

устанавливаем драйверы модема

apt-get install usb-modeswitch usb-modeswitch-data

подключаемся и проверяем, установлен ли модем

usbclnt -connect 1-7
ls /dev | grep ttyUSB

в ответ должны увидеть:
ttyUSB0
ttyUSB1
ttyUSB2

Устанавливаем SMS-tools


Информацию о пакеты можно найти на сайте разработчика.
Устанавливаем и настраиваем:
apt-get install smstools
nano /etc/smsd.conf

находим строчку [GSM1]
[GSM1]
 device = /dev/ttyUSB0
 incoming = yes
 baudrate = 9600
 eventhandler = /var/www/sms_recieve.php

создаем файл обработчика входящих смс и делаем его исполняемым
nano /var/www/sms_recieve.php
chmod 755 /var/www/sms_recieve.php
service smstools restart

Настраиваем DHCP сервер


apt-get install isc-dhcp-server
nano /etc/default/isc-dhcp-server
#указываем, на каком интерфейсе будет слушать dhcp сервер
INTERFACES="eth1"
sudo nano /etc/dhcp/dhcpd.conf 
# указываем
authoritative;
subnet 10.0.80.0 netmask 255.255.248.0 {
  range 10.0.80.1 10.0.86.254;
  option domain-name-servers 10.0.87.254;
  option domain-name "wifi-free";
  option subnet-mask 255.255.248.0;
  option routers 10.0.87.254;
  option broadcast-address 10.0.87.255;
  default-lease-time 7200; #2h
  max-lease-time 72000; #20h
}

/etc/init.d/isc-dhcp-server restart 

Настраиваем Nginx и PHP 5.6 FPM


Так как php обновилось до 7 версии и во всех репозитариях ubuntu уже лежит php7, то добавляем новый и ставим:
add-apt-repository ppa:ondrej/php
apt-get install php5.6-cli php5.6-common php5.6-mysql php5.6-gd php5.6-fpm php5.6-cgi php-pear 

останавливаем установленный сервис и редактируем файлы настройки
service php5.6-fpm stop

nano /etc/php/5.6/fpm/php.ini
	cgi.fix_pathinfo = 0
	post_max_size = 200M
	upload_max_filesize = 200M

nano /etc/php/5.6/fpm/pool.d/www.conf
	security.limit_extensions = .php .php3 .php4 .php5
	listen = /run/php/php5.6-fpm.sock
	listen.owner = www-data
	listen.group = www-data
	listen.mode = 0660

service php5.6-fpm start

Можно убедится в том, что права доступа к сокету установлены верно:
ls -la /run/php/php5.6-fpm.sock
#srw-rw---- 1 www-data www-data 0 May  2 16:36 /run/php/php5.6-fpm.sock

Проверяем:
php -v

PHP 5.6.23-2+deb.sury.org~xenial+1 (cli)
Copyright (c) 1997-2016 The PHP Group
Zend Engine v2.6.0, Copyright (c) 1998-2016 Zend Technologies
    with Zend OPcache v7.0.6-dev, Copyright (c) 1999-2016, by Zend Technologies

Переходим к Nginx
apt-get install nginx nginx-extras

Основные настройки Nginx хранятся в файле /etc/nginx/nginx.conf.

Настройки базового сайта хранятся в файле /etc/nginx/sites-available/default.

Базовый конфигурационный файл сайта принято помещать в папку /etc/nginx/sites-available/ и затем включить его путём добавления символической ссылки на этот файл в папке /etc/nginx/sites-enabled/.

touch /etc/nginx/sites-available/hotspot.domain.com
ln -s /etc/nginx/sites-available/hotspot.domain.com /etc/nginx/sites-enabled/
mkdir /etc/nginx/common

Дальше буду краток, так как пояснения можно почитать по ссылке. Создаем общие файлы настройки сервера, в которых описываем настройки безопасности, сжатия, кэширования и php.
touch /etc/nginx/common/upstream
nano /etc/nginx/common/upstream

upstream php-fpm
{
	# PHP5.6-FPM сервер
	server unix:/run/php/php5.6-fpm.sock;
}

touch /etc/nginx/common/security
nano /etc/nginx/common/security

add_header		X-Frame-Options		"SAMEORIGIN";
add_header		X-Content-Type-Options	"nosniff"; 

touch /etc/nginx/common/gzip
nano /etc/nginx/common/gzip

gzip		on;
gzip_disable	"msie6";
gzip_comp_level	6;
gzip_min_length	1100;
gzip_buffers	16 8k;
gzip_proxied	any;
gzip_types	text/plain application/xml text/css text/js text/xml application/x-javascript text/javascript application/javascript application/json application/xml+rss; 

touch /etc/nginx/common/php-fpm
nano /etc/nginx/common/php-fpm

# Настройки порта или сокета PHP-FPM производятся в файле "/etc/php/5.6/fpm/pool.d/www.conf"
fastcgi_pass	php-fpm;
# Порядок важен - строчка "include fastcgi_params" должна быть первой
include fastcgi_params;
fastcgi_split_path_info			^(.+?\.php)(/.*)?$;
# Вместо переменной "$document_root" можно указать адрес к корневому каталогу сервера и это желательно (см. http://wiki.nginx.org/Pitfalls)
fastcgi_param	SCRIPT_FILENAME		$document_root$fastcgi_script_name;
fastcgi_param	PATH_TRANSLATED		$document_root$fastcgi_script_name;
# См. http://trac.nginx.org/nginx/ticket/321
set		$path_info		$fastcgi_path_info;
fastcgi_param	PATH_INFO		$path_info;
# Additional variables
fastcgi_param	SERVER_ADMIN		email@example.com;
fastcgi_param	SERVER_SIGNATURE	nginx/$nginx_version;
fastcgi_index	index.php;

touch /etc/nginx/common/cache
nano /etc/nginx/common/cache

location ~* ".+\.(?:ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|css|swf|js|atom|jpe?g|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$"
{
	access_log	off;
	log_not_found	off;
	expires		max;
}

В моем случаи, у меня был публичный wildcard сертификат для моего домена. Распаковываю файл сертификата и устанавливаю права доступа
 openssl pkcs12 -in certname.pfx -nocerts -out  /etc/nginx/ssl/key.pem -nodes
 openssl pkcs12 -in certname.pfx -nokeys -out /etc/nginx/ssl/cert.pem
 openssl rsa -in /etc/nginx/ssl/key.pem -out /etc/nginx/ssl/domain.key 

cd /etc/nginx/ssl/
chown www-data:www-data domain.key
chmod 400 domain.key

touch /etc/nginx/common/ssl
nano /etc/nginx/common/ssl

ssl_certificate /etc/nginx/ssl/domain.crt;
ssl_certificate_key /etc/nginx/ssl/domain.key;

ssl_session_timeout                     20m;    # время 20 минут
ssl_session_cache                       shared:SSL:20m; # размер кеша 20МБ
ssl_protocols                           TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers               on;
ssl_ciphers                             ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AE$

Настраиваем наш сервер:

nano /etc/nginx/sites-available/hotspot.domain.com
include common/upstream;
server
{
	listen	80;
	server_name hotspot.domain.com;
	root	/var/www;
	index	index.php index.html index.htm;
	client_max_body_size	200m;	# увеличение максимального объема файла для загрузки до 200МБ
	# Buffers
	fastcgi_buffers 64 4K;
	include common/security;
	include common/gzip;
	location "/"
	{
		index index.php index.html index.htm;	# варианты индексных файлов если имя файла в запросе не задано
		try_files	$uri $uri/	=404;	# проверить есть ли файл из запроса на диске, иначе - вернуть ошибку 404
		include common/deny;
		include common/cache;
		include common/php-fpm;
	}
}

и делаем редирект с сервера по умолчанию (не забываем, что DNS сервер должен резолвить имя hotspot.domain.com):
nano /etc/nginx/sites-available/default
server {
        listen 80 default_server;
        listen [::]:80 default_server;
        listen 443 ssl default_server;
        listen [::]:443 ssl default_server;
        include common/ssl;
        rewrite ^ http://hotspot.domain.com?url=$scheme://$host$request_uri? redirect;
...
}

Настраиваем phpmyadmin


Для более удобной работы с базой данных, устанавливаем phpmyadmin.
apt-get install phpmyadmin
apt-get install mcrypt php5.6-mcrypt php-gettext

touch /etc/nginx/common/phpmyadmin
nano /etc/nginx/common/phpmyadmin

 location /phpmyadmin {
        root /usr/share/;
        index index.htm index.html index.php;
        location ~ ^/phpmyadmin/(.+.php)$ {
            try_files $uri = 404;
            root /usr/share/;
            fastcgi_pass unix:/run/php/php5.6-fpm.sock; 
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $request_filename;
            include /etc/nginx/fastcgi_params;
        }
        location ~* ^/phpmyadmin/(.+.(html|ico|xml|css|jpg|png|js|txt|gif|jpeg))$ {
            root /usr/share/;
        }
    }
   location /phpMyAdmin {
        rewrite ^/* /phpmyadmin last;
    }

и аналогично подключаем к nginx
nano /etc/nginx/sites-available/hotspot.domain.com
	include common/phpmyadmin;

перезапускаемся
service nginx restart
service php5.6-fpm restart

Настройка фаервола iptables


В данном разделе, нам требуется ограничить доступ к серверу и завернуть трафик на прокси. Для начала, настроим сохранение всех правил после перезагрузки:
apt-get install iptables-persistent ipset

так как в нашей задаче, нам необходимо, чтобы сохранялись и таблицы ipset, то немного правим следующий файл:
nano /usr/share/netfilter-persistent/plugins.d/15-ip4tables
#в функцию save_rules()
	touch /etc/iptables/rules.v4
	touch /etc/iptables/ipset.rules
	chmod 0640 /etc/iptables/ipset.rules
	chmod 0640 /etc/iptables/rules.v4
	iptables-save > /etc/iptables/rules.v4
	ipset save > /etc/iptables/ipset.rules
#в функцию load_rules()
	ipset restore < /etc/iptables/ipset.rules
    iptables-restore < /etc/iptables/rules.v4 2> /dev/null

Для сохранения правил на фаерволе используем команду:
netfilter-persistent save

Для отслеживания и сброса установленных соединений используем conntrack
apt-get install conntrack

Настраиваем правила iptables
#создаем таблицу авторизованных mac-ip
ipset --create authorized macipmap --network 10.0.80.0/21
ipset -L authorized

#создаем дополнительные цепочки обработки правил
iptables -t nat -N toSQUID
iptables -t nat -N toHOTSPOT
#очищаем все ранее созданные правила
iptables -t nat -F PREROUTING

#пробрасываем DNS на наш сервер
iptables -t nat -A PREROUTING -i eth1 -p udp --dport 53 -j DNAT --to 10.66.66.1
#если ip,mac авторизован, то перенаправляем на squid
iptables -t nat -A PREROUTING -i eth1 -m set --match-set authorized src,src -j toSQUID
#если ip,mac не авторизован, то перенаправляем на заглушку
iptables -t nat -A PREROUTING -i eth1 -j toHOTSPOT

iptables -t nat -F toHOTSPOT
# в заглушке, все запросы перенаправляем на наш сервер nginx
iptables -t nat -A toHOTSPOT -p tcp -m multiport --dports 80,8080 -j DNAT --to-destination 10.0.87.254:80
iptables -t nat -A toHOTSPOT -p tcp -m multiport --dports 443 -j DNAT --to-destination 10.0.87.254:443
iptables -t nat -A toHOTSPOT -j RETURN

iptables -t nat -F toSQUID 
# перенаправляем разрешенные порты на прокси
#создаем обход хотспота для telegram (так и не смог заставить работать через squid)
iptables -t nat -A toSQUID -p tcp -d 149.154.164.0/22 --dport 443 -j ACCEPT
#аналогично для whatsapp
iptables -t nat -A toSQUID -p tcp -m multiport --dport 4244,5242,5228,5223,5222 -j ACCEPT
iptables -t nat -A toSQUID -p tcp -m multiport --dports 80,25,465,110,995,119,563,8080 -j REDIRECT --to-ports 3128
iptables -t nat -A toSQUID -p tcp -m multiport --dports 443 -j REDIRECT --to-ports 3129
iptables -t nat -A toSQUID -j RETURN

#Включаем NAT для прошедших пакетов
iptables -t nat -A POSTROUTING -s 10.0.80.0/21 -o eth0 -j MASQUERADE

#политика по умолчанию для входящих пакетов
iptables -P INPUT ACCEPT
iptables -F INPUT 
#разрешаем траффик на lo
iptables -A INPUT -i lo -j ACCEPT
#разрешаем доступ к портам управления из вышестоящей сети
iptables -A INPUT -i eth0 -p tcp -m multiport --dports 22,80,443 -j ACCEPT
#разрешаем пинг и трасерт
iptables -A INPUT -p icmp -m icmp --icmp-type 0 -j ACCEPT
iptables -A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT
#разрешаем работу уже открытых соединений
iptables -A INPUT -p TCP -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p UDP -m state --state ESTABLISHED,RELATED -j ACCEPT
#разрешаем доступ на прокси
iptables -A INPUT -i eth1 -p tcp -m multiport --dport 3128,3129,3130 -j ACCEPT
iptables -A INPUT -i eth1 -p udp -m multiport --dport 3128,3129,3130 -j ACCEPT
№разрешаем доступ на заглушку
iptables -A INPUT -i eth1 -p tcp -m multiport --dports 80,443 -j ACCEPT
#политика по умолчанию для входящих пакетов
iptables -P INPUT DROP

#создаем цепочку для проблемных сервисов, которым разрешена пересылка
iptables -N FORWARD_AUTHORIZED
iptables -F FORWARD_AUTHORIZED 
# разрешаем пересылку telegram
iptables -A FORWARD_AUTHORIZED -p tcp -d 149.154.164.0/22 --dport 443 -j ACCEPT
# разрешаем пересылку whatsapp
iptables -A FORWARD_AUTHORIZED -p tcp -m multiport --dport 4244,5242,5228,5223,5222 -j ACCEPT
iptables -A FORWARD_AUTHORIZED -j RETURN

#политика по умолчанию для пересылаемых пакетов
iptables -P FORWARD ACCEPT
iptables -F FORWARD 
#разрешаем пересылку на dns
iptables -A FORWARD -i eth1 -p udp --dport 53 -d 10.66.66.1 -j ACCEPT
#разрешаем работу уже открытых соединений
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
#перенаправляем авторизованных клиентов на проверку в цепочку FORWARD_AUTHORIZED
iptables -A FORWARD -i eth1 -m set --match-set authorized src,src -j FORWARD_AUTHORIZED
#политика по умолчанию для пересылаемых пакетов
iptables -P FORWARD DROP

Для отлавливания проблем используем следующие команды
#сброс установленных соединений с заданного ip
conntrack -D conntrack --orig-src 10.0.8X.XXX
#просмотр новых записей в логе
tail -f /var/log/firewall | grep 10.0.8X.XXX

Настраиваем php файлы обработки запросов и скрипты


Создаем таблицы в нашей базе данных:
SET FOREIGN_KEY_CHECKS=0;
CREATE TABLE IF NOT EXISTS `mac-auth` (
  `mac` char(17) NOT NULL COMMENT 'мак адрес устройства',
  `code` int(6) NOT NULL COMMENT 'код авторизации',
  `phone` varchar(15) NOT NULL DEFAULT '' COMMENT 'телефонный номер с которого пришло смс',
  `updated` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'дата последнего изменения',
  `created` datetime DEFAULT CURRENT_TIMESTAMP COMMENT 'дата создания',
  UNIQUE KEY `mac` (`mac`),
  KEY `code` (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS `mac-ip` (
  `mac` char(17) NOT NULL COMMENT 'мак адрес устройства',
  `ip` varchar(15) NOT NULL COMMENT 'ip адрес устройства',
  `date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'дата изменения',
  KEY `mac` (`mac`),
  KEY `ip` (`ip`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS `mac-phone` (
  `mac` char(17) NOT NULL COMMENT 'мак адрес устройства',
  `phone` varchar(15) NOT NULL COMMENT 'номер телефона',
  `date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'дата изменения',
  KEY `mac` (`mac`),
  KEY `ip` (`phone`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

ALTER TABLE `mac-ip`
  ADD CONSTRAINT `mac` FOREIGN KEY (`mac`) REFERENCES `mac-auth` (`mac`) ON DELETE CASCADE ON UPDATE CASCADE;

SET FOREIGN_KEY_CHECKS=1;

создаем скрипт, при входе в систему, для проверки работы всех служб:
nano /var/www/check_services.sh

#!/bin/bash
function check {
	printf %-30s "check $1"
	if (( $(ps -ef | grep -v grep | grep $1 | wc -l) > 0 ))
	then
		echo -e "[\033[32;1m OK \033[0m]"
	else
		echo -e "[\033[31;1m ERROR \033[0m]"
	fi
}

check dhcpd
check nginx
check mysql
check php-fpm 
check smstools
check squid
check usbsrvd

устанавливаем права запуска и добавляем на старт
chmod 755 /var/www/check_services.sh
nano  ~/.profile
     /var/www/check_services.sh

, а теперь исходники php файлов:
nano /var/www/sms_recieve.php

#!/usr/bin/php
$ip){
		sql_query('UPDATE `mac-auth` SET `code` = "0" WHERE `mac` IN ("'.implode('","',$macs).'")');
		//удаляем авторизацию на фаерволе
		shell_exec('sudo ipset -D authorized '.$ip .','.$mac);	
		//сбрасываем все подключения данного ip
		shell_exec('sudo conntrack -D conntrack --orig-src '.$ip);		
	}
	shell_exec('ipset save > /etc/iptables/ipset.rules');
}

echo $phone.'-'.$code;
$mac = sql_getValue('SELECT `mac` FROM `mac-auth` WHERE `code` = '.$code);
if ($mac){
	$ip = sql_getValue('SELECT DISTINCT(`ip`) FROM `mac-ip` WHERE `mac` = "'.$mac.'" ORDER BY `date` DESC');
	echo '-'.$mac.'-'.$ip;
	//вносим в базу информацию о номере телефона
	sql_query('INSERT `mac-phone` (`mac`,`phone`) VALUES("'.$mac.'","'.$phone.'")');
	sql_query('UPDATE `mac-auth` SET `phone` = "'.$phone.'", `code` = "1" WHERE `mac` = "'.$mac.'"');
	//разрешаем доступ на фаерволе	
	shell_exec('sudo ipset -A authorized '.$ip .','.$mac);	
	shell_exec('ipset save > /etc/iptables/ipset.rules');	
	shell_exec('sudo conntrack -D conntrack --orig-src '.$ip);	
}

//удаляем сообщение
unlink($sms_file);
?>

nano /var/www/hotspot/index.php

Доступ в интернет не может быть предоставлен.
Error with your network card. Internet access can not be granted'; } else { //проверяем, не изменился ли ip адрес у абонента $base_ip = sql_getValue('SELECT DISTINCT(`ip`) FROM `mac-ip` WHERE `mac` = "'.$mac.'" ORDER BY `date` DESC'); if ($base_ip != $ip){ sql_query('INSERT INTO `mac-ip` (`mac`,`ip`) VALUES ("'.$mac.'","'.$ip.'")'); } //проверяем авторизацию мак адреса $code = sql_getValue('SELECT `code` FROM `mac-auth` WHERE `mac` = "'.$mac.'"'); switch ($code){ case '1': //абонент авторизован shell_exec('sudo ipset -A authorized '.$ip .','.$mac); shell_exec('ipset save > /etc/iptables/ipset.rules'); header('Location: '.$url); //shell_exec('sudo conntrack -D conntrack -p tcp --dport 80 --state ESTABLISHED --src '.$ip.' --dst 10.0.87.254'); die; break; case '': //новый абонент sql_query('INSERT INTO `mac-auth` (`mac`,`code`) VALUES ("'.$mac.'",0)'); case '0': //абонент без авторизации и без кода доступа //генерируем код do { $code = rand(10000,99999); } while(sql_getValue('SELECT `mac` FROM `mac-auth` WHERE `code` = "'.$code.'"')); sql_query('UPDATE `mac-auth` SET `code` = '.$code.' WHERE `mac` = "'.$mac.'"'); default: //абонент без авторизации, но с генерированным кодом break; } } ?> Хотспот
Добро пожаловать в открытую сеть
Welcome to the open network
'.$error.'
';} else { ?>
Для получения доступа в интернет, Вам необходимо c Вашего телефона отправить смс с кодом: на номер: +7 917 XXX-XX-XX.
To get access to the Internet, you need to send SMS with code to number +7 917 XXX-XX-XX.
Если Вы уже отправили смс с кодом, то нажмите на кнопку:
If you have to send SMS with the code, then click on the button:
перейти в интернет
Задержка предоставления доступа в интернет зависит от скорости доставки нам Вашего смс сообщения.
Delayed access to the Internet depends on the speed of delivery of SMS messages to us.
Стоимость SMS-сообщений тарифицируется согласно вашему тарифному плану.
Cost of SMS-messages is charged according to your tariff plan.
Согласно постановлению Правительства №758 от 31 июля 2014г. и №801 от 12 августа 2014 г. - все публичные WIFI сети обязаны производить идентификацию пользователей.
According to the decree №758 of the Government dated 31 July 2014. and №801 from 12 August 2014 - all public WIFI network required to make user authentication.

nano /var/www/hotspot/connect.php

# mysql login params
$mysql_host = '';
$mysql_db = '';
$mysql_login = '';
$mysql_password = '';

$sql_link = mysqli_connect($mysql_host, $mysql_login, $mysql_password, $mysql_db) or die(mysql_error());
function sql_query($sql, $unbuffered = false){		
	global $sql_link;
	$resource = $unbuffered ? mysqli_real_query($sql_link, $sql) : mysqli_query($sql_link, $sql);
	if (mysqli_errno($sql_link) === 1016) {
		if (preg_match("/'(\S*)\.MYD'\. \(errno\: 145\)/", mysqli_error($sql_link), $m)) {
			mysqli_unbuffered_query("REPAIR TABLE ".$m[1]);
			$resource = $unbuffered ? mysqli_unbuffered_query($sql_link, $sql) : mysqli_query($sql_link, $sql);
		}
	}
	return $resource;
}

function sql_getValue($sql, $unbuffered = false){
	$resource = sql_query($sql, $unbuffered);
	$ret = false;
	if (is_resource($resource) || is_object($resource)) {
		$row = mysqli_fetch_row($resource); 
		$ret = $row[0];
	}
	return $ret;
}

function sql_getRows($sql, $use_key = false, $unbuffered = false){
	$resource = sql_query($sql, $unbuffered);
	$ret = false;
	if (is_resource($resource) || is_object($resource)) {
		$row = true;
		while ($row) { 			 
			$row = mysqli_fetch_array($resource, MYSQLI_ASSOC);  
			if (!$row) continue;
			$_row = $row;
			if ($use_key) {
				if (isset($_row[$use_key])) {
					$link = &$ret[$_row[$use_key]];
					$link = $_row;
				}
			} else {
				$link = &$ret[];
				$link = (count($_row) == 1) ? array_shift($_row) : $_row;
			}
		}
	}
	return $ret;
}	

function sql_getColumn($sql, $id_field = 'id', $unbuffered = false){
	$resource = sql_query($sql, $unbuffered);
	$ret = false;
	if (is_resource($resource) || is_object($resource)) {
		$row = true;
		while ($row) { 			 
			$row = mysqli_fetch_array($resource, MYSQLI_ASSOC);  
			if (!$row) continue;
			$_row = $row;
			$id = $_row[$id_field];
			unset($_row[$id_field]);
			$value = current($_row);			
			$ret[$id] = $value;				 			
		}
	}
	return $ret;
}

function sql_getRow($sql, $unbuffered = false){
	$resource = sql_query($sql, $unbuffered);
	$ret = false;
	if (is_resource($resource) || is_object($resource)) {
		$ret = mysqli_fetch_assoc($resource);          
	}
	return $ret;
}

Ну, а теперь, добавим в sudoers права для управления iptables из php

nano /etc/sudoers
	smsd	ALL=(ALL) NOPASSWD: /sbin/ipset
	smsd	ALL=(ALL) NOPASSWD: /sbin/iptables
	www-data	ALL=(ALL) NOPASSWD: /sbin/ipset
	www-data	ALL=(ALL) NOPASSWD: /sbin/iptables
	smsd	ALL=(ALL) NOPASSWD: /usr/sbin/conntrack
	www-data	ALL=(ALL) NOPASSWD: /usr/sbin/conntrack

На этом вроде всё. Система в работе уже месяц, полёт нормальный. Спасибо за внимание!

Комментарии (0)

© Habrahabr.ru