commit 81e837729cb35739792dd009eddbd2ac90aa7e96 Author: teldra Date: Sun Nov 24 11:32:15 2019 +0100 firewall.sh: initial commit diff --git a/firewall.sh b/firewall.sh new file mode 100755 index 0000000..5841e58 --- /dev/null +++ b/firewall.sh @@ -0,0 +1,428 @@ +#!/bin/bash +#config +alldevs=(enp0s25 wlp2s0 vpn0 vpn-gateway) + +hometcp=(ssh vncd misc) +homeudp=(avahi-daemon) +outtcp=(ssh) +outudp=() + +declare -A ports=( + [ssh]=22 + [smtp]=25 + [dns]=53 + [netbios-ssn]=139 + [dovecot]=143 + [microsoft-ds]=445 + [cups-ipp]=631 + [imaps]=993 + [kdeconnect]=1714-1764 + [avahi-daemon]=5353 + [vncd]=5900 + [mpd]=6600 + [mpd-audio]=8000 + [misc]=8080 + [syncthing]=8384 + [wireguard]=51820 + [mosh]=60000-61000 +) + +nft="/usr/sbin/nft" + +##script + +#source data at the end of file +source <(sed -n '/^#functions/,$p' $(dirname "$0")/$(basename "$0")) + +#for index in "${!ports[@]}"; do +# echo "$index"; +# echo "${ports[$index]}" +#done + +while getopts Aia:r:l:fDhnL option +do + case "${option}" in + A) AUTOMATIC=1;; + i) INIT=1;; + a) ADEVICE+=("${OPTARG}");; + r) RDEVICE+=("${OPTARG}");; + l) LOCATION="${OPTARG}";; + f) FLUSH=1;; + D) DEBUG=1;; + h) HELP=1;; + n) NM=1;; + L) LIST=1;; + *) HELP=1;; + esac +done + +help +flush +check_deviceinput +get_devices +location +init +add_device +remove_device +set_rules +[[ $LIST == "1" ]] && echo " " && sudo nft list ruleset -a +exit 0 + + + + +#functions +debug() { + if [[ $DEBUG == "1" ]]; then + echo "$@" + fi +} + +die() { + debug "$@" + exit 1 +} + +flush() { + [[ ! $FLUSH == "1" ]] && return 0 + debug "Flush ruleset" + sudo "${nft}" flush ruleset + if [[ $FLUSH == "1" ]]; then + exit + fi +} + +location() { + [[ $INIT == "1" ]] && return 0 + if [[ -z $LOCATION ]]; then + if [[ -e /run/home ]]; then + HOME=$(cat /run/home) + if [[ "${HOME}" == "home" ]]; then + LOCATION=home + elif [[ "${HOME}" == "out" ]]; then + LOCATION=out + else + LOCATION=out + fi + fi + fi + case $LOCATION in + h|home) + portstcp=("${hometcp[@]}") + portsudp=("${homeudp[@]}");; + o|out) + portstcp=("${outtcp[@]}") + portsudp=("${outudp[@]}") ;; + esac + debug location: "${LOCATION[@]}" + debug tcp ports: "${portstcp[@]}" + debug udp ports: "${portsudp[@]}" +} + +check_deviceinput() { + [[ $INIT == "1" ]] && return 0 + MISSING=1 + if [[ -n "$AUTOMATIC" ]]; then + MISSING=0 + elif [[ -n "${ADEVICE[*]}" ]]; then + MISSING=0 + elif [[ -n "${RDEVICE[*]}" ]]; then + MISSING=0 + fi + if [[ $MISSING == "1" ]]; then + die "Whether automatic (-A) nor any device to add or remove (-a/-r) given." + fi + + if [[ -n "$AUTOMATIC" ]] && [[ -n "${ADEVICE[*]}" ]]; then + MISSING=1 + elif [[ -n "$AUTOMATIC" ]] && [[ -n "${RDEVICE[*]}" ]]; then + MISSING=1 + fi + if [[ $MISSING == "1" ]]; then + die "Automatic (-A) and (-a/-r) can not be used together" + fi + + + if [[ -n "${ADEVICE[*]}" ]] && [[ -n "${RDEVICE[*]}" ]]; then + for i in "${ADEVICE[@]}"; do + for z in "${RDEVICE[@]}"; do + if [[ "$z" == "$i" ]]; then + MISSING=1 + REASON+=("$i") + fi + done + done + fi + if [[ $MISSING == "1" ]]; then + die "Device can not be removed and added:" "${REASON[*]}" + fi + + for i in "${ADEVICE[@]}" "${RDEVICE[@]}"; do + FOUND=$(grep "$i" <<<"${alldevs[*]}") + if [ ! "${FOUND}" != "" ]; then + MISSING=1 + REASON+=("$i") + fi + done + if [[ $MISSING == "1" ]]; then + die "Device not found in cfg:" "${REASON[*]}" + fi +} + +get_devices() { + [[ $INIT == "1" ]] && return 0 + if [[ -n $AUTOMATIC ]]; then + if [[ $NM == "1" ]]; then + for i in $(LANG=en_US.UTF-8 nmcli device status|grep " connected"|awk '{print $1}'); do + DEVON+=("$i") + done + else + for i in $(ip -oneline addr show scope global|awk '{print $2}'|uniq); do + DEVON+=("$i") + done + fi + DEVOFF=() + for item1 in "${alldevs[@]}"; do + for item2 in "${DEVON[@]}"; do + if [[ $item1 == "$item2" ]]; then + continue 2 + fi + done + DEVOFF+=( "$item1" ) + done + fi + + if [[ -z $AUTOMATIC ]]; then + for i in "${ADEVICE[@]}"; do + DEVON+=("$i") + done + for i in "${RDEVICE[@]}"; do + DEVOFF+=("$i") + done + fi + + debug alldevices: "${alldevs[*]}" + debug devices on: "${DEVON[*]}" + debug devices off: "${DEVOFF[*]}" +} + +function init() { + if ! $nft list ruleset -a |grep -q "table inet filter"; then + debug "Initialise rule: nft add table inet filter" + $nft add table inet filter + $nft add chain inet filter INPUT \{ type filter hook input priority 0 \; policy drop \; \} + $nft add rule inet filter INPUT ct state invalid drop comment \"early drop of invalid packets\" + $nft add rule inet filter INPUT ct state \{established, related\} accept comment \"accept all connections related to connections made by us\" + $nft add rule inet filter INPUT iif lo accept comment \"accept loopback\" + $nft add rule inet filter INPUT iif != lo ip daddr 127.0.0.1/8 drop comment \"drop connections to loopback not coming from loopback ipv4\" + $nft add rule inet filter INPUT iif != lo ip6 daddr ::1/128 drop comment \"drop connections to loopback not coming from loopback ipv6\" + $nft add chain inet filter FORWARD \{ type filter hook forward priority 0\; policy drop \; \} + $nft add chain inet filter OUTPUT \{ type filter hook output priority 0\; policy accept \; \} + fi + [[ $INIT == "1" ]] && exit +} + +add_device() { + if [[ -n "${DEVON[*]}" ]]; then + for _device in "${DEVON[@]}" "${VPNON[@]}"; do + if ! $nft list table inet filter -a|grep -q "INPUT_${_device}"; then + debug "Add device: ${_device}" + $nft add chain inet filter INPUT_"${_device}" + fi + if ! $nft list ruleset -a |grep -q "jump INPUT_${_device}"; then + $nft add rule inet filter INPUT iif "${_device}" jump INPUT_"${_device}" + fi + if ! $nft list ruleset -a |grep -i "${_device}"| grep -q "${_device}: accept icmpv4 types"; then + $nft add rule inet filter INPUT_"${_device}" ip protocol icmp accept comment \""${_device}": accept icmpv4 types\" + fi + if ! $nft list ruleset -a |grep -i "${_device}"| grep -q "${_device}: accept icmpv6 types"; then + $nft add rule inet filter INPUT_"${_device}" ip6 nexthdr icmpv6 accept comment \""${_device}": accept icmpv6 types\" + fi + if [[ $_device == *"vpn"* ]]; then + if ! $nft list ruleset -a |grep -q "$_device: accept all"; then + debug "$_device: add rule \"$_device: accept all\"" + $nft add rule inet filter INPUT_"${_device}" accept comment \""$_device": accept all\" + fi + fi + done + fi +} + +remove_device() { + if [[ -n "${DEVOFF[*]}" ]]; then + for _device in "${DEVOFF[@]}"; do + if $nft list table inet filter -a|grep -q "INPUT_${_device}"; then + debug "Delete device: ${_device}" + $nft flush chain inet filter INPUT_"${_device}" + HANDLE=$($nft list table inet filter -a|grep "jump INPUT_${_device}"|awk '{print $NF}') + $nft delete rule inet filter INPUT handle "${HANDLE}" + $nft delete chain inet filter INPUT_"${_device}" + fi + done + fi +} + +getports() { + dev=$1 + istportstcp=() + istportsudp=() + turnontcp=() + turnonudp=() + turnofftcp=() + turnoffudp=() + unchangedtcp=() + unchangedudp=() + for _prot in tcp udp; do + for _port in $($nft list ruleset -a|grep -e dport| grep "${dev}"|grep "${_prot}"|awk '{print $12}'| tr ' ' '\n'); do + if [[ $_prot == "tcp" ]]; then + istportstcp+=("$_port") + elif [[ $_prot == "udp" ]]; then + istportsudp+=("$_port") + fi + done + done + + #TCP + for item1 in "${portstcp[@]}"; do + for item2 in "${istportstcp[@]}"; do + if [[ $item1 == "$item2" ]]; then + unchangedtcp+=("$item1") + break + fi + done + done + turnofftcp=() + for item1 in "${istportstcp[@]}"; do + for item2 in "${portstcp[@]}"; do + [[ $item1 == "$item2" ]] && continue 2 + done + turnofftcp+=("$item1") + done + if [[ ! $2 == "off" ]]; then + turnontcp=() + for item1 in "${portstcp[@]}"; do + for item2 in "${istportstcp[@]}"; do + [[ $item1 == "$item2" ]] && continue 2 + done + turnontcp+=("$item1") + done + fi + + + #UDP + for item1 in "${portsudp[@]}"; do + for item2 in "${istportsudp[@]}"; do + if [[ $item1 == "$item2" ]]; then + unchangedudp+=("$item1") + break + fi + done + done + + turnoffudp=() + for item1 in "${istportsudp[@]}"; do + for item2 in "${portsudp[@]}"; do + [[ $item1 == "$item2" ]] && continue 2 + done + + # If we reached here, nothing matched. + turnoffudp+=("$item1") + done + if [[ ! $2 == "off" ]]; then + turnonudp=() + for item1 in "${portsudp[@]}"; do + for item2 in "${istportsudp[@]}"; do + [[ $item1 == "$item2" ]] && continue 2 + done + + # If we reached here, nothing matched. + turnonudp+=("$item1") + done + fi +} + +function addrule() { + portnumber=$1 + portname=$2 + protocol=$3 + device=$4 + if ! $nft list table inet filter -a |grep -q "INPUT: allow ${portname} ${portnumber} ${protocol} ${device}"; then + debug "$device: add rule for ${portname} (${portnumber}, ${protocol})" + #$nft add rule inet filter INPUT iif "${device}" "${protocol}" dport "${portnumber}" ct state new log accept comment \"INPUT: allow "${portname}" "${portnumber}" "${protocol}" "${device}"\" + $nft add rule inet filter INPUT_"${_device}" "${protocol}" dport "${portnumber}" ct state new log accept comment \"INPUT: allow "${portname}" "${portnumber}" "${protocol}" "${device}"\" + fi +} + +function removerule() { + portnumber=$1 + portname=$2 + protocol=$3 + device=$4 + HANDLER="" + HANDLER=$($nft list table inet filter -a |grep "INPUT: allow ${portname} ${portnumber} ${protocol} ${device}" | awk '{print $NF}') + if [[ $HANDLER ]]; then + debug "$device: remove rule for ${portname} (${portnumber}, ${protocol})" + #$nft delete rule inet filter INPUT handle "${HANDLER}" + $nft delete rule inet filter INPUT_"${device}" handle "${HANDLER}" + fi +} + +set_rules() { + for _device in "${DEVOFF[@]}"; do + if [[ ! ${_device} == *"vpn"* ]]; then + getports "${_device}" off + for _pn in "${turnofftcp[@]}"; do + removerule "${ports[$_pn]}" "${_pn}" tcp "${_device}" + done + + for _pn in "${turnoffudp[@]}"; do + removerule "${ports[$_pn]}" "${_pn}" udp "${_device}" + done + for _pn in "${unchangedtcp[@]}"; do + removerule "${ports[$_pn]}" "${_pn}" tcp "${_device}" + done + + for _pn in "${unchangedudp[@]}"; do + removerule "${ports[$_pn]}" "${_pn}" udp "${_device}" + done + fi + done + + for _device in "${DEVON[@]}"; do + if [[ ! "${_device}" == *"vpn"* ]]; then + getports "${_device}" + for _pn in "${turnofftcp[@]}"; do + removerule "${ports[$_pn]}" "${_pn}" tcp "${_device}" + done + for _pn in "${turnoffudp[@]}"; do + removerule "${ports[$_pn]}" "${_pn}" udp "${_device}" + done + for _pn in "${turnontcp[@]}"; do + addrule "${ports[$_pn]}" "${_pn}" tcp "${_device}" + done + for _pn in "${turnonudp[@]}"; do + addrule "${ports[$_pn]}" "${_pn}" udp "${_device}" + done + fi + done +} + +help() { +[[ ! $HELP == "1" ]] && return 0 +cat <] [-r ] [-l ] + [-f] [-D] [-h] + + -A Automagic find connected devices + -a device to add to firewall (must be configured in cfg) + -D debug + -f flush firewall + -h is help + -i initialise firewall + -l where are we (home|out) + -L list firewall settings after processing + -n use networkmanager to get online devices + -r device to remove from firewall +EOF +exit +}