Create "Captive Portal/NAC" iptables rules

skydiablo

New Member
Joined
Jul 15, 2021
Messages
1
Reaction score
0
Credits
19
hello!

I am trying to build a NAC system with Captive Portal. My plan is very simple, I have my NAC system as gateway and a webserver as captive portal (will be in the first step on the same machine).
the access should be controlled by a `iptables ipset module list`. the NAC has two interfaces, one to the internet and one to the clients. every client that is stored in the ipset list should get access to the internet.
here is my current approach:


Bash:
# highly inspired by https://www.debuntu.org/iptables-how-to-share-your-internet-connection-page-2/

# install dependencies
apt ipset -y

# Where to find ipset binary
IPS="/sbin/ipset";

# create ipset hash-table:
$IPS create allow-mac hash:mac counters -exist
# add test client
$IPS add allow-mac 11:22:33:44:55:66 -exist


# Where to find iptables binary
IPT="/usr/sbin/iptables"

# The network interface you will use
# WAN is the one connected to the internet
# LAN the one connected to your local network
WAN="eth0"
LAN="eth1"
# First we need to clear up any existing firewall rules
# and chain which might have been created
$IPT -F
$IPT -F INPUT
$IPT -F OUTPUT
$IPT -F FORWARD
$IPT -F -t mangle
$IPT -F -t nat
$IPT -X

# Default policies: Drop any incoming packets
# accept the rest.
$IPT -P INPUT DROP
$IPT -P OUTPUT ACCEPT
$IPT -P FORWARD ACCEPT

# To be able to forward traffic from your LAN
# to the Internet, we need to tell the kernel
# to allow ip forwarding
echo 1 > /proc/sys/net/ipv4/ip_forward

# Masquerading will make machines from the LAN
# look like if they were the router
$IPT -t nat -A POSTROUTING -o $WAN -j MASQUERADE

# forward only packets that allowed via IPSET module
$IPT -I FORWARD -i $LAN -m set ! --match-set allow-mac src -j DROP

# Do not allow new or invalid connections to reach your internal network
$IPT -A FORWARD -i $WAN -m state --state NEW,INVALID -j DROP

# Accept any connections from the local machine
$IPT -A INPUT -i lo -j ACCEPT

# Here we define a new chain which is going to handle
# packets we don't want to respond to
# limit the amount of logs to 10/min
$IPT -N Firewall
$IPT -A Firewall -m limit --limit 10/minute -j LOG --log-prefix "Firewall: "
$IPT -A Firewall -j DROP

# log those packets and inform the sender that the packet was rejected
$IPT -N Rejectwall
$IPT -A Rejectwall -m limit --limit 10/minute -j LOG --log-prefix "Rejectwall: "
$IPT -A Rejectwall -j REJECT

# here we create a chain to deal with unlegitimate packets
# and limit the number of alerts to 10/min
# packets will be drop without informing the sender
$IPT -N Badflags
$IPT -A Badflags -m limit --limit 10/minute -j LOG --log-prefix "Badflags: "
$IPT -A Badflags -j DROP

# A list of well known combination of Bad TCP flags
# we redirect those to the Badflags chain
# which is going to handle them (log and drop)
$IPT -A INPUT -p tcp --tcp-flags ACK,FIN FIN -j Badflags
$IPT -A INPUT -p tcp --tcp-flags ACK,PSH PSH -j Badflags
$IPT -A INPUT -p tcp --tcp-flags ACK,URG URG -j Badflags
$IPT -A INPUT -p tcp --tcp-flags FIN,RST FIN,RST -j Badflags
$IPT -A INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -j Badflags
$IPT -A INPUT -p tcp --tcp-flags SYN,RST SYN,RST -j Badflags
$IPT -A INPUT -p tcp --tcp-flags ALL ALL -j Badflags
$IPT -A INPUT -p tcp --tcp-flags ALL NONE -j Badflags
$IPT -A INPUT -p tcp --tcp-flags ALL FIN,PSH,URG -j Badflags
$IPT -A INPUT -p tcp --tcp-flags ALL SYN,FIN,PSH,URG -j Badflags
$IPT -A INPUT -p tcp --tcp-flags ALL SYN,RST,ACK,FIN,URG -j Badflags

# Accept certain icmp message, drop the others
# and log them through the Firewall chain
# 0 => echo reply
$IPT -A INPUT -p icmp --icmp-type 0 -j ACCEPT
# 3 => Destination Unreachable
$IPT -A INPUT -p icmp --icmp-type 3 -j ACCEPT
# 11 => Time Exceeded
$IPT -A INPUT -p icmp --icmp-type 11 -j ACCEPT
# 8 => Echo
# avoid ping flood
$IPT -A INPUT -p icmp --icmp-type 8 -m limit --limit 1/second -j ACCEPT
$IPT -A INPUT -p icmp -j Firewall

# Accept ssh connections from the Internet
$IPT -A INPUT -i $WAN -p tcp --dport 22 -j ACCEPT

# Accept related and established connections
$IPT -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

# Drop netbios from the outside, no log, just drop
$IPT -A INPUT -p udp --sport 137 --dport 137 -j DROP

# Finally, anything which was not allowed yet
# is going to go through our Rejectwall rule
$IPT -A INPUT -j Rejectwall

as already mentioned, the captive portal should run on the same machine, so i release the necessary ports once:

Bash:
# allow local webserver
$IPT -I INPUT -i $LAN -p tcp --dport 80 -j ACCEPT
$IPT -I INPUT -i $LAN -p tcp --dport 443 -j ACCEPT

now all traffic that tries to leave the net on port 80 and 443 should be redirected to this local webserver:

Bash:
$IPT -t nat -A PREROUTING -i $LAN -p tcp --dport 80 -j DNAT --to-destination 127.0.0.1:80 -m set ! --match-set allow-mac src
$IPT -t nat -A PREROUTING -i $LAN -p tcp --dport 443 -j DNAT --to-destination 127.0.0.1:443 -m set ! --match-set allow-mac src

and from here on it starts to cause problems, because 1. now the webserver is not reachable anymore and 2. the actual goal is not reached either.

does anyone have a tip for me? otherwise the complete approach is to be questioned and optimizations are always welcome.

other topics like DNS and DHCP are currently not yet in focus, but will of course come but one after the other ;)

regards, volker.
 


I am struggling with the same issue.
Were you able to find the solution to this?
 

Members online


Top