Вопрос или проблема
Я пытаюсь создать captive портал с помощью nginx, hostapd, nftables, dnsmasq и python-flask.
У меня есть две основные проблемы
- На Android не появляется всплывающее окно, но на Iphone/OSX появляется.
- Я не уверен, как перенаправить пользователя после подключения. У меня есть команда nftables, но для этого нужен IP-адрес. Поскольку nginx перенаправляет с порта 80 на 8080 (python приложение), я не знаю, как получить это.
Вот nginx.conf
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
if ($request_method !~ ^(GET|HEAD|POST)$) { return 444; }
# Handle iOS
if ($http_user_agent ~* (CaptiveNetworkSupport) ) {
return 302 http://go.portal;
}
# Handle Android captive portal detection
location = /generate_204 {
return 302 http://go.portal;
}
location = /gen_204 {
return 302 http://go.portal;
}
# Default redirect for any unexpected requests to trigger captive portal
# sign in screen on device.
location / {
return 302 http://go.portal;
}
}
server {
listen 80;
listen [::]:80;
server_name go.portal;
# Only allow GET, HEAD, POST
if ($request_method !~ ^(GET|HEAD|POST)$) { return 444; }
root /var/www;
index index.html;
location /api/ {
proxy_pass http://127.0.0.1:8080/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location / {
try_files $uri $uri/ =404;
}
# Redirect these errors to the home page.
error_page 401 403 404 =200 /index.html;
}
}
dnsmasq.conf
listen-address=192.168.2.1
no-hosts
# log-queries
log-facility=/var/log/dnsmasq.log
dhcp-range=192.168.2.2,192.168.2.254,72h
dhcp-option=option:router,192.168.2.1
dhcp-authoritative
dhcp-option=114,http://go.portal/index.html
# Resolve captive portal check domains to a "fake" external IP
address=/connectivitycheck.gstatic.com/10.45.12.1
address=/connectivitycheck.android.com/10.45.12.1
address=/clients3.google.com/10.45.12.1
address=/clients.l.google.com/10.45.12.1
address=/play.googleapis.com/10.45.12.1
# Resolve everything to the portal's IP address.
address=/#/192.168.2.1
Вот bash, который запускает всё.
INET_NIC=$(cat /run/inet_nic 2>/dev/null) || { echo "Connect to WiFi first"; exit 1; }
AP_NIC=$(cat /run/ap_nic 2>/dev/null) || { echo "Create AP first"; exit 1; }
echo 1 > /proc/sys/net/ipv4/ip_forward
nft flush ruleset
# Set up the filter table (Mode 1)
nft add table ip filter
nft add chain ip filter input '{ type filter hook input priority 0; policy accept; }'
nft add chain ip filter forward '{ type filter hook forward priority 0; policy accept; }'
nft add chain ip filter output '{ type filter hook output priority 0; policy accept; }'
# Set up the NAT table and chain for masquerading (Mode 2)
nft add table ip nat
nft add chain ip nat postrouting '{ type nat hook postrouting priority 100; }'
kill -9 $(pidof dnsmasq) 2>/dev/null
dnsmasq -C /etc/dnsmasq.conf -d 2>&1 > $LOG_F &
kill -9 $(pidof nginx) 2>/dev/null
mkdir /var/log/nginx 2>/dev/null
nginx &
kill -9 $(pidof evil_portal) 2>/dev/null
ip link set lo up
/usr/bin/evil_portal &
И вот команда, которую я бы ввёл, когда пользователь принимает условия.
nft add rule ip nat postrouting oifname wlan1 ip saddr 192.168.2.217 masquerade
Я не буду делиться python/html кодом, так как это всё работает отлично. В основном я получаю нажатие кнопки пользователем, и моя функция Python вызывает это. Но Python сообщает мне, что IP — 127.0.0.1, потому что nginx перенаправляет трафик с порта 80 на 8080
Спасибо 🙂
Относительно проблемы 2:
Но Python сообщает мне, что IP — 127.0.0.1, потому что nginx перенаправляет трафик с порта 80 на 8080
Я предполагаю, что вы получаете 127.0.0.1 из подключенного сокета, верно? Но вам действительно нужен IP-адрес клиента. Согласно вашему конфигу nginx, вы должны быть в состоянии получить его из заголовка перенаправленного HTTP-сессии X-Real-IP.
Ответ или решение
Для реализации captive portal с использованием nginx, hostapd, nftables, dnsmasq и python-flask, важно создать сеть, где подключенные пользователи будут перенаправляться на веб-интерфейс, позволяющий осуществлять авторизацию или принимать условия предоставления доступа.
Теория
При создании captive portal необходимо учитывать несколько ключевых элементов:
- nginx: Используется для обработки HTTP-запросов, создания редиректов и проксирования запросов к Flask-приложению.
- hostapd: Создает точку доступа Wi-Fi и регулирует доступ Wi-Fi клиентов.
- nftables: Управляет сетевой обработкой и обеспечивает механизм NAT для перенаправления сетевого трафика.
- dnsmasq: Используется для предоставления DHCP-услуг клиентам и перенаправления DNS-запросов для передачи клиентов на указанный веб-сервер.
Рассмотрение проблемы с Android
Одной из главных ваших задач является отображение captive portal на устройствах с Android, подобно тому, как это происходит на iPhone и macOS. Поп-ап, который вы ожидаете на Android, связан с особенностями проверки состояния интернета на устройстве через определенные URL. В данной конфигурации используется перенаправление специфичных для Android доменов на внутренний IP (10.45.12.1). Убедитесь, что все такие домены актуальны для последних Android-версий. Также проверьте, что ваше Android-устройство может отправлять неполные GET-запросы на стандартный портал, и эти запросы корректно обрабатываются сервером.
Пример: Схема применения NGINX
Ваш конфигурационный файл nginx в целом верно настроен для обработки запросов от captive portal. Однако, особое внимание следует уделить правильному обработчику для Android. Убедитесь, что все возможные вариации URL для проверки сети поддерживаются.
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
if ($http_user_agent ~* (Android|CaptivePortal) ) {
return 302 http://go.portal;
}
# Default redirect for any unexpected requests to trigger captive portal
location / {
return 302 http://go.portal;
}
}
Пример: Реализация DHCP и DNS с Dnsmasq
Конфигурация dnsmasq должна корректно обслуживать запросы, распределять IP-адреса и экранировать все запросы на заданные домены. Пример вашей настройки выглядит подходяще, но проверьте актуальность всех доменных имен, используемых в Android для проверки соединения.
Применение: Решение проблемы с IP-адресами
Ваша задача по получению реального IP-адреса клиента связана с тем, что nginx проксирует запросы во Flask-приложение, и оно принимает их с IP 127.0.0.1. Чтобы извлечь реальный IP-адрес, передаваемый nginx, используйте заголовок HTTP X-Real-IP
, как правило, он добавляется конфигурацией nginx и затем может быть извлечен во Flask:
from flask import request
@app.route('/your-endpoint', methods=['POST'])
def your_endpoint():
real_ip = request.headers.get('X-Real-IP')
if not real_ip:
real_ip = request.remote_addr
# Дальнейшая обработка
Практическая рекомендация
Понимание проблемы со специфичным поведением Android в captive portal может потребовать комплексного тестирования и отладки. Убедитесь, что сеть captive portal действительно доступна и корректно перенаправляет на всех устройствах. Это может потребовать изменения или дополнения к nginx-конфигурации, а также внесения правок в правила nftables для обеспечения правильной маршрутизации.
Заключение
Каждая часть системы captive portal (nginx, hostapd, dnsmasq, nftables, Flask) должна быть тщательно интегрирована и протестирована, чтобы обеспечить плавное и надежное функционирование. Отлаживайте как свой серверный код, так и сетевые конфигурации, чтобы идентифицировать и устранять узкие места. Внимательное тестирование на разных устройствах и платформах поможет гарантировать, что пользователи смогут легко подключаться и взаимодействовать с вашим порталом.