diff --git a/README.md b/README.md index 1c09453..ec40392 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,10 @@ This is a commonly seen setup in public Wifi networks or hotspots. This app was specifically written for such a hotspot and as such requires a lot of other configuration around it. This is an ongoing [documentation project here](https://wiki.sydit.se/teknik:guider:networking:captive_portal_med_iptables). +## More documentation + +I've moved all examples from the [aforementioned wiki-page](https://wiki.sydit.se/teknik:guider:networking:captive_portal_med_iptables) to the docs/examples directory. + # Plugins Plugins are executed when the user clicks through the captive portal form, whether they submit data or just approve an EULA these plugins are executed. diff --git a/docs/examples/iptables/README.md b/docs/examples/iptables/README.md new file mode 100644 index 0000000..dc1f39f --- /dev/null +++ b/docs/examples/iptables/README.md @@ -0,0 +1,9 @@ +# IPtables example + +The example is written for Ansible so it contains Jinja2 brackets for things like input NIC, output NIC and other items important for a captive portal firewall configuration. + +Server configurations vary so it's not applicable to any situation but it might help as guidance and it's well commented. + +# Script examples + +They're also written as Ansible templates because I copy them straight from my Ansible playbooks for deploying the captive portal. But it matters less for them, no important values to keep track of, everything is argument input. diff --git a/docs/examples/iptables/cp_iptables.sh.j2 b/docs/examples/iptables/cp_iptables.sh.j2 new file mode 100644 index 0000000..558a4d9 --- /dev/null +++ b/docs/examples/iptables/cp_iptables.sh.j2 @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +# Captiveportal iptables wrapper script +#iptables_mac = iptables -t mangle -I internet 1 -m mac --mac-source {mac_address} -j RETURN + +# First argument must be IP-address of client +test -n "$1" || exit 1 + +client_ip="$1" +ipt=/sbin/iptables + +# Enable client traffic in internet chain by jumping over the mark +$ipt -t mangle -I internet 1 -p tcp --source "$client_ip" -j RETURN &>/dev/null && \ + $ipt -t mangle -I internet 1 -p udp --source "$client_ip" -j RETURN &>/dev/null +iptables_rc=$? + +# Delete conntrack info for client IP +/usr/local/sbin/rmtrack.sh "$client_ip" &>/dev/null +rmtrack_rc=$? + +if [[ $iptables_rc == 0 && $rmtrack_rc == 0 ]]; then + # Success + exit 0 +else + echo "Error: iptables[$iptables_rc], rmtrack[$rmtrack_rc]" 1&>2 + exit 1 +fi diff --git a/docs/examples/iptables/iptables.j2 b/docs/examples/iptables/iptables.j2 new file mode 100644 index 0000000..7f55e4c --- /dev/null +++ b/docs/examples/iptables/iptables.j2 @@ -0,0 +1,98 @@ +# {{ ansible_managed }} +# +# These rules are for the Captive Portal project. +# by Stefan Midjich - 2016/03 + +# Routing of traffic requires: sysctl net.ipv4.ip_forward = 1 +# +# {{captiveportal_conf.input_nic}} is LAN and used as default route on LAN clients. +# {{captiveportal_conf.output_nic}} is WAN. +# {{captiveportal_conf.webportal_ip}} is the same as IP on {{captiveportal_conf.input_nic}} + +# Mangle table allows the marking of traffic. If you use -j RETURN before +# -j MARK you jump out of the internet chain and your traffic is not marked. +*mangle +:PREROUTING ACCEPT +:INPUT ACCEPT +:OUTPUT ACCEPT +:POSTROUTING ACCEPT + +# Create custom chain in mangle table called "internet" +:internet - [0:0] + +# Run all traffic from {{captiveportal_conf.input_nic}} through the internet chain +-A PREROUTING -i {{captiveportal_conf.input_nic}} -j internet + +# Example to allow authorized clients in by MAC address +#-A internet -m mac --mac-source "xx:xx:xx:xx:56:eb" -j RETURN +# Live example: -I internet 1 -m mac --mac-source "xx:xx:xx:xx:56:eb" -j RETURN +# inserts at the top of the rules before the mark rule. +# +# iptables -t mangle -I internet -m tcp -p tcp --source 1.2.3.4 -j RETURN +# iptables -t mangle -I internet -m udp -p udp --source 1.2.3.4 -j RETURN + +# For MGMT SSH traffic return out of internet chain so it's not marked +-A internet -p tcp -d {{captiveportal_conf.webportal_ip}} --dport ssh -j RETURN + +# Bypass NTP also +#-A internet -p udp --dport ntp -j RETURN + +# Mark all other traffic in the internet chain with 99. Any traffic after +# this rule is marked and blocked. +-A internet -j MARK --set-mark 99 + +COMMIT + +# NAT rules that redirect traffic and allow the portal server to act as gateway. +*nat +:PREROUTING ACCEPT +:INPUT ACCEPT +:OUTPUT ACCEPT +:POSTROUTING ACCEPT + +# Redirect all marked HTTP traffic to the webportal IP +-A PREROUTING -m mark --mark 99 -p tcp --dport http -j DNAT --to-destination {{captiveportal_conf.webportal_ip}} +-A PREROUTING -m mark --mark 99 -p tcp --dport https -j DNAT --to-destination {{captiveportal_conf.webportal_ip}} + +# Redirect all marked DNS traffic to the webportal IP +-A PREROUTING -m mark --mark 99 -p udp --dport domain -j DNAT --to-destination {{captiveportal_conf.webportal_ip}} +-A PREROUTING -m mark --mark 99 -p tcp --dport domain -j DNAT --to-destination {{captiveportal_conf.webportal_ip}} + +# Redirect all ICMP to the webportal IP +-A PREROUTING -m mark --mark 99 -p icmp -j DNAT --to-destination {{captiveportal_conf.webportal_ip}} + +# Redirect all unmarked DNS traffic to upstream DNS servers +{% for server in captiveportal_conf.upstream_dns %} +-A PREROUTING -p udp --dport domain -j DNAT --to-destination {{server}} +-A PREROUTING -p tcp --dport domain -j DNAT --to-destination {{server}} +{% endfor %} + +# Route any traffic out through the output NIC to act as gateway +-A POSTROUTING -o {{captiveportal_conf.output_nic}} -j MASQUERADE + +COMMIT + +# Filter rules that determine access to the portal server. +*filter +:INPUT ACCEPT +:FORWARD ACCEPT +:OUTPUT ACCEPT + +# Enable stateful connections +-I OUTPUT -o {{captiveportal_conf.output_nic}} -d 0.0.0.0/0 -j ACCEPT +-I INPUT -i {{captiveportal_conf.input_nic}} -m state --state ESTABLISHED,RELATED -j ACCEPT + +# Accept HTTP traffic both to the server and forwarded +-A INPUT -p tcp --dport http -j ACCEPT +-A FORWARD -p tcp --dport http -j ACCEPT + +# Accept DNS traffic to self +-A INPUT -p udp --dport domain -j ACCEPT +-A INPUT -p tcp --dport domain -j ACCEPT + +# Drop all other traffic marked 99 +-A FORWARD -m mark --mark 99 -j DROP +-A INPUT -m mark --mark 99 -j DROP + +COMMIT + diff --git a/docs/examples/iptables/rmtrack.sh.j2 b/docs/examples/iptables/rmtrack.sh.j2 new file mode 100644 index 0000000..8d77a3b --- /dev/null +++ b/docs/examples/iptables/rmtrack.sh.j2 @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +# Conntracking keeps track of active connections so even if a user +# authenticates with a captive portal and new firewall rules are +# created it will take a while before the client takes these new +# routes. So conntrack -D can expedite that process. + +test -n "$1" || exit 1 +client_ip=$1 + +conntrack_cmd=/sbin/conntrack + +# Deletes all conntracking entries for connections originating from +# to webportal server IP so that hopefully new connections can be +# initiated directly to destination. +$conntrack_cmd -D --orig-src $client_ip --orig-dst {{captiveportal_conf.webportal_ip}}