mirror of
https://github.com/openwrt/packages.git
synced 2025-12-10 12:41:22 +00:00
simple-captive-portal: add new package
This package intercepts/blocks traffic from 'interface' and redirects http requests to a splash page that you can personalize, stored in '/etc/simple-captive-portal/'. After clicking on 'connect' the MAC of the client is allowed, for 'timeout' seconds (24h), allowing both IPv4 and IPv6. If your guest interface defaults to input drop or reject (recommended), make sure to allow tcp 8888-8889 on input (and also dns and dhcp). Signed-off-by: Etienne Champetier <champetier.etienne@gmail.com>
This commit is contained in:
47
net/simple-captive-portal/Makefile
Normal file
47
net/simple-captive-portal/Makefile
Normal file
@@ -0,0 +1,47 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=simple-captive-portal
|
||||
PKG_VERSION:=2025.06.22
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_MAINTAINER:=Etienne CHAMPETIER <champetier.etienne@gmail.com>
|
||||
PKG_LICENSE:=GPL-2.0-or-later
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
Build/Compile=
|
||||
|
||||
define Package/simple-captive-portal
|
||||
SUBMENU:=Captive Portals
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
TITLE:=Simple captive portal
|
||||
PKGARCH:=all
|
||||
DEPENDS:=+uhttpd +uhttpd-mod-lua +luci-lib-ip
|
||||
endef
|
||||
|
||||
define Package/simple-captive-portal/install
|
||||
$(INSTALL_DIR) $(1)/etc/config
|
||||
$(INSTALL_CONF) ./files/etc/config/simple-captive-portal $(1)/etc/config/simple-captive-portal
|
||||
$(INSTALL_DIR) $(1)/etc/hotplug.d/net/
|
||||
$(INSTALL_DATA) ./files/etc/hotplug.d/net/00-simple-captive-portal $(1)/etc/hotplug.d/net/00-simple-captive-portal
|
||||
$(INSTALL_DIR) $(1)/etc/init.d
|
||||
$(INSTALL_BIN) ./files/etc/init.d/simple-captive-portal $(1)/etc/init.d/simple-captive-portal
|
||||
$(INSTALL_DIR) $(1)/etc/simple-captive-portal/
|
||||
$(INSTALL_DATA) ./files/etc/simple-captive-portal/index.html $(1)/etc/simple-captive-portal/index.html
|
||||
$(INSTALL_DIR) $(1)/usr/share/simple-captive-portal/
|
||||
$(INSTALL_DATA) ./files/usr/share/simple-captive-portal/capabilities.json $(1)/usr/share/simple-captive-portal/capabilities.json
|
||||
$(INSTALL_DATA) ./files/usr/share/simple-captive-portal/portal.lua $(1)/usr/share/simple-captive-portal/portal.lua
|
||||
$(INSTALL_DATA) ./files/usr/share/simple-captive-portal/redirect.lua $(1)/usr/share/simple-captive-portal/redirect.lua
|
||||
endef
|
||||
|
||||
define Package/simple-captive-portal/conffiles
|
||||
/etc/simple-captive-portal/
|
||||
/etc/config/simple-captive-portal
|
||||
endef
|
||||
|
||||
define Package/simple-captive-portal/description
|
||||
Provides a simple captive portal splash page.
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,simple-captive-portal))
|
||||
52
net/simple-captive-portal/README.md
Normal file
52
net/simple-captive-portal/README.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# Simple captive portal
|
||||
|
||||
This package intercepts/blocks traffic from 'interface' and
|
||||
redirects http requests to a splash page that you can personalize,
|
||||
stored in '/etc/simple-captive-portal/'.
|
||||
After clicking on 'connect' the MAC of the client is allowed,
|
||||
for 'timeout' seconds (24h), allowing both IPv4 and IPv6.
|
||||
|
||||
If your guest interface defaults to input drop or reject (recommended),
|
||||
make sure to allow tcp 8888-8889 on input (and also dns and dhcp).
|
||||
|
||||
Here an example (ipv4) firewall configuration.
|
||||
|
||||
```
|
||||
config zone
|
||||
option name 'guest'
|
||||
option forward 'REJECT'
|
||||
option output 'ACCEPT'
|
||||
option input 'REJECT'
|
||||
option network 'guest'
|
||||
|
||||
config forwarding
|
||||
option dest 'wans'
|
||||
option src 'guest'
|
||||
|
||||
config rule
|
||||
option name 'guest-dhcp'
|
||||
option src 'guest'
|
||||
option family 'ipv4'
|
||||
option proto 'udp'
|
||||
option dest_port '67'
|
||||
option target 'ACCEPT'
|
||||
|
||||
config rule
|
||||
option name 'guest-dns'
|
||||
option src 'guest'
|
||||
option family 'ipv4'
|
||||
list proto 'tcp'
|
||||
list proto 'udp'
|
||||
option dest_port '53'
|
||||
option target 'ACCEPT'
|
||||
|
||||
config rule
|
||||
option name 'guest-portal'
|
||||
option src 'guest'
|
||||
option family 'ipv4'
|
||||
list proto 'tcp'
|
||||
option dest_port '8888-8889'
|
||||
option target 'ACCEPT'
|
||||
```
|
||||
|
||||
To disable simple-captive-portal, just unset/comment 'interface' in the uci config.
|
||||
@@ -0,0 +1,5 @@
|
||||
config simple-captive-portal main
|
||||
#option interface guest
|
||||
#option port_redirect 8888
|
||||
#option port_portal 8889
|
||||
#option timeout 86400
|
||||
@@ -0,0 +1,5 @@
|
||||
INTF=$(uci -q get simple-captive-portal.main.interface)
|
||||
|
||||
if [ "$ACTION" = add -a "$DEVICENAME" == "$INTF" ]; then
|
||||
/etc/init.d/simple-captive-portal firewall
|
||||
fi
|
||||
@@ -0,0 +1,81 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
START=10
|
||||
USE_PROCD=1
|
||||
EXTRA_COMMANDS='firewall'
|
||||
|
||||
firewall() {
|
||||
local INTF PORT_REDIRECT TIMEOUT
|
||||
config_load "simple-captive-portal"
|
||||
config_get INTF main interface
|
||||
[ -z "${INTF}" ] && exit 0
|
||||
config_get PORT_REDIRECT main port_redirect 8888
|
||||
config_get TIMEOUT main timeout 86400
|
||||
|
||||
/usr/sbin/nft -f- <<EOF
|
||||
table inet simple-captive-portal
|
||||
flush table inet simple-captive-portal
|
||||
table inet simple-captive-portal {
|
||||
set guest_macs {
|
||||
type ether_addr
|
||||
timeout ${TIMEOUT}s
|
||||
}
|
||||
|
||||
chain prerouting {
|
||||
type nat hook prerouting priority mangle; policy drop;
|
||||
iif != ${INTF} accept
|
||||
ether saddr @guest_macs accept
|
||||
tcp dport 80 redirect to ${PORT_REDIRECT}
|
||||
fib daddr . iif type { local, broadcast, multicast } accept
|
||||
reject
|
||||
}
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
boot() {
|
||||
BOOT=1
|
||||
start "$@"
|
||||
}
|
||||
|
||||
start_service() {
|
||||
# firewall() called by hotplug on boot
|
||||
[ -z "${BOOT}" ] && firewall
|
||||
|
||||
local INTF PORT_REDIRECT PORT_PORTAL
|
||||
config_load "simple-captive-portal"
|
||||
config_get INTF main interface
|
||||
[ -z "${INTF}" ] && exit 0
|
||||
config_get PORT_REDIRECT main port_redirect 8888
|
||||
config_get PORT_PORTAL main port_portal 8889
|
||||
|
||||
procd_open_instance redirect
|
||||
procd_set_param command /usr/sbin/uhttpd -f -c /dev/null -k0 -h /etc/simple-captive-portal/ -l / -L /usr/share/simple-captive-portal/redirect.lua -p "${PORT_REDIRECT}"
|
||||
procd_set_param env PORT_PORTAL=${PORT_PORTAL}
|
||||
procd_add_jail simple-captive-portal-redirect log procfs sysfs ronly
|
||||
procd_add_jail_mount /etc/simple-captive-portal/
|
||||
procd_add_jail_mount /usr/share/simple-captive-portal/redirect.lua
|
||||
procd_add_jail_mount /usr/lib/uhttpd_lua.so
|
||||
procd_set_param user nobody
|
||||
procd_set_param no_new_privs
|
||||
procd_set_param respawn
|
||||
procd_set_param stdout 1
|
||||
procd_set_param stderr 1
|
||||
procd_close_instance
|
||||
|
||||
procd_open_instance portal
|
||||
procd_set_param command /usr/sbin/uhttpd -f -c /dev/null -k0 -h /etc/simple-captive-portal/ -l /connect -L /usr/share/simple-captive-portal/portal.lua -p "${PORT_PORTAL}"
|
||||
procd_add_jail simple-captive-portal-portal log procfs sysfs ronly
|
||||
procd_add_jail_mount /etc/simple-captive-portal/
|
||||
procd_add_jail_mount /usr/lib/lua/luci/ip.so
|
||||
procd_add_jail_mount /usr/share/simple-captive-portal/portal.lua
|
||||
procd_add_jail_mount /usr/lib/uhttpd_lua.so
|
||||
procd_add_jail_mount /usr/sbin/nft
|
||||
procd_add_jail_mount /bin/sh
|
||||
procd_set_param capabilities /usr/share/simple-captive-portal/capabilities.json
|
||||
procd_set_param user nobody
|
||||
procd_set_param no_new_privs
|
||||
procd_set_param respawn
|
||||
procd_set_param stdout 1
|
||||
procd_set_param stderr 1
|
||||
procd_close_instance
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Simple captive portal</title>
|
||||
</head>
|
||||
<body style="text-align:center">
|
||||
<a href="/connect">
|
||||
<h1>Free Wifi</h1>
|
||||
<p>Click here to connect</p>
|
||||
</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"bounding": [
|
||||
"CAP_NET_ADMIN"
|
||||
],
|
||||
"effective": [
|
||||
"CAP_NET_ADMIN"
|
||||
],
|
||||
"ambient": [
|
||||
"CAP_NET_ADMIN"
|
||||
],
|
||||
"permitted": [
|
||||
"CAP_NET_ADMIN"
|
||||
],
|
||||
"inheritable": [
|
||||
"CAP_NET_ADMIN"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
require "luci.ip"
|
||||
|
||||
function handle_request(env)
|
||||
local mac = nil
|
||||
luci.ip.neighbors({ dest = env.REMOTE_ADDR }, function(n) mac = n.mac end)
|
||||
if mac == nil then
|
||||
uhttpd.send("Status: 500 Internal Server Error\r\n")
|
||||
uhttpd.send("Server: simple-captive-portal\r\n")
|
||||
uhttpd.send("Content-Type: text/plain\r\n\r\n")
|
||||
uhttpd.send("ERROR: MAC not found for IP " .. env.REMOTE_ADDR)
|
||||
return
|
||||
end
|
||||
|
||||
ret = os.execute("nft add element inet simple-captive-portal guest_macs { " .. tostring(mac) .. " }")
|
||||
if ret ~= 0 then
|
||||
uhttpd.send("Status: 500 Internal Server Error\r\n")
|
||||
uhttpd.send("Server: simple-captive-portal\r\n")
|
||||
uhttpd.send("Content-Type: text/plain\r\n\r\n")
|
||||
uhttpd.send("ERROR: failed to add mac to set\n")
|
||||
return
|
||||
end
|
||||
|
||||
uhttpd.send("Status: 200 OK\r\n")
|
||||
uhttpd.send("Server: simple-captive-portal\r\n")
|
||||
uhttpd.send("Content-Type: text/plain\r\n\r\n")
|
||||
uhttpd.send("You now have internet access\n")
|
||||
end
|
||||
@@ -0,0 +1,13 @@
|
||||
port_portal = os.getenv("PORT_PORTAL")
|
||||
|
||||
function handle_request(env)
|
||||
uhttpd.send("Status: 302 Found\r\n")
|
||||
uhttpd.send("Server: simple-captive-portal\r\n")
|
||||
if string.find(env.SERVER_ADDR, ":") == nil then
|
||||
uhttpd.send("Location: http://" .. env.SERVER_ADDR .. ":" .. port_portal .. "/\r\n")
|
||||
else
|
||||
uhttpd.send("Location: http://[" .. env.SERVER_ADDR .. "]:" .. port_portal .. "/\r\n")
|
||||
end
|
||||
uhttpd.send("Cache-Control: no-cache\r\n")
|
||||
uhttpd.send("Content-Length: 0\r\n\r\n")
|
||||
end
|
||||
Reference in New Issue
Block a user