#!/bin/bash

# Copyright: 2023 Virtuozzo International GmbH

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

# http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.



DESCRIPTION="Firewall module";
VERSION="1.1";
DEFAULT_ACTION="Usage";

inherit tests os envinfo log net config;
include cartridge-common virtuozzo vzexec;

$PROGRAM 'iptables'
$PROGRAM 'ip6tables'
$PROGRAM 'find';
$PROGRAM 'xargs';
$PROGRAM 'netstat';
$PROGRAM 'rpcinfo'
$PROGRAM 'ip';
$PROGRAM 'sort';
$PROGRAM 'sed';
$PROGRAM 'grep';
$PROGRAM 'awk';
$PROGRAM 'iptables-restore'
$PROGRAM 'iptables-save'
$PROGRAM 'ip6tables-restore'
$PROGRAM 'ip6tables-save'
$PROGRAM 'vzctl'

#posible values: iptables, nftables
BACKEND='iptables'

declare PublicIP;
declare FTPPort;
declare RSLV_ADDR;
declare CORE_ADDR;
declare APP_ADDR;
declare DB_ADDR;
declare -a OPENPORTS;
declare -a CLOSEDPORTS;

declare -r ERR_NO_METADATA=4080;
CTID=0

OS=""
OS_ver=""

function _getbalancerRedirPorts() { return 0; }
function _getapache_phpRedirPorts { return 0; }
function _getnginx_phpRedirPorts { return 0; }
function _getmavenRedirPorts { return 0; }
function _getnginx_rubyRedirPorts { return 0; }

function _getcartridgePorts() {
    #restoreEnvironmentVariables;
    echo $($SED -re 's/"//g'  <<<  $($SED -re 's/Private_Port=//g' <<< $(cat $MANIFEST_EXPORTED | $GREP "Private_Port=" ))| sort -u) ${FTPPort};
    return 0;
}

function _getcartridgeRedirPorts () {
    echo "PREROUTING:0.0.0.0/0:0.0.0.0/0:80:8080:REDIRECT";
    return 0;
}

function _getglassfishRedirPorts () {
    echo "PREROUTING:0.0.0.0/0:0.0.0.0/0:80:28080:REDIRECT PREROUTING:0.0.0.0/0:0.0.0.0/0:443:8743:REDIRECT OUTPUT:0.0.0.0/0:127.0.0.0/8:80:28080:REDIRECT";
    return 0;
}

function _defaultComputeNodeRedirPorts () {
    return 0;
}

function _computeRedirPorts () {
    echo "PREROUTING:0.0.0.0/0:0.0.0.0/0:80:8080:REDIRECT PREROUTING:0.0.0.0/0:0.0.0.0/0:443:8743:REDIRECT OUTPUT:0.0.0.0/0:127.0.0.0/8:80:8080:REDIRECT";
    return 0;
}

function _getjettyRedirPorts () {
    echo "PREROUTING:0.0.0.0/0:0.0.0.0/0:80:8080:REDIRECT PREROUTING:0.0.0.0/0:0.0.0.0/0:443:8743:REDIRECT OUTPUT:0.0.0.0/0:127.0.0.0/8:80:8080:REDIRECT";
    return 0;
}

function _getapache_rubyRedirPorts() { return 0; }
function _getcouchdbRedirPorts() { return 0; }
function _getmysqlRedirPorts() { return 0; }
function _getmariadbRedirPorts() { return 0; }
function _getmongodbRedirPorts() { return 0; }
function _getpostgresRedirPorts() { return 0; }

function _isPublicIPv4() {
    getAddr "venet0" "ipv4"
    [[ "${#ipv4_list[@]}" -ne 0 ]] && {
        for ip in ${ipv4_list[@]} ; do
            [[ "${ip}" == "127.0.0.1" ]] && continue;
            isLANIP "$ip" || return 0;
        done;
    }
    return 1;
}


function __getAllIPs() {
    echo $($IP a l | $SED -rne '/inet/{s/.*inet6.* //;s/\/.*//;s/peer.*//;s/in.*\s+(.*)/\1/; /(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])/p}' | sort -u | $SED -re ':a;N;$!ba;s/\n/ /g;');
}

function getLANIPs() {
    local IPS=$(__getAllIPs);
    local res=();
    for ip in ${IPS[@]} ; do
        [[ "${ip}" == "127.0.0.1" ]] && continue;
        isLANIP "${ip}" && res=(${res} ${ip});
    done;
    echo "${res[@]}";
}

function isRange() {
    : ${1:?"Missing param: Port to test"};
    $GREP -qP "[-|:]" <<< $1;
    return $?;
}

function _setDefaultPolicy() {
    setDefaultPolicy $1
}

function _saveCurrentRules() {
    saveCurrentRules
}

function doSave() {
    _saveCurrentRules 2>>$ACTIONS_LOG && writeJSONResponseOut "result=>0" "message=>Firewall has been saved" || writeJSONResponseErr "result=>4143" "message=>Cannot save configuration files!"
}

function describeSave() {
    echo "save firewall rules";
}

function doUsage() {
    showUsageMessage
}

function onModLoadCallback() {
    log "Preload callback";
    local __closedports __openports

    ## TODO: remove "params" garbarge option
    local temp=`getopt -l ctid:,rules: -- params "$@" 2>/dev/null` oldifs;
    [[ $? != 0 ]] && die -q "Terminating...";
    eval set -- "$temp";

    while true ; do
        case "$1" in
            --ctid)
                shift
                CTID=$1
                vzexecSetCTID $CTID
                shift
                ;;
            --rules)
                shift;
                oldifs=$IFS;
                IFS=';';
                RULES=(${1// /});
                IFS=${oldifs};
                shift;
                ;;
            --)
                shift;
                break;
                ;;
        esac
    done

    local _prefix=""
    if [[ "$CTID" -gt 0 ]] ; then
        VZ_CTID_PRIVATE=$(vzlist -Ho private $CTID )
        VZ_CTID_ROOT=$(vzlist -Ho root $CTID )
        _prefix="/$VZ_CTID_ROOT"
    fi

    local issue_file=$_prefix/etc/issue
    local release_files=$_prefix/etc/*-release
    local issue_string=$(set +f; cat $release_files $issue_file 2>/dev/null);
    OS=$(detectOS "$issue_string")
    OS_ver=$(getOSVersion "$issue_string")
    OS_ver=${OS_ver%%.*}

    #do we started "inside"
    if [[ "$CTID" -eq 0 ]] ; then

	if [[ "$UID" == '0' ]]; then
    	    ACTIONS_LOG="/var/log/actions$$.log";
	else
    	    ACTIONS_LOG="/tmp/actions$$.log";
	fi

        _isPublicIPv4 && PublicIP=$__TRUE || publicIP=$__FALSE

        #do not perform any operration if container not started
        if [ -d /run/systemd/system ] ; then
            count=10
            while [[ $count -gt 0 ]] ; do
                out=$(systemctl is-system-running)
                if [[ "x$out" == "xstarting" ]] ; then
                    count=$((count-1))
                    sleep 1
                else
                    count=0
                fi
                echo "`date +%D.%k:%M:%S.%N`:   $action:$subaction - waiting start $count" >> $JEM_CALLS_LOG
            done
        fi

        FTPPort=$($NETSTAT -tnlp | $SED -n -re '/ftp/{s/((\S+)\s+){3}(\S+)\s+((\S+)\s+)+/\3/;s/[^:]+:([0-9]+)/\1/;s/://g;p}');
        NFSPort=$($RPCINFO -p 127.0.0.1 2>/dev/null | $SED -n -re '/nfs/{s/\s+(\S+\s+)+([0-9]+)\s+nfs.*/\2/g;p}' | uniq | $SED -e ':a' -e 'N' -e '$!ba' -e 's/\n/ /g' | $SED -e 's/ /,/g' )
        RPCPort=$($RPCINFO -p 127.0.0.1 2>/dev/null | $SED -n -re '/(portmapper|nlockmgr|mountd)/{s/\s+(\S+\s+)+([0-9]+)\s+.*/\2/g;p}' | uniq | $SED -e ':a' -e 'N' -e '$!ba' -e 's/\n/ /g' | $SED -e 's/ /,/g' )

        if [ "x$OS" == "xcentos" -a "$OS_ver" -ge "7" ] ; then
            IPTABLES="$IPTABLES -w"
            IP6TABLES="$IP6TABLES -w"
            IPTABLES_RESTORE="$IPTABLES_RESTORE -w"
            IP6TABLES_RESTORE="$IP6TABLES_RESTORE -w"
        fi
    fi

    if [ "x$OS" == "xalmalinux" -a "$OS_ver" -ge "9" ] ; then
        inherit nftables
    else
        inherit iptables
    fi

    return 0
}

function describeStart() {
    echo "start firewall and load rules";
}

function describeStartParameters() {
    echo "--rules string --ctid integer";
}

function describeStartOptions() {
    echo "rules: firewall rules to apply";
    echo "ctid: container id";
}

function doStart() {
    local cartridge_config="/opt/repo/jelastic/jelastic.conf";
    [ -f "$cartridge_config"  ] && Firewall_Enabled=$($GREP Firewall_Enabled "$cartridge_config" | $AWK -F "=" '{ print $2 }')
    [ ! -z "${Firewall_Enabled}" ] && FIREWALL_ENABLED=${Firewall_Enabled};  ### Override for cartridges
    [[ "${FIREWALL_ENABLED}" -eq "0" ]] || [ -z "${FIREWALL_ENABLED+xxx}" ] || [ -z "${FIREWALL_ENABLED}" -a "${FIREWALL_ENABLED+xxx}" = "xxx" ] && return 0;

    netfilterStart
    _saveCurrentRules

    return 0
}

function describeFWStart() {
    echo "Alias for Start";
}

function doFWStart() {
    local resultMsg
    declare -i result
    if [[ $CTID -eq 0 ]] ; then
        log "FWSTART configuring firewall"
        resultMsg=$(doStart); result=$?
    else
        resultMsg=$(vzctl exec2 $CTID "jem firewall start"); result=$?
    fi
    [[ "$result" -eq "0" ]] && writeJSONResponseOut "result=>${result}" "message=>Firewall has been started" || writeJSONResponseErr "result=>${result}" "message=>${resultMsg}"
    return $result
}

function describeStop() {
    echo "clear all rules and stop firewall";
}

function doStop() {
    netfilterStop
}

function describeFWStop() {
    echo "Alias for Stop";
}

function doFWStop() {
    local resultMsg;
    declare -i result;
    resultMsg=$(doStop); result=$?;
    [[ "${result}" ]] && writeJSONResponseOut "result=>${result}" "message=>Firewall has been stopped" || writeJSONResponseErr "result=>${result}" "message=>${resultMsg}";
}

function _isFWEnabled() {
    local status=$($SED -rne '/FIREWALL_ENABLED/{s/FIREWALL_ENABLED=([0-1]){1}/\1/;p}' ${META_FILE})
    local cartridge_config="/opt/repo/jelastic/jelastic.conf"
    if [ -f "$cartridge_config"  ] ; then
	status=$($GREP Firewall_Enabled "$cartridge_config" | $AWK -F "=" '{ print $2 }')
        [ -z "${status}" ] && status="1"
    fi
    [[ "${status}" -eq "1" ]] && return 0
    return 1
}

function doisEnabled() {
    _isFWEnabled
}

function describeFWStatus() {
    echo "displays firewall status";
}

function doFWStatus() {
    local param=${1};

    [[ -z ${param} ]] && {
        _isFWEnabled && writeJSONResponseOut "result=>0" "message=>Firewall enabled" || writeJSONResponseOut "result=>0" "message=>Firewall disabled";
        return 0;
    }

    message=$($IPTABLES -vnL INPUT | $SED -re '/INPUT/d;/target/d;s/\s*((\S+)\s+){8}(\S+)\s+(.*)/\4/g' | $SED -rne '/^\s*$/d;/:/{s/.*dpt(s)?:([0-9]+)(:)?([0-9]+)?.*/\2:\4/;p}' | $SED -re ':a;N;$!ba;s/\n/,/g;s/:,/,/g;s/:$//g');
    message="Open ports: ${message}";
    writeJSONResponseOut "result=>0" "message=>${message}";
    return 0;
}

function describeFWReconfigure() {
    echo "reconfigure firewall";
}

function describeFWReconfigureParameters() {
    describeStartParameters
}

function describeFWReconfigureOptions() {
    describeStartOptions
}


function doFWReconfigure() {
    _isFWEnabled && doStart
}

function describeEnable() {
    echo "enable firewall";
}

function describeEnableParameters() {
   describeStartParameters
}

function describeEnableOptions() {
    describeStartOptions
}

function doEnable() {
    [ ! -e "${META_FILE}" ] && touch ${META_FILE}
    if $GREP -q "FIREWALL_ENABLED" ${META_FILE} ; then
        $SED -i -re "s/FIREWALL_ENABLED=([0-9]+)/FIREWALL_ENABLED=1/g" ${META_FILE}
    else
        echo -e "\nFIREWALL_ENABLED=1" >> ${META_FILE}
        $SED -i -re '/^$/d' ${META_FILE}
    fi

    netfilterEnable

    writeJSONResponseOut "result=>0" "message=>Firewall has been enabled"
    return 0
}

function describeFWEnable() {
    echo "Alias for Enable";
}

function doFWEnable() {
    declare -i result
    if [[ $CTID -eq 0 ]] ; then
        local resultMsg
        resultMsg=$(doEnable); result=$?
        [[ "${result}" ]] && writeJSONResponseOut "result=>${result}" "message=>Firewall has been enabled" || writeJSONResponseErr "result=>${result}" "message=>${resultMsg}"
        return $result
    fi

    resultMsg=$(vzctl exec2 $CTID "jem firewall Enable"); result=$?
    if [[ "${result}" -gt 0 ]] ; then
        writeJSONResponseErr "result=>${result}" "message=>${resultMsg}"
        return 1
    fi

    FWSetMsg=$(dofwset $@)
    eval $($SED -re 's/.*"result":"([0-9]+)".*"message":"([^"]+)".*/fwset_result="\1";fwset_msg="\2"/g' <<< $FWSetMsg)
    [[ "$fwset_result" -gt 0 ]] && writeJSONResponseErr "result=>$fwset_result" "message=>$fwset_msg"
    writeJSONResponseOut "result=>0" "message=>Firewall has been enabled"
    return 0
}

function describeDisable() {
    echo "disable firewall";
}

function doDisable() {
    [ ! -e "${META_FILE}" ] && touch ${META_FILE}
    netfilterStop
    if $GREP -q "FIREWALL_ENABLED" ${META_FILE} ; then
        $SED -i -re "s/FIREWALL_ENABLED=([0-9]+)/FIREWALL_ENABLED=0/g" ${META_FILE};
    fi
    _setDefaultPolicy "ACCEPT"
    _saveCurrentRules
    writeJSONResponseOut "result=>0" "message=>Firewall has been disabled"
    return 0;
}

function describeDisable() {
    echo "Alias for Disable";
}

function doFWDisable() {
    local resultMsg
    declare -i result
    if [[ $CTID -eq 0 ]] ; then
        resultMsg=$(doDisable); result=$?
    else
        resultMsg=$(vzctl exec2 $CTID "jem firewall Disable"); result=$?
    fi

    [[ "${result}" ]] && writeJSONResponseOut "result=>${result}" "message=>Firewall has been disabled" || writeJSONResponseErr "result=>${result}" "message=>${resultMsg}"
}

function describePanic() {
    echo "clear all rules and set default policy to DROP";
}

function doPanic() {
    netfilterStop
    _setDefaultPolicy "DROP"
    _saveCurrentRules
    return 0
}

function doList() {
    netfilterList $@
}

function dofwset() {
    running=1
    if ! isContainerRunning ${CTID} ; then
        running=0
        vzctl mount ${CTID} >> $ACTIONS_LOG 2>&1
    fi
    
    firewallSet

    if [[ $running -eq 1 ]] ; then
        vzexecRun "jem firewall start"
    else
        vzctl umount ${CTID} >> $ACTIONS_LOG 2>&1
    fi
    log="$(vzexecGetLastStdOut) $(vzexecGetLastStdErr)"
    local _errCode=$(vzexecGetLastErrCode)
    if [[ "$_errCode" -gt 0 ]] ; then
        writeJSONResponseOut "result=>4068" "message=>Firewall rules are not set properly" "log=>${log//\"/\\\"}"
    else
        writeJSONResponseOut "result=>0" "message=>Firewall rules set" "log=>${log//\"/\\\"}"
    fi
    return 0
}
