#!/bin/bash
#
#Server-Manager: o_firewall (Manages the firewall port rules) [Power: 700]
#
#Version: 1.5.1
#Date:    2024.10.29
#Unix:    Debian 11
#Author:  P.Klapp
#Company: Omoti UG (haftungsbeschränkt)
#Website: https://omoti.de
#
#Path:    /Commands/


#Version
    version=$(sed -n '5p' ${0})
    version=${version#*\ }

#Temporary file
    tmp="/srv/omoti/tmp/o_firewall.$(date +'%H%M%S')$(pwgen -s -0 -N 1 2).tmp"

#Parameters
    #missing parameter
        if [[ -z ${1} ]]
        then
            echo "Missing parameter! Please use -h for help."
            exit 1
        fi

    while getopts ":a:c:d:hi:p:rs:" option
    do
        case ${option} in
            a)  #add
                task='add'
                port="${OPTARG}"

                if ! [[ "${port}" =~ ^[0-9]+$ ]]
                then
                    echo "Invalid port! Please use -h for help."
                    exit 1
                fi
            ;;

            c)  #comment
                comment="${OPTARG}"
            ;;

            d)  #delete
                task='del'
                port="${OPTARG}"

                if ! [[ "${port}" =~ ^[0-9]+$ ]]
                then
                    echo "Invalid port! Please use -h for help."
                    exit 1
                fi
            ;;

            h)  #help
                echo ''
                echo 'Usage: o_firewall OPTION...'
                echo "o_firewall (v${version}) Manages the firewall port rules"
                echo ''
                echo 'Options:'
                echo '-a <port>                 Adds a port to the firewall'
                echo '-c <comment>              Set a comment to the port'
                echo '-d <port>                 Deletes a port from the firewall'
                echo '-h                        Display this help and exit'
                echo '-i <interface>            Specify the network interface'
                echo '-p <network protocol>     Sets the port to tcp or udp'
                echo '-r                        Writes the current config to the config file'
                echo '-s <0/4/6/b/d/f/g>        Outputs the current rules'
                echo ''
                echo 'Further documentation can be found in the Omoti Wiki'
                echo 'https://gitlab.omoti.work/omoti/Server-Manager/-/wikis/home'
                echo ''
                exit 0
            ;;

            i)  #interface
                interface="${OPTARG}"

                if ! ip link show "${interface}" > /dev/null 2>&1
                then
                    echo "The interface ${interface} does not exist!"
                    exit 1
                fi
            ;;

            p)  #network protocol (udp/tcp)
                protocol="${OPTARG}"

                if [[ ${protocol} != 'tcp' ]] && [[ ${protocol} != 'udp' ]]
                then
                    echo "Invalid option! Please use -h for help."
                    exit 1
                fi

            ;;

            r)  #Writes the current config to the config file
                iptables-save > /etc/iptables/rules.v4
                ip6tables-save > /etc/iptables/rules.v6

                if [[ -f /etc/iptables/rules.v4 ]] && [[ -f /etc/iptables/rules.v6 ]]
                then
                    echo -e "\033[32mConfig saved\033[0m"
                else
                    o_log -c 'Firewall save failed'
                fi
                exit 0
            ;;

            s)  #show firewall
                if [[ ${OPTARG} == '0' ]]
                then
                    iptables -L INPUT -n -v --line-numbers
                    ip6tables -L INPUT -n -v --line-numbers
                    exit 0

                elif [[ ${OPTARG} == '4' ]]
                then
                    iptables -L INPUT -n -v --line-numbers                 
                    exit 0

                elif [[ ${OPTARG} == '6' ]]
                then
                    ip6tables -L INPUT -n -v --line-numbers                  
                    exit 0

                elif [[ ${OPTARG} == 'b' ]]
                then
                    iptables -L BLACKLIST -n -v --line-numbers
                    ip6tables -L BLACKLIST -n -v --line-numbers
                    exit 0

                elif [[ ${OPTARG} == 'd' ]]
                then
                    #DOCKER
                        iptables -L DOCKER -n -v --line-numbers
                        ip6tables -L DOCKER -n -v --line-numbers

                    #DOCKER-ISOLATION-STAGE-1
                        iptables -L DOCKER-ISOLATION-STAGE-1 -n -v --line-numbers
                        ip6tables -L DOCKER-ISOLATION-STAGE-1 -n -v --line-numbers

                    #DOCKER-ISOLATION-STAGE-2
                        iptables -L DOCKER-ISOLATION-STAGE-2 -n -v --line-numbers
                        ip6tables -L DOCKER-ISOLATION-STAGE-2 -n -v --line-numbers

                    #DOCKER-USER
                        iptables -L DOCKER-USER -n -v --line-numbers
                        ip6tables -L DOCKER-USER -n -v --line-numbers

                    exit 0

                elif [[ ${OPTARG} == 'f' ]]
                then
                    iptables -L f2b-sshd -n -v --line-numbers
                    ip6tables -L f2b-sshd -n -v --line-numbers
                    exit 0

                elif [[ ${OPTARG} == 'g' ]]
                then
                    iptables -L GEOIP -n -v --line-numbers
                    #ip6tables -L GEOIP -n -v --line-numbers
                    exit 0

                else
                    echo "Invalid option! Please use -h for help."
                    exit 1 
                fi
            ;;

            \?) #invalid option
                echo "Invalid option! Please use -h for help."
                exit 1
            ;;

            :)  #missing argument
                echo "Missing argument! Please use -h for help."
                exit 1
            ;;
        esac
    done
    unset option

    #comment
        if [[ -z ${comment} ]]
        then
            comment="$(ps -o comm= $PPID)"

            if [[ ${comment} == 'bash' ]]
            then
                comment='User-input'
            fi
        fi

#Functions
#Dependencies
    #user
        if [[ ${USER} != 'root' ]]
        then
            echo
            echo -e "\033[33mThis command requests root rights\033[0m"
            echo 'Please run the command as root or with sudo!'
            echo
            exit 1
        fi

#Variables
    #comment        -> Parameters
    #interface      -> Parameters
    #port           -> Parameters
    #protocol       -> Parameters (tcp/udp)
    #rule           -> Main
    #task           -> Parameters (add/del)
    #tmp            -> Temporary file
    #version        -> Version

#Main
    #controll
        if [[ ${interface} ]]
        then
            rule=$(iptables -L INPUT -n -v | grep "dpt:${port} " | grep " ${interface} ")
        else
            rule=$(iptables -L INPUT -n -v | grep "dpt:${port} ")
        fi

        if [[ ${task} == 'add' ]] && [[ ${rule} ]]
        then
            echo -e "\033[33mA rule already exists for port ${port}!\033[0m"
            echo "${rule}"
            o_log -w "Duplicate rule for port ${port} rejected"
            exit 1

        elif [[ ${task} == 'del' ]] && [[ -z ${rule} ]]
        then
            echo -e "No rule exists for port ${port}"
            exit 0
        fi

    #add rule
        if [[ ${task} == 'add' ]]
        then
            if [[ ${protocol} ]]
            then
                if [[ ${interface} ]]
                then
                    iptables -A INPUT -i "${interface}" -p ${protocol} --dport ${port} -m comment --comment "${comment}" -j ACCEPT
                    ip6tables -A INPUT -i "${interface}" -p ${protocol} --dport ${port} -m comment --comment "${comment}" -j ACCEPT
                else
                    iptables -A INPUT -p ${protocol} --dport ${port} -m comment --comment "${comment}" -j ACCEPT
                    ip6tables -A INPUT -p ${protocol} --dport ${port} -m comment --comment "${comment}" -j ACCEPT
                fi

            else
                if [[ ${interface} ]]
                then
                    iptables -A INPUT -i "${interface}" -p tcp --dport ${port} -m comment --comment "${comment}" -j ACCEPT
                    iptables -A INPUT -i "${interface}" -p udp --dport ${port} -m comment --comment "${comment}" -j ACCEPT
                    ip6tables -A INPUT -i "${interface}" -p tcp --dport ${port} -m comment --comment "${comment}" -j ACCEPT
                    ip6tables -A INPUT -i "${interface}" -p udp --dport ${port} -m comment --comment "${comment}" -j ACCEPT
                else
                    iptables -A INPUT -p tcp --dport ${port} -m comment --comment "${comment}" -j ACCEPT
                    iptables -A INPUT -p udp --dport ${port} -m comment --comment "${comment}" -j ACCEPT
                    ip6tables -A INPUT -p tcp --dport ${port} -m comment --comment "${comment}" -j ACCEPT
                    ip6tables -A INPUT -p udp --dport ${port} -m comment --comment "${comment}" -j ACCEPT
                fi
            fi
        fi

    #del rule
        if [[ ${task} == 'del' ]]
        then
            #ipv4
                while true
                do
                    if [[ ${interface} ]]
                    then
                        rule=$(iptables -L INPUT -n -v --line-numbers | grep "dpt:${port} " | grep " ${interface} ")
                    else
                        rule=$(iptables -L INPUT -n -v --line-numbers | grep "dpt:${port} ")
                    fi
                    rule_num=${rule%%\ *}
                    if [[ ${rule_num} ]]
                    then
                        iptables -D INPUT ${rule_num}
                    else
                        break
                    fi
                done

            #ipv6
                while true
                do
                    if [[ ${interface} ]]
                    then
                        rule=$(ip6tables -L INPUT -n -v --line-numbers | grep "dpt:${port} " | grep " ${interface} ")
                    else
                        rule=$(ip6tables -L INPUT -n -v --line-numbers | grep "dpt:${port} ")
                    fi
                    rule_num=${rule%%\ *}
                    if [[ ${rule_num} ]]
                    then
                        ip6tables -D INPUT ${rule_num}
                    else
                        break
                    fi
                done
        fi

    #controll
        if [[ ${interface} ]]
        then
            rule=$(iptables -L INPUT -n -v | grep "dpt:${port} " | grep " ${interface} ")
        else
            rule=$(iptables -L INPUT -n -v | grep "dpt:${port} ")
        fi

        if [[ ${task} == 'add' ]] && [[ -z ${rule} ]]
        then
            echo -e "\033[33mAdding the rule failed!\033[0m"
            o_log -w "Rule for port ${port} could not be added"
            exit 1

        elif [[ ${task} == 'del' ]] && [[ ${rule} ]]
        then
            echo -e "\033[33mDeleting the rule failed!\033[0m"
            o_log -w "Rule for port ${port} could not be deleted"
            exit 1
        fi

    #save
        iptables-save > /etc/iptables/rules.v4
        ip6tables-save > /etc/iptables/rules.v6

        if [[ -f /etc/iptables/rules.v4 ]] && [[ -f /etc/iptables/rules.v6 ]]
        then
            if [[ ${task} == 'add' ]]
            then
                echo -e "\033[32mRule was successfully entered\033[0m"
                o_log -i "Rule for port ${port} was set"

            elif [[ ${task} == 'del' ]]
            then
                echo -e "\033[32mRule was successfully deleted\033[0m"
                o_log -i "Rule for port ${port} was deleted"
            fi

        else
            o_log -c 'Firewall save failed'
        fi