#!/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. BACKUP_IPV4='/etc/jelastic/iptables4.backup' BACKUP_IPV6='/etc/jelastic/iptables6.backup' CUSTOM_RULES_FILE='/etc/sysconfig/iptables-custom' CUSTOM_6RULES_FILE='/etc/sysconfig/ip6tables-custom' JELASTIC_RULES_DIR='/etc/sysconfig' JELASTIC_4RULES_FILE=${JELASTIC_RULES_DIR}'/iptables4-jelastic' JELASTIC_4NATRULES_FILE=${JELASTIC_RULES_DIR}'/iptables4-jelastic-nat' JELASTIC_6RULES_FILE=${JELASTIC_RULES_DIR}'/iptables6-jelastic' JELASTIC_6NATRULES_FILE=${JELASTIC_RULES_DIR}'/iptables6-jelastic-nat' # $cmd $proto $ptype $dport $srchost $dsthost $chain $tcptype $raction # 1 2 3 4 5 6 7 8 9 function addRule() { chain="${7^^}" proto="${2,,}" cmd="${1,^^}" if [ "x$cmd" != "x" ] ; then cmd="-$cmd" else cmd="-A" fi rulecmd="$cmd $chain" if [ "x$proto" != "x" ] ; then rulecmd="$rulecmd -p $proto" fi srchost=${5/ALL/0.0.0.0\/0} if [ "x$srchost" != "x" ] ; then if grep -q '!' <<< $srchost ; then srchost=${srchost//'!'/} rulecmd="$rulecmd ! -s $srchost" else rulecmd="$rulecmd -s $srchost" fi fi dsthost=${6/ALL/0.0.0.0\/0} if [ "x$dsthost" != "x" ] ; then if grep -q '!' <<< $dsthost ; then dsthost=${dsthost//'!'/} rulecmd="$rulecmd ! -d $dsthost" else rulecmd="$rulecmd -d $dsthost" fi fi ptype="$3" dport="$4" if [ "x$ptype" == "xrange" ] ; then if [ "x$dport" != "x" ] ; then rulecmd="$rulecmd -m multiport --dports $dport" fi else if [ "x$dport" != "x" ] ; then rulecmd="$rulecmd --dport $dport" fi fi raction="$9" rulecmd="$rulecmd -j $raction" if [ "x$proto" == "xall" ] ; then tcprule=$($SED -e 's/all/tcp/g' <<< $rulecmd ) udprule=$($SED -e 's/all/udp/g' <<< $rulecmd ) rulecmd="${tcprule}\n${udprule}" fi tcptype="$8" if [ "x$tcptype" == "xIPV4" ] ; then echo -e $rulecmd >> "$(vzctPath $VZ_CTID_ROOT $JELASTIC_4RULES_FILE)" else echo -e $rulecmd >> "$(vzctPath $VZ_CTID_ROOT $JELASTIC_6RULES_FILE)" fi } #just stub because iptables doesn't need additional init function _doInitTables() { return 0 } function setDefaultPolicy() { local policy=${1:-"DROP"} $IPTABLES -P INPUT ${policy} $IPTABLES -P FORWARD ${policy} $IPTABLES -P OUTPUT ACCEPT $IP6TABLES -P INPUT ${policy} $IP6TABLES -P FORWARD ${policy} $IP6TABLES -P OUTPUT ACCEPT } function netfilterStop() { [[ "${FIREWALL_ENABLED}" -eq "0" ]] || [ -z "${FIREWALL_ENABLED+xxx}" ] || [ -z "${FIREWALL_ENABLED}" -a "${FIREWALL_ENABLED+xxx}" = "xxx" ] && return 0; $IPTABLES -F &>>$JEM_CALLS_LOG $IPTABLES -F -t nat &>>$JEM_CALLS_LOG $IPTABLES -X &>>$JEM_CALLS_LOG $IPTABLES -X -t nat &>>$JEM_CALLS_LOG $IP6TABLES -F &>>$JEM_CALLS_LOG $IP6TABLES -F -t nat &>>$JEM_CALLS_LOG $IP6TABLES -X &>>$JEM_CALLS_LOG $IP6TABLES -X -t nat &>>$JEM_CALLS_LOG setDefaultPolicy "ACCEPT" } function netfilterRedirect() { __PORT=$1 __SPORT=$2 __PROTO=$3 local rules_file=$(vzctPath $VZ_CTID_ROOT $CUSTOM_RULES_FILE) local portExists=$(vzexecRunStdOut "iptables -w -L -n -t nat" | sed -nre "/REDIRECT/{/${__PROTO}/{/dpt:${__SPORT}/{/ports ${__PORT}/p}}}") if [ ! -z "$portExists" ] ; then writeJSONResponseErr "result=>4099" "message=>This pair SPORT:DPORT already exists" return 1 fi if vzexecRun "iptables -t nat -w -I PREROUTING -p $__PROTO -m $__PROTO --dport $__SPORT -j REDIRECT --to-port $__PORT" ; then rule="$(vzexecRunStdOut "iptables -w -t nat -S" | sed -nre "/$__PROTO/{/--dport $__SPORT/{/--to-ports $__PORT/p;}}")" [ ! -z "$rule" ] && rule=$(sed -nre "/$rule/p" <<< "$rule") if [ ! -z "$rule" ] ; then if [ ! -e "$rules_file" ] ; then echo -e "*nat\n$rule\nCOMMIT\n" > "$rules_file" else #insert the rule just after chain definition sed -i -re "/:PREROUTING/a$rule" $rules_file fi fi writeJSONResponseOut "result=>0" "message=>Port has been redirected" "sport=>$__SPORT" "dport=>$__PORT" return 0 fi } function netfilterForward() { __IP=$1 __PORT=$2 __PROTO=$3 __SPORT=$4 if ! isValidIP $__IP ; then writeJSONResponseErr "result=>4083" "message=>Invalid IP specified" exit 1 fi local port=$(iptables -w -L PREROUTING -t nat -vnx | sed -nre "/($__IP:[0-9]{1,5})/{/$__PROTO/{/:$__PORT\s*$/{s/.*dpt:([0-9]{4,5}).*/\1/;p}}}") if [[ ! -z "$port" ]] ; then writeJSONResponseErr "result=>4092" "message=>This pair IP:PORT already bind to port $port" "port=>$port" exit 1 fi local freeport=$(getFreePort $__SPORT) if [[ -z "$freeport" ]] ; then writeJSONResponseErr "result=>4084" "message=>Unable to bind IP. There is no free ports" exit 1 fi if iptables -w -t nat -A PREROUTING -p $__PROTO --dport $freeport -j DNAT --to-destination ${__IP}:${__PORT} ; then service iptables save | $GRAB_OUTPUT; writeJSONResponseOut "result=>0" "message=>Port has been added" "port=>$freeport" return 0 fi } function checkRule() { chainName=$1 rule=$2 $IPTABLES -C $chainName $rule 1>/dev/null 2>&1 } function processRule4File() { [ -f "$1" ] || return 0 echo "CUSTOM RULES FILE $1 CONTENT" >> $JEM_CALLS_LOG cat "$1" >> $JEM_CALLS_LOG $IPTABLES_RESTORE $2 -n < "$1" 2>>$ACTIONS_LOG return $? } function processRule6File() { [ -f "$1" ] || return 0 echo "CUSTOM RULES FILE $1 CONTENT " >> $JEM_CALLS_LOG cat "$1" >> $JEM_CALLS_LOG $IP6TABLES_RESTORE $2 -n < "$1" 2>>$ACTIONS_LOG return $? } function getUsedPorts() { echo $(iptables -w -L PREROUTING -t nat -n | sed -nre '/REDIRECT/d; s/.*dpt:([0-9]{4,5}).*/\1/;/[0-9]+/p' | uniq -u) } function getPortByIP() { : ${1? "Missing IP address" } local _IP=$1 local _port=$2 echo $(iptables -w -L PREROUTING -t nat -n | sed -nre "/:${_IP}:${_port}$/{/$__PROTO/{s/.*dpt:([0-9]+).*/\1/;p}}") } function getDestinationPort() { : ${1?"Missing port param"} local _PORT=$1 echo $(iptables -w -L PREROUTING -t nat -n | $SED -nre "/:${_PORT}\s/{s/.*:([0-9]+)\s*$/\1/;p}") } function netfilterSNAT() { __IP=$1 __NETWORK=$2 __PROTO=$3 if ! isValidIP $__IP ; then writeJSONResponseErr "result=>4083" "message=>Invalid IP specified" ; exit 1; fi # Check if binding already exists {{{ local port=$(iptables -w -L POSTROUTING -t nat -n | grep -E "$__PROTO.*$__NETWORK.*to:$__IP" ) if [[ ! -z $port ]] ; then writeJSONResponseErr "result=>4092" "message=>This pair IP:NETWORK ($__IP:$__PROTO) already bind"; exit 1; fi # }}} if $iptables -w -t nat -A POSTROUTING -p $__PROTO --dst $__NETWORK -j SNAT --to-source $__IP ; then service iptables save &>> $JEM_CALLS_LOG writeJSONResponseOut "result=>0" "message=>SNAT has been added"; return 0 ; fi } function netfilterUnbind() { __IP=$1 __PORT=$2 __PROTO=$3 local _ports=($(getPortByIP ${__IP} $__PORT)) if [[ ! -z ${__PORT+x} ]] ; then #single port #TODO: This comparation is buggy. Need to refactor it in future if [[ -z ${_ports[0]} ]] ; then writeJSONResponseErr "result=>4091" "message=>there is no bindings for $__IP:$__PORT"; exit 1; fi if [[ $NAT_ACTION == "dnat" ]] ; then $EXEC "$IPTABLES -w -t nat -D PREROUTING -p $__PROTO --dport $_ports -j DNAT --to-destination $__IP:$__PORT" 2>/dev/null res=$? else $EXEC "$IPTABLES -w -t nat -D PREROUTING -p $__PROTO --dport $_ports -j REDIRECT --to-port $__PORT" 2>/dev/null res=$? fi if [[ $res -eq 0 ]] ; then service iptables save | $GRAB_OUTPUT; writeJSONResponseOut "result=>0" "message=>Port binding has been removed"; return 0 ; else writeJSONResponseErr "result=>4093" "message=>Port binding has not been removed"; return 1 ; fi else # multiple ports msg="" for port in "${_ports[@]}" ; do destPort=$(getDestinationPort $port) msg="$msg $destPort" $IPTABLES -w -t nat -D PREROUTING -p $__PROTO --dport ${port} -j DNAT --to-destination ${__IP}:${destPort} done service iptables save &>> $JEM_CALLS_LOG writeJSONResponseOut "result=>0" "message=>Port bindings for ${__IP} has been removed (${msg} )"; return 0 ; fi } function netfilterStart() { echo "`date +%D.%k:%M:%S.%N`: $action:$subaction Starting firewall" #making backup of current rules [ ! -d "/etc/jelastic" ] && mkdir -p "/etc/jelastic" 2>>$JEM_CALLS_LOG $IPTABLES_SAVE > $BACKUP_IPV4 2>>$JEM_CALLS_LOG $IP6TABLES_SAVE > $BACKUP_IPV6 2>>$JEM_CALLS_LOG _rule4Files=($CUSTOM_RULES_FILE $JELASTIC_4RULES_FILE $JELASTIC_4NATRULES_FILE) _rule6Files=($CUSTOM_6RULES_FILE $JELASTIC_6RULES_FILE $JELASTIC_6NATRULES_FILE) #test rule files _testRes=0 for j in $(seq 4 2 6) ; do eval "_ruleFiles=(\${_rule${j}Files[*]})" eval "_processRuleFile=processRule${j}File" for i in $(seq 0 $((${#_ruleFiles[*]}-1)) ) ; do $_processRuleFile ${_ruleFiles[$i]} "-t" _res=$? [[ "$_res" -ne 0 ]] && echo "Test contents of ${_ruleFiles[$i]} filed" >>$JEM_CALLS_LOG _testRes=$((_testRes+_res)) done done if [[ $_testRes -gt 0 ]] ; then echo "Current iptables rules not modified due to errors in new rule-files" >>$JEM_CALLS_LOG return 2 fi if [ "x$OS" == "xubuntu" ] ; then #fix ubuntu with broken links [ -e '/etc/iptables/rules.v4' ] && [ -L "$CUSTOM_RULES_FILE" ] && cp -H $CUSTOM_RULES_FILE{,.bkp} 2>> $JEM_CALLS_LOG && rm -f $CUSTOM_RULES_FILE 2>> $JEM_CALLS_LOG [ -e '/etc/iptables/rules.v6' ] && [ -L "$CUSTOM_6RULES_FILE" ] && cp -H $CUSTOM_6RULES_FILE{,.bkp} 2>> $JEM_CALLS_LOG && rm -f $CUSTOM_6RULES_FILE 2>> $JEM_CALLS_LOG fi $IPTABLES -F; $IPTABLES -t nat -F; if [ -f /proc/net/if_inet6 ] ; then $IP6TABLES -F; $IP6TABLES -t nat -F 2>/dev/null; fi _setDefaultPolicy #exports set -f NFSIPS=() [ -e '/etc/exports' ] && NFSIPS=($($SED -re 's/.+(\s|\t)+((\*)|([0-9]+\.){3}[0-9]+(\/[0-9]{0,2})?).*/\2/g' -e '/#/d' '/etc/exports' | sort | uniq | $SED -e '/^$/d' -e ':a' -e 'N' -e '$!ba' -e 's/\n/ /g')) local __allnetwork=0 # for internal services: intips=($(getLANIPs)) for ip in ${intips[@]} ; do for nfsip in ${NFSIPS[@]} ; do if [[ "${#NFSPort}" -gt 0 ]] ; then if [ "$nfsip" == "*" ] ; then $IPTABLES -A INPUT -p tcp -m multiport --dports $NFSPort -d $ip -j ACCEPT $IPTABLES -A INPUT -p udp -m multiport --dports $NFSPort -d $ip -j ACCEPT __allnetwork=1 else if [[ "$__allnetwork" -eq 0 ]] ; then $IPTABLES -A INPUT -p tcp -m multiport --dports $NFSPort -d $ip -s $nfsip -j ACCEPT $IPTABLES -A INPUT -p udp -m multiport --dports $NFSPort -d $ip -s $nfsip -j ACCEPT fi fi fi if [[ "${#RPCPort}" -gt 0 ]] ; then if [ "$nfsip" == "*" ] ; then $IPTABLES -A INPUT -p tcp -m multiport --dports $RPCPort -d $ip -j ACCEPT $IPTABLES -A INPUT -p udp -m multiport --dports $RPCPort -d $ip -j ACCEPT __allnetwork=1 else if [[ "$__allnetwork" -eq 0 ]] ; then $IPTABLES -A INPUT -p tcp -m multiport --dports $RPCPort -d $ip -s $nfsip -j ACCEPT $IPTABLES -A INPUT -p udp -m multiport --dports $RPCPort -d $ip -s $nfsip -j ACCEPT fi fi fi done done; set +f $IPTABLES -A INPUT -i tap -j ACCEPT; redirPorts=($(isFunction _get$($SED -re 's/-/_/g' <<< ${COMPUTE_TYPE})RedirPorts && { _get$($SED -re 's/-/_/g' <<< ${COMPUTE_TYPE})RedirPorts; } || _defaultComputeNodeRedirPorts)); # process input chain NAT for port in ${redirPorts[@]} ; do local ruleParams=($($SED 's/:/ /g' <<< ${port})); $IPTABLES -t nat -A ${ruleParams[0]} -s ${ruleParams[1]} -d ${ruleParams[2]} -m tcp -p tcp --dport ${ruleParams[3]} -j ${ruleParams[5]} --to-port ${ruleParams[4]}; done; [ "x${COMPUTE_TYPE}" == "xcartridge" ] && { [ -f "${CARTRIDGE_HOME}/jelastic/scripts/firewall.sh" ] && source "${CARTRIDGE_HOME}/jelastic/scripts/firewall.sh" >>$JEM_CALLS_LOG 2>&1; } _loadRes=0 for j in $(seq 4 2 6) ; do eval "_ruleFiles=(\${_rule${j}Files[*]})" eval "_processRuleFile=processRule${j}File" for i in $(seq 0 $((${#_ruleFiles[*]}-1)) ) ; do $_processRuleFile ${_ruleFiles[$i]} _res=$? [[ "$_res" -ne 0 ]] && echo "Load contents of ${_ruleFiles[$i]} filed" >>$JEM_CALLS_LOG _loadRes=$((_loadRes+_res)) done done if [ -f /proc/net/if_inet6 ] ; then $IP6TABLES -I INPUT -p icmpv6 -j ACCEPT $IP6TABLES -I INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT $IP6TABLES -I INPUT -i lo -j ACCEPT fi $IPTABLES -I INPUT -p icmp -j ACCEPT $IPTABLES -I INPUT -i lo -j ACCEPT $IPTABLES -I INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT #JE-67929 $IPTABLES -I INPUT -p icmp -m icmp --icmp-type 13 -j DROP #Add reject only if there was no problem while loading rules if [[ "$_loadRes" -gt 0 ]] ; then echo "Not saving rules due to errors above" >>$JEM_CALLS_LOG _setDefaultPolicy "ACCEPT" return 2 fi checkRule "INPUT" "-j REJECT --reject-with icmp-host-prohibited" || $IPTABLES -A INPUT -j REJECT --reject-with icmp-host-prohibited } function netfilterEnable() { [ -d "${JELASTIC_RULES_DIR}" ] || mkdir -p "${JELASTIC_RULES_DIR}" [ ! -e "${CUSTOM_6RULES_FILE}" ] && touch ${CUSTOM_6RULES_FILE} [ -n "$DATA_OWNER" ] && chown "$DATA_OWNER" "${CUSTOM_6RULES_FILE}" &>>"$JEM_CALLS_LOG" # patch iptables-persistent plugins # VZ containers has no modprobe support if [ "x$OS" == "xubuntu" -o "x$OS" == "xdebian" ] ; then [ -d "/usr/share/netfilter-persistent/plugins.d/" ] && { sed -i -re '/\/sbin\/modprobe/s/^/#/g' /usr/share/netfilter-persistent/plugins.d/* >>$ACTIONS_LOG 2>&1 systemctl enable netfilter-persistent >>$ACTIONS_LOG 2>&1 } else chkconfig --level 345 iptables on 2>&1 | $GRAB_OUTPUT chkconfig --level 345 ip6tables on 2>&1 | $GRAB_OUTPUT fi } function saveCurrentRules() { savefile="${1:-""}" if [ ! -z "$savefile" ] ; then [ ! -d "/etc/jelastic" ] && mkdir -p "/etc/jelastic" 2>>$JEM_CALLS_LOG $IPTABLES_SAVE > $BACKUP_IPV4 2>>$JEM_CALLS_LOG $IP6TABLES_SAVE > $BACKUP_IPV6 2>>$JEM_CALLS_LOG return 0 fi if [ "x$OS" == "xubuntu" -o "x$OS" == "xdebian" ] ; then [ -x /etc/init.d/netfilter-persistent ] && /etc/init.d/netfilter-persistent save &>> $JEM_CALLS_LOG else /sbin/service iptables save &>> $JEM_CALLS_LOG /sbin/service ip6tables save &>> $JEM_CALLS_LOG fi echo "iptables rules saved" >>$JEM_CALLS_LOG } # global variable RULES required see firewall.module function firewallSet() { if [ ${#RULES[@]} -eq 0 ] ; then echo "rules are epmty" return 1 fi [ -d "$(vzctPath $VZ_CTID_ROOT $JELASTIC_RULES_DIR)" ] || mkdir -p "${VZ_CTID_ROOT}${JELASTIC_RULES_DIR}" echo "*filter" > "$(vzctPath $VZ_CTID_ROOT $JELASTIC_4RULES_FILE)" echo "*filter" > "$(vzctPath $VZ_CTID_ROOT $JELASTIC_6RULES_FILE)" for ruleraw in ${RULES[@]} ; do rulevars=$(awk -F, '{print "proto="$1";ptype="$2";dport="$3";srchost="$4";dsthost="$5";chain="$6";raction="$7";tcptype="$8";cmd="$9";"}' <<< $ruleraw ) eval $rulevars addRule "$cmd" "$proto" "$ptype" "$dport" "$srchost" "$dsthost" "$chain" "$tcptype" "$raction" res=$? if [ $res -gt 0 ] ; then echo "error adding rules" return 1 fi done echo "COMMIT" >> "$(vzctPath $VZ_CTID_ROOT $JELASTIC_4RULES_FILE)" echo "COMMIT" >> "$(vzctPath $VZ_CTID_ROOT $JELASTIC_6RULES_FILE)" } function getNumberOfActiveSets() { local jel_sets=($(ipset -L -n 2>>$JEM_CALLS_LOG | sed -rne "/^${_JELASTIC_REGEX}\$/p;" | grep "${HOSTER_DOMAIN}" | sed ':a;N;$!ba;s/\n/ /g')) echo "${#jel_sets[@]}" } function setExists() { ipset -L -n 2>>$JEM_CALLS_LOG | grep -q "$1" } function netfilterList() { echo "IPTABLES rules ($@)" echo "--------------------" $IPTABLES -L -t $@ echo echo "IP6TABLES rules ($@)" echo "--------------------" $IP6TABLES -L -t $@ }