def setupCaptiveNat(self): # TODO captive_port = 9000 self.forwarding_was_enabled = False # Check if forwarding is enabled to restore it after program end with open("/proc/sys/net/ipv4/ip_forward", 'r') as f: self.forwarding_was_enabled = (f.read(1) == '1') self.initNftables() # Devices are classified into 3 sets: # - cp_auth_ok: devices which have passed the captive portal auth # - cp_whitelisted: devices manually set as "pass" from the gui (or "capture") # - cp_blacklisted: devices manually set as "block" from the gui # NOTE: Could use ether_addr sets with "ether saddr" match but the captive_portal # does not know MAC addresses nft.run( "add rule nat nw_prerouting iif %s tcp dport { 80 } ip saddr %s ip saddr != @cp_auth_ok ether saddr != @cp_whitelisted ether saddr != @cp_blacklisted counter dnat %s:%d" % (self.options["interface"], self.lan_network, self.iface_ip, captive_port)) # Masquerade outgoing traffic nft.run( "add rule nat nw_postrouting oif %s ip saddr %s counter masquerade" % (self.options["interface"], self.lan_network)) # Only allow DNS traffic to pass (otherwise captive portal detection on the device won't work) nft.run( "add rule filter nw_forward ether saddr @cp_blacklisted counter drop" ) nft.run( "add rule filter nw_forward iif %s ip saddr %s udp dport { 53 } counter accept" % (self.options["interface"], self.lan_network)) nft.run( "add rule filter nw_forward iif %s ct state new ip saddr %s ip saddr != @cp_auth_ok ether saddr != @cp_whitelisted counter drop" % (self.options["interface"], self.lan_network)) if not self.forwarding_was_enabled: self.setForwarding(True)
def POST_LoginOk(self): username = request.form.get("username") password = request.form.get("password") success = False if username and password: log.info("Login: ip='%s' username='******' password='******'" % (request.remote_addr, username, password)) # TODO success = True self.cp_eventsqueue[0].send(("auth_ok", request.remote_addr)) if success: # Need to add the expection immediately, before redirecting the device # TODO: IP should be harvested when DHCP requests are seen, otherwill # the IP will be allowed forever nft.run("add element ip nat cp_auth_ok { %s }" % (request.remote_addr, )) nft.run("add element ip filter cp_auth_ok { %s }" % (request.remote_addr, )) return (self.GET_LoginOk()) else: return redirect(self.get_login_url(url), code=303)
def termNftables(self): # NOTE: don't delete tables as rules from other programs may be present nft.run("delete chain ip nat nw_prerouting") nft.run("delete chain ip nat nw_postrouting") nft.run("delete chain ip filter nw_forward") nft.run("delete set ip nat cp_auth_ok") nft.run("delete set ip nat cp_whitelisted") nft.run("delete set ip nat cp_blacklisted") nft.run("delete set ip filter cp_auth_ok") nft.run("delete set ip filter cp_whitelisted") nft.run("delete set ip filter cp_blacklisted")
def initNftables(self): nft.run("add table ip nat") nft.run("add table ip filter") # Chains are marked with the "nw_" prefix to identify them nft.run( "add chain ip nat nw_prerouting { type nat hook prerouting priority -100; }" ) nft.run( "add chain ip nat nw_postrouting { type nat hook postrouting priority -100; }" ) nft.run( "add chain ip filter nw_forward { type filter hook forward priority 0; }" ) nft.run("add set ip nat cp_auth_ok { type ipv4_addr;}") nft.run("add set ip nat cp_whitelisted { type ether_addr;}") nft.run("add set ip nat cp_blacklisted { type ether_addr;}") nft.run("add set ip filter cp_auth_ok { type ipv4_addr;}") nft.run("add set ip filter cp_whitelisted { type ether_addr;}") nft.run("add set ip filter cp_blacklisted { type ether_addr;}")
def reloadExceptions(self): if self.passive_mode: return devices = config.getConfiguredDevices() now = time.time() nft.run("flush set ip filter cp_whitelisted") nft.run("flush set ip filter cp_blacklisted") nft.run("flush set ip nat cp_whitelisted") nft.run("flush set ip nat cp_blacklisted") for mac, mac_info in devices.items(): policy = mac_info.get("policy", "default") rearp_mac = False spoof_mac = False if ((policy == "pass") or (policy == "capture")): nft.run("add element ip nat cp_whitelisted { %s }" % (mac, )) nft.run("add element ip filter cp_whitelisted { %s }" % (mac, )) if (policy == "pass"): rearp_mac = True elif policy == "block": nft.run("add element ip nat cp_blacklisted { %s }" % (mac, )) nft.run("add element ip filter cp_blacklisted { %s }" % (mac, )) spoof_mac = True elif policy == "default": applied_policy = getDevicePolicy(mac) if applied_policy == "pass": rearp_mac = True if rearp_mac: spoofed_mac = self.macs_to_spoof.pop(mac, None) if spoofed_mac: # The MAC was spoofed, rearp it pkt_reader.arp_rearp(self.handle, mac, spoofed_mac["ip"]) elif spoof_mac and (not self.macs_to_spoof.get(mac)): # Try to find the MAC IP to start blocking it found_ip = None for ip, m in self.ip_to_mac.items(): if m == mac: found_ip = ip break if found_ip: self.macs_to_spoof[mac] = { "last_seen": now, "ip": found_ip }