From 5c01597490d992377ef2c41fd6bed69aa7b6975d Mon Sep 17 00:00:00 2001 From: ljf Date: Mon, 13 Mar 2023 02:58:30 +0100 Subject: [PATCH] [wip] Captive portal --- conf/captiveportal_fakedns.service | 13 ++++++ config_panel.toml | 42 ++++++++++++++++++ hooks/post_iptables_rules | 68 ++++++++++++++++++++++++++++++ scripts/backup | 3 ++ scripts/config | 9 ++++ scripts/install | 9 ++++ scripts/remove | 10 +++++ sources/captiveportal_allow | 11 +++++ sources/captiveportal_fakedns | 35 +++++++++++++++ sources/index.php | 2 + 10 files changed, 202 insertions(+) create mode 100644 conf/captiveportal_fakedns.service create mode 100644 hooks/post_iptables_rules create mode 100644 sources/captiveportal_allow create mode 100644 sources/captiveportal_fakedns create mode 100644 sources/index.php diff --git a/conf/captiveportal_fakedns.service b/conf/captiveportal_fakedns.service new file mode 100644 index 0000000..aeb7647 --- /dev/null +++ b/conf/captiveportal_fakedns.service @@ -0,0 +1,13 @@ +[Unit] +Description=YunoHost Wifi Captive Portal +Requires=network.target +After=network.target + +[Service] +Type=oneshot +User=root +ExecStart=/usr/local/bin/captiveportal_fakedns +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target diff --git a/config_panel.toml b/config_panel.toml index 7b11364..d303bcf 100644 --- a/config_panel.toml +++ b/config_panel.toml @@ -123,6 +123,20 @@ name = "Configuration" pattern.regexp = '^([0-9.]{7,15}|[0-9a-fA-F:]+)$' pattern.error = "Not an ip" + [main.hotspot1.captive_portal__1] + ask = "Captive portal" + type = "boolean" + bind = "array_settings()" + visible = "advanced__1" + help = "Activate the captive portal mode" + + [main.hotspot1.captive_portal_url__1] + ask = "Local captive portal URL" + type = "string" + bind = "array_settings()" + visible = "advanced__1 && captive_portal__1" + help = "Local URL on which redirect onto when the user mac address is not yet allowed" + [main.hotspot2] name = "Hotspot 2" visible = "! no_antenna && multissid >= 2" @@ -183,6 +197,20 @@ name = "Configuration" pattern.regexp = '^([0-9.]{7,15}|[0-9a-fA-F:]+)$' pattern.error = "Not an ip" + [main.hotspot1.captive_portal__2] + ask = "Captive portal" + type = "boolean" + bind = "array_settings()" + visible = "advanced__2" + help = "Activate the captive portal mode" + + [main.hotspot1.captive_portal_url__2] + ask = "Local captive portal URL" + type = "string" + bind = "array_settings()" + visible = "advanced__2 && captive_portal__2" + help = "Local URL on which redirect onto when the user mac address is not yet allowed" + [main.hotspot3] name = "Hotspot 3" visible = "! no_antenna && multissid >= 3" @@ -243,3 +271,17 @@ name = "Configuration" pattern.regexp = '^([0-9.]{7,15}|[0-9a-fA-F:]+)$' pattern.error = "Not an ip" + [main.hotspot1.captive_portal__3] + ask = "Captive portal" + type = "boolean" + bind = "array_settings()" + visible = "advanced__3" + help = "Activate the captive portal mode" + + [main.hotspot1.captive_portal_url__3] + ask = "Local captive portal URL" + type = "string" + bind = "array_settings()" + visible = "advanced__3 && captive_portal__3" + help = "Local URL on which redirect onto when the user mac address is not yet allowed" + diff --git a/hooks/post_iptables_rules b/hooks/post_iptables_rules new file mode 100644 index 0000000..903c649 --- /dev/null +++ b/hooks/post_iptables_rules @@ -0,0 +1,68 @@ +#!/bin/bash + +multissid=$(grep multissid /etc/yunohost/apps/hotspot/settings.yml | cut -d: -f2 | sed "s/[ ']//g") +interface=$(grep wifi_device /etc/yunohost/apps/hotspot/settings.yml | cut -d: -f2 | sed "s/[ ']//g") +IFS='|' read -a captive_portal <<< "$(grep captive_portal /etc/yunohost/apps/hotspot/settings.yml | grep -v captive_portal_url | cut -d: -f2 | sed "s/[ ']//g")" +IFS='|' read -a ipv4 <<< "$(grep ip4_nat_prefix /etc/yunohost/apps/hotspot/settings.yml | cut -d: -f2 | sed "s/[ ']//g")" +IFS='|' read -a ipv6 <<< "$(grep ip6_net /etc/yunohost/apps/hotspot/settings.yml | cut -d: -f2 | sed "s/[ ']//g")" + +iptables -w -N hotspot_fwd +ip6tables -w -N hotspot_fwd +for (( j=0; j/dev/null 2>&1 +then + ynh_script_progression --message="Removing $app captiveportal_fakedns service" + yunohost service stop captiveportal_fakedns + yunohost service remove captiveportal_fakedns +fi #================================================= # STOP AND REMOVE SERVICE @@ -40,6 +46,7 @@ ynh_script_progression --message="Stopping and removing the systemd service..." # Remove the dedicated systemd config ynh_remove_systemd_config --service=$service_name +ynh_remove_systemd_config --service=captiveportal_fakedns #================================================= # REMOVE DEPENDENCIES @@ -56,6 +63,9 @@ ynh_script_progression --message="Removing app main directory..." # Remove the app directory securely ynh_secure_remove --file="/usr/local/bin/$service_name" +ynh_secure_remove --file="/usr/local/bin/captiveportal_fakedns" +ynh_secure_remove --file="/usr/local/bin/captiveportal_allow" +ynh_secure_remove --file="/etc/hotspot" for FILE in $(ls /tmp/.ynh-hotspot-* 2>/dev/null) do diff --git a/sources/captiveportal_allow b/sources/captiveportal_allow new file mode 100644 index 0000000..03c671d --- /dev/null +++ b/sources/captiveportal_allow @@ -0,0 +1,11 @@ +#!/bin/bash + +date=$(date +"%Y-%m-%d %T") +ip=$1 +mac=$(arp -a $ip | cut -d" " -f4 | head -n1) +interface=$(grep wifi_device /etc/yunohost/apps/hotspot/settings.yml | cut -d: -f2 | sed "s/[ ']//g") +if ! grep $mac /etc/hotspot/allowed.csv ; then + echo "$date,$ip,$mac" >> /etc/hotspot/allowed.csv + iptables -w -I hotspot_fwd 1 -s $ip -m mac --mac-source $mac -j ACCEPT + iptables -t nat -w -I PREROUTING 1 -i $interface -s $ip -m mac --mac-source $mac -j ACCEPT +fi diff --git a/sources/captiveportal_fakedns b/sources/captiveportal_fakedns new file mode 100644 index 0000000..c634bfd --- /dev/null +++ b/sources/captiveportal_fakedns @@ -0,0 +1,35 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use Net::DNS::Nameserver; + +my $ip4_addr = shift @ARGV; + +sub reply_handler { + my ($qname, $qclass, $qtype, $peerhost,$query,$conn) = @_; + my ($rcode, @ans, @auth, @add); + + if($qtype eq "A") { + my ($ttl, $rdata) = (1, $ip4_addr); + my $rr = new Net::DNS::RR("$qname $ttl $qclass $qtype $rdata"); + push @ans, $rr; + $rcode = "NOERROR"; + + } else { + $rcode = "NXDOMAIN"; + } + + return ($rcode, \@ans, \@auth, \@add, { aa => 1 }); +} + +my $ns = new Net::DNS::Nameserver( + LocalPort => 4253, + LocalAddr => $ip4_addr, + ReplyHandler => \&reply_handler, + Verbose => 0 + ) || die "Couldn't create fake nameserver object.\n"; + +$ns->main_loop; + +exit 0; diff --git a/sources/index.php b/sources/index.php new file mode 100644 index 0000000..c5f05db --- /dev/null +++ b/sources/index.php @@ -0,0 +1,2 @@ +