From 59abc641d6f5c2af46515bc917fe0474b7b3727f Mon Sep 17 00:00:00 2001 From: Christian Zeitnitz Date: Fri, 16 Jul 2021 21:40:28 +0200 Subject: [PATCH] Implement firewall - settings in iptables_rules.json - creates a script under /tmp/iptables_raspap.sh and executes it - no installer yet - to do: deal with Bridge and VPN settings --- config/iptables_rules.json | 168 +++++++++++++++++++++++++++++++++++ includes/firewall.php | 174 +++++++++++++++++++++++++++++++++++++ installers/raspap.sudoers | 1 + templates/firewall.php | 70 +++++++++++++++ 4 files changed, 413 insertions(+) create mode 100644 config/iptables_rules.json create mode 100644 includes/firewall.php create mode 100644 templates/firewall.php diff --git a/config/iptables_rules.json b/config/iptables_rules.json new file mode 100644 index 00000000..fa23d707 --- /dev/null +++ b/config/iptables_rules.json @@ -0,0 +1,168 @@ +{ + "info": "IPTABLES rules. $...$ expressions will be replaces automatically ($INTERFACE$, $PORT$, $IPADDRESS$)", + "rules_v4_file": "/etc/iptables/rules.v4", + "rules_v6_file": "/etc/iptables/rules.v6", + "order": [ "pre_rules", "restriction_rules", "main_rules", "exception_rules" ], + "pre_rules": [ + { + "name": "firewall policies", + "fw-state": true, + "comment": "Policy rules (firewall)", + "rules": [ + "-P INPUT DROP", + "-P FORWARD ACCEPT", + "-P OUTPUT ACCEPT", + "-t nat -P PREROUTING ACCEPT", + "-t nat -P POSTROUTING ACCEPT", + "-t nat -P INPUT ACCEPT", + "-t nat -P OUTPUT ACCEPT" + ] + }, + { + "name": "policies", + "fw-state": false, + "comment": "Policy rules", + "rules": [ + "-P INPUT ACCEPT", + "-P FORWARD ACCEPT", + "-P OUTPUT ACCEPT", + "-t nat -P PREROUTING ACCEPT", + "-t nat -P POSTROUTING ACCEPT", + "-t nat -P INPUT ACCEPT", + "-t nat -P OUTPUT ACCEPT" + ] + }, + { + "name": "loopback", + "fw-state": true, + "comment": "allow loopback device", + "rules": [ + "-A INPUT -i lo -j ACCEPT", + "-A OUTPUT -o lo -j ACCEPT" + ] + }, + { + "name": "ping", + "fw-state": true, + "comment": "allow ping request and echo", + "rules": [ + "-A INPUT -p icmp --icmp-type 8/0 -j ACCEPT", + "-A INPUT -p icmp --icmp-type 0/0 -j ACCEPT" + ] + }, + { + "name": "ntp", + "fw-state": true, + "comment": "allow ntp request via udp (tcp should work w/o rule)", + "rules": [ + "-A INPUT -p udp --sport 123 -j ACCEPT" + ] + }, + { + "name": "dns", + "fw-state": true, + "comment": "allow dns request via tcp and udp", + "rules": [ + "-A INPUT -p udp -m multiport --sport 53,853 -j ACCEPT", + "-A INPUT -p tcp -m multiport --sport 53,853 -j ACCEPT" + ] + } + ], + "main_rules": [ + { + "name": "accesspoint", + "fw-state": true, + "comment": "Access point interface by default no restrictions", + "dependson": [ + { "var": "ap-device", "type": "string", "replace": "$INTERFACE$" } + ], + "rules": [ + "-A INPUT -i $INTERFACE$ -j ACCEPT", + "-A OUTPUT -o $INTERFACE$ -j ACCEPT" + ] + }, + { + "name": "clients", + "fw-state": true, + "comment": "Rules for client interfaces (includes tun device)", + "rules": [ + "-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT" + ] + }, + { + "name": "openvpn", + "comment": "Rules for tunnel device (tun)", + "dependson": [ + { "var": "openvpn-enable", "type": "bool" }, + { "var": "openvpn-serverip", "type": "string", "replace": "$IPADDRESS$" }, + { "var": "client-device", "type": "string", "replace": "$INTERFACE$" } + ], + "rules": [ + "-A FORWARD -i tun+ -o $INTERFACE$ -m state --state RELATED,ESTABLISHED -j ACCEPT", + "-A FORWARD -i $INTERFACE$ -o tun+ -j ACCEPT", + "-t nat -A POSTROUTING -o tun+ -j MASQUERADE" + ] + } + ], + "exception_rules": [ + { + "name": "ssh", + "fw-state": true, + "comment": "Allow ssh access to RaspAP on port 22", + "dependson": [ + { "var": "ssh-enable", "type": "bool" } + ], + "rules": [ + "-A INPUT -p tcp --dport 22 -j ACCEPT" + ] + }, + { + "name": "http", + "fw-state": true, + "comment": "Allow access to RaspAP GUI (https)", + "dependson": [ + { "var": "http-enable", "type": "bool" } + ], + "rules": [ + "-A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT" + ] + }, + { + "name": "interface", + "fw-state": true, + "comment": "Exclude interface from firewall", + "dependson": [ + { "var": "excl-devices", "type": "list", "replace": "$INTERFACE$" } + ], + "rules": [ + "-A INPUT -i $INTERFACE$ -j ACCEPT", + "-A OUTPUT -o $INTERFACE$ -j ACCEPT" + ] + }, + { + "name": "ipaddress", + "fw-state": true, + "comment": "allow access from/to IP", + "dependson": [ + { "var": "excluded-ips", "type": "list", "replace": "$IPADDRESS$" } + ], + "rules": [ + "-A INPUT -s $IPADDRESS$ -j ACCEPT", + "-A INPUT -d $IPADDRESS$ -j ACCEPT" + ] + } + ], + "restriction_rules": [ + { + "name": "ipaddress", + "fw-state": true, + "dependson": [ + { "var": "restricted-ips", "type": "list", "replace": "$IPADDRESS$" } + ], + "comment": "Block access from IP-address", + "rules": [ + "-A INPUT -s $IPADDRESS$ -j DROP" + ] + } + ] +} diff --git a/includes/firewall.php b/includes/firewall.php new file mode 100644 index 00000000..ceac3748 --- /dev/null +++ b/includes/firewall.php @@ -0,0 +1,174 @@ +"; +// print_r($ipt); + $txt = "#!/bin/bash\n"; + $txt .= "iptables -F\n"; + $txt .= "iptables -X\n"; + $txt .= "iptables -t nat -F\n"; + file_put_contents(RASPAP_IPTABLES_SCRIPT, $txt); + if ( empty($conf) || empty($ipt) ) return false; + $count=0; + foreach ( $ipt["order"] as $idx ) { + if ( isset($ipt[$idx]) ) { +// echo "Handle $idx \n"; + foreach ( $ipt[$idx] as $i => $sect ) { + if ( isRuleEnabled($sect, $conf) ) { +// echo " rule $i name ".$sect["name"]."\n"; + $str_rules= createRuleStr($sect, $conf); + if ( !empty($str_rules) ) { + file_put_contents(RASPAP_IPTABLES_SCRIPT, $str_rules, FILE_APPEND); + ++$count; + } + } + } + } + } +// echo "Firewall ON"; +//echo ""; + if ( $count > 0 ) { + exec("chmod +x ".RASPAP_IPTABLES_SCRIPT); + exec("sudo ".RASPAP_IPTABLES_SCRIPT); +// exec("sudo iptables-save > /etc/iptables/rules.v4"); +// unlink(RASPAP_IPTABLES_SCRIPT); + } + return ($count > 0); +} + +function WriteFirewallConf($conf) { + if ( is_array($conf) ) write_php_ini($conf,RASPAP_FIREWALL_CONF); +} + + +function ReadFirewallConf() { + if ( file_exists(RASPAP_FIREWALL_CONF) ) { + $conf = parse_ini_file(RASPAP_FIREWALL_CONF); + } else { + $conf = array(); + $conf["firewall-enable"] = false; + $conf["openvpn-enable"] = false; + $conf["openvpn-serverip"] = ""; + $conf["wireguard-enable"] = false; + $conf["wireguard-serverip"] = ""; + $conf["ssh-enable"] = false; + $conf["http-enable"] = false; + $conf["excl-devices"] = ""; + $conf["excluded-ips"] = ""; + $conf["ap-device"] = ""; + $conf["client-device"] = ""; + $conf["restricted-ips"] = ""; + } + return $conf; +} + +function DisplayFirewallConfig() +{ + + $status = new StatusMessages(); + + $json = file_get_contents(RASPAP_IPTABLES_CONF); + $ipt_rules = json_decode($json, true); + + getWifiInterface(); + $ap_device = $_SESSION['ap_interface']; + $clients = getClients(); + $fw_conf = ReadFirewallConf(); + $fw_conf["ap-device"] = $ap_device; + $id=findCurrentClientIndex($clients); + if ( $id >= 0 ) $fw_conf["client-device"] = $clients["device"][$id]["name"]; + if (!empty($_POST)) { + $fw_conf["ssh-enable"] = isset($_POST['ssh-enable']); + $fw_conf["http-enable"] = isset($_POST['http-enable']); + $fw_conf["firewall-enable"] = isset($_POST['firewall-enable']) || isset($_POST['apply-firewall']); + if ( isset($_POST['firewall-enable']) ) $status->addMessage(_('Firewall is now enabled'), 'success'); + if ( isset($_POST['apply-firewall']) ) $status->addMessage(_('Firewall settings changed'), 'success'); + if ( isset($_POST['firewall-disable']) ) $status->addMessage(_('Firewall is now disabled'), 'warning'); + if ( isset($_POST['save-firewall']) ) $status->addMessage(_('Firewall settings saved. Firewall is still disabled.'), 'success'); + WriteFirewallConf($fw_conf); + setFirewall(); + } + echo renderTemplate("firewall", compact( + "status", + "ap_device", + "clients", + "fw_conf", + "ipt_rules") + ); +} diff --git a/installers/raspap.sudoers b/installers/raspap.sudoers index 7ce40f28..ae737910 100644 --- a/installers/raspap.sudoers +++ b/installers/raspap.sudoers @@ -62,3 +62,4 @@ www-data ALL=(ALL) NOPASSWD:/bin/cat /etc/wireguard/*.conf www-data ALL=(ALL) NOPASSWD:/bin/cat /etc/wireguard/wg-*.key www-data ALL=(ALL) NOPASSWD:/bin/rm /etc/wireguard/*.conf www-data ALL=(ALL) NOPASSWD:/bin/rm /etc/wireguard/wg-*.key +www-data ALL=(ALL) NOPASSWD:/tmp/iptables_raspap.sh diff --git a/templates/firewall.php b/templates/firewall.php new file mode 100644 index 00000000..39e41b71 --- /dev/null +++ b/templates/firewall.php @@ -0,0 +1,70 @@ +
+
+
+
+
+
+ +
+
+
+
+ showMessages(); ?> +

+ + + + + +
+
+

+
+
+ +
+ +
+
+
+
+ > + +
+
+ > + +
+

+ +

+
+
+ + " name="apply-firewall" /> + " name="firewall-disable" data-toggle="modal" data-target="#firewallModal"/> + + " name="save-firewall" /> + " name="firewall-enable" data-toggle="modal" data-target="#firewallModal"/> + +
+
+ +
+
+
+ + +