mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
Signed-off-by: Chip Childers <chip.childers@gmail.com> I've assumed that Gavin's commit is appropriate, based on an assumption that we will keep these files in the source tree. If https://issues.apache.org/jira/browse/LEGAL-146 results in a different opionion from the members, then we will end up having to do something more drastic anyway.
358 lines
12 KiB
Bash
Executable File
358 lines
12 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Licensed to the Apache Software Foundation (ASF) under one
|
|
# or more contributor license agreements. See the NOTICE file
|
|
# distributed with this work for additional information
|
|
# regarding copyright ownership. The ASF licenses this file
|
|
# to you 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.
|
|
# $Id: firewall.sh 9947 2010-06-25 19:34:24Z manuel $ $HeadURL: svn://svn.lab.vmops.com/repos/vmdev/java/patches/xenserver/root/firewall.sh $
|
|
# firewall.sh -- allow some ports / protocols to vm instances
|
|
# @VERSION@
|
|
|
|
source /root/func.sh
|
|
|
|
lock="biglock"
|
|
locked=$(getLockFile $lock)
|
|
if [ "$locked" != "1" ]
|
|
then
|
|
exit 1
|
|
fi
|
|
|
|
vpnoutmark="0x525"
|
|
|
|
usage() {
|
|
printf "Usage: %s: (-A|-D) -r <target-instance-ip> -P protocol (-p port_range | -t icmp_type_code) -l <public ip address> -d <target port> -s <source cidrs> [-G] \n" $(basename $0) >&2
|
|
}
|
|
|
|
#set -x
|
|
|
|
get_dev_list() {
|
|
ip link show | grep -e eth[2-9] | awk -F ":" '{print $2}'
|
|
ip link show | grep -e eth1[0-9] | awk -F ":" '{print $2}'
|
|
}
|
|
|
|
ip_to_dev() {
|
|
local ip=$1
|
|
|
|
for dev in $DEV_LIST; do
|
|
ip addr show dev $dev | grep inet | grep $ip &>> /dev/null
|
|
[ $? -eq 0 ] && echo $dev && return 0
|
|
done
|
|
return 1
|
|
}
|
|
|
|
doHairpinNat () {
|
|
local vrGuestIPNetwork=$(sudo ip addr show dev eth0 | grep inet | grep eth0 | awk '{print $2}' | head -1)
|
|
local vrGuestIP=$(echo $vrGuestIPNetwork | awk -F'/' '{print $1}')
|
|
|
|
local publicIp=$1
|
|
local prot=$2
|
|
local port=$3
|
|
local guestVmIp=$4
|
|
local guestPort=$(echo $5 | sed 's/:/-/')
|
|
local op=$6
|
|
logger -t cloud "$(basename $0): create HairPin entry : public ip=$publicIp \
|
|
instance ip=$guestVmIp proto=$proto portRange=$guestPort op=$op"
|
|
|
|
if [ "$prot" == "all" ]
|
|
then
|
|
logger -t cloud "creating hairpin nat rules for static nat"
|
|
(sudo iptables -t nat $op PREROUTING -d $publicIp -i eth0 -j DNAT --to-destination $guestVmIp &>> $OUTFILE || [ "$op" == "-D" ]) &&
|
|
(sudo iptables -t nat $op POSTROUTING -s $vrGuestIPNetwork -d $guestVmIp -j SNAT -o eth0 --to-source $vrGuestIP &>> $OUTFILE || [ "$op" == "-D" ])
|
|
else
|
|
(sudo iptables -t nat $op PREROUTING -d $publicIp -i eth0 -p $prot --dport $port -j DNAT --to-destination $guestVmIp:$guestPort &>> $OUTFILE || [ "$op" == "-D" ]) &&
|
|
(sudo iptables -t nat $op POSTROUTING -s $vrGuestIPNetwork -p $prot --dport $port -d $guestVmIp -j SNAT -o eth0 --to-source $vrGuestIP &>> $OUTFILE || [ "$op" == "-D" ])
|
|
fi
|
|
}
|
|
|
|
#Port (address translation) forwarding for tcp or udp
|
|
tcp_or_udp_entry() {
|
|
local instIp=$1
|
|
local dport0=$2
|
|
local dport=$(echo $2 | sed 's/:/-/')
|
|
local publicIp=$3
|
|
local port=$4
|
|
local op=$5
|
|
local proto=$6
|
|
local cidrs=$7
|
|
|
|
logger -t cloud "$(basename $0): creating port fwd entry for PAT: public ip=$publicIp \
|
|
instance ip=$instIp proto=$proto port=$port dport=$dport op=$op"
|
|
|
|
#if adding, this might be a duplicate, so delete the old one first
|
|
[ "$op" == "-A" ] && tcp_or_udp_entry $instIp $dport0 $publicIp $port "-D" $proto $cidrs
|
|
# the delete operation may have errored out but the only possible reason is
|
|
# that the rules didn't exist in the first place
|
|
local dev=$(ip_to_dev $publicIp)
|
|
local tableNo=$(echo $dev | awk -F'eth' '{print $2}')
|
|
# shortcircuit the process if error and it is an append operation
|
|
# continue if it is delete
|
|
(sudo iptables -t nat $op PREROUTING --proto $proto -i $dev -d $publicIp \
|
|
--destination-port $port -j DNAT \
|
|
--to-destination $instIp:$dport &>> $OUTFILE || [ "$op" == "-D" ]) &&
|
|
(sudo iptables -t mangle $op PREROUTING --proto $proto -i $dev -d $publicIp \
|
|
--destination-port $port -j MARK --set-mark $tableNo &>> $OUTFILE || [ "$op" == "-D" ]) &&
|
|
(sudo iptables -t mangle $op PREROUTING --proto $proto -i $dev -d $publicIp \
|
|
--destination-port $port -m state --state NEW -j CONNMARK --save-mark &>> $OUTFILE || [ "$op" == "-D" ]) &&
|
|
(doHairpinNat $publicIp $proto $port $instIp $dport0 $op) &&
|
|
(sudo iptables -t nat $op OUTPUT --proto $proto -d $publicIp \
|
|
--destination-port $port -j DNAT \
|
|
--to-destination $instIp:$dport &>> $OUTFILE || [ "$op" == "-D" ]) &&
|
|
(sudo iptables $op FORWARD -p $proto -s $cidrs -d $instIp -m state \
|
|
--state ESTABLISHED,RELATED -m comment --comment "$publicIp:$port" -j ACCEPT &>> $OUTFILE || [ "$op" == "-D" ]) &&
|
|
(sudo iptables $op FORWARD -p $proto -s $cidrs -d $instIp \
|
|
--destination-port $dport0 -m state --state NEW -m comment --comment "$publicIp:$port" -j ACCEPT &>> $OUTFILE)
|
|
|
|
|
|
local result=$?
|
|
logger -t cloud "$(basename $0): done port fwd entry for PAT: public ip=$publicIp op=$op result=$result"
|
|
return $result
|
|
}
|
|
|
|
|
|
#Forward icmp
|
|
icmp_entry() {
|
|
local instIp=$1
|
|
local icmptype=$2
|
|
local publicIp=$3
|
|
local op=$4
|
|
|
|
logger -t cloud "$(basename $0): creating port fwd entry for PAT: public ip=$publicIp \
|
|
instance ip=$instIp proto=icmp port=$port dport=$dport op=$op"
|
|
#if adding, this might be a duplicate, so delete the old one first
|
|
[ "$op" == "-A" ] && icmp_entry $instIp $icmpType $publicIp "-D"
|
|
# the delete operation may have errored out but the only possible reason is
|
|
# that the rules didn't exist in the first place
|
|
local dev=$(ip_to_dev $publicIp)
|
|
sudo iptables -t nat $op PREROUTING --proto icmp -i $dev -d $publicIp --icmp-type $icmptype -j DNAT --to-destination $instIp &>> $OUTFILE
|
|
|
|
sudo iptables -t nat $op OUTPUT --proto icmp -d $publicIp --icmp-type $icmptype -j DNAT --to-destination $instIp &>> $OUTFILE
|
|
sudo iptables $op FORWARD -p icmp -s 0/0 -d $instIp --icmp-type $icmptype -j ACCEPT &>> $OUTFILE
|
|
|
|
result=$?
|
|
logger -t cloud "$(basename $0): done port fwd entry for PAT: public ip=$publicIp op=$op result=$result"
|
|
return $result
|
|
}
|
|
|
|
|
|
|
|
one_to_one_fw_entry() {
|
|
local publicIp=$1
|
|
local instIp=$2
|
|
local proto=$3
|
|
local portRange=$4
|
|
local op=$5
|
|
logger -t cloud "$(basename $0): create firewall entry for static nat: public ip=$publicIp \
|
|
instance ip=$instIp proto=$proto portRange=$portRange op=$op"
|
|
|
|
#if adding, this might be a duplicate, so delete the old one first
|
|
[ "$op" == "-A" ] && one_to_one_fw_entry $publicIp $instIp $proto $portRange "-D"
|
|
# the delete operation may have errored out but the only possible reason is
|
|
# that the rules didn't exist in the first place
|
|
|
|
local dev=$(ip_to_dev $publicIp)
|
|
[ $? -ne 0 ] && echo "Could not find device associated with $publicIp" && return 1
|
|
|
|
# shortcircuit the process if error and it is an append operation
|
|
# continue if it is delete
|
|
(sudo iptables -t nat $op PREROUTING -i $dev -d $publicIp --proto $proto \
|
|
--destination-port $portRange -j DNAT \
|
|
--to-destination $instIp &>> $OUTFILE || [ "$op" == "-D" ]) &&
|
|
(doHairpinNat $publicIp $proto $portRange $instIp $portRange $op) &&
|
|
(sudo iptables $op FORWARD -i $dev -o eth0 -d $instIp --proto $proto \
|
|
--destination-port $portRange -m state \
|
|
--state NEW -j ACCEPT &>> $OUTFILE )
|
|
|
|
result=$?
|
|
logger -t cloud "$(basename $0): done firewall entry public ip=$publicIp op=$op result=$result"
|
|
return $result
|
|
}
|
|
|
|
fw_chain_for_ip() {
|
|
local pubIp=$1
|
|
if iptables -t mangle -N FIREWALL_$pubIp &> /dev/null
|
|
then
|
|
logger -t cloud "$(basename $0): created a firewall chain for $pubIp"
|
|
(sudo iptables -t mangle -A FIREWALL_$pubIp -j DROP) &&
|
|
(sudo iptables -t mangle -I FIREWALL_$pubIp -m state --state RELATED,ESTABLISHED -j ACCEPT ) &&
|
|
(sudo iptables -t mangle -I PREROUTING 2 -d $pubIp -j FIREWALL_$pubIp)
|
|
return $?
|
|
fi
|
|
logger -t cloud "fw chain for $pubIp already exists"
|
|
return 0
|
|
}
|
|
|
|
static_nat() {
|
|
local publicIp=$1
|
|
local instIp=$2
|
|
local op=$3
|
|
local op2="-D"
|
|
local rulenum=
|
|
local proto="all"
|
|
|
|
logger -t cloud "$(basename $0): static nat: public ip=$publicIp \
|
|
instance ip=$instIp op=$op"
|
|
|
|
#TODO check error below
|
|
fw_chain_for_ip $publicIp
|
|
|
|
#if adding, this might be a duplicate, so delete the old one first
|
|
[ "$op" == "-A" ] && static_nat $publicIp $instIp "-D"
|
|
# the delete operation may have errored out but the only possible reason is
|
|
# that the rules didn't exist in the first place
|
|
[ "$op" == "-A" ] && op2="-I"
|
|
if [ "$op" == "-A" ]
|
|
then
|
|
# put static nat rule one rule after VPN no-NAT rule
|
|
# rule chain can be used to improve it later
|
|
iptables-save -t nat|grep "POSTROUTING" | grep $vpnoutmark > /dev/null
|
|
if [ $? -eq 0 ]
|
|
then
|
|
rulenum=2
|
|
else
|
|
rulenum=1
|
|
fi
|
|
fi
|
|
|
|
local dev=$(ip_to_dev $publicIp)
|
|
[ $? -ne 0 ] && echo "Could not find device associated with $publicIp" && return 1
|
|
local tableNo=$(echo $dev | awk -F'eth' '{print $2}')
|
|
|
|
# shortcircuit the process if error and it is an append operation
|
|
# continue if it is delete
|
|
(sudo iptables -t mangle $op PREROUTING -i $dev -d $publicIp \
|
|
-j MARK -m state --state NEW --set-mark $tableNo &>> $OUTFILE || [ "$op" == "-D" ]) &&
|
|
(sudo iptables -t mangle $op PREROUTING -i $dev -d $publicIp \
|
|
-m state --state NEW -j CONNMARK --save-mark &>> $OUTFILE || [ "$op" == "-D" ]) &&
|
|
(sudo iptables -t mangle $op PREROUTING -s $instIp -i eth0 \
|
|
-j MARK -m state --state NEW --set-mark $tableNo &>> $OUTFILE || [ "$op" == "-D" ]) &&
|
|
(sudo iptables -t mangle $op PREROUTING -s $instIp -i eth0 \
|
|
-m state --state NEW -j CONNMARK --save-mark &>> $OUTFILE || [ "$op" == "-D" ]) &&
|
|
(sudo iptables -t nat $op PREROUTING -i $dev -d $publicIp -j DNAT \
|
|
--to-destination $instIp &>> $OUTFILE || [ "$op" == "-D" ]) &&
|
|
(sudo iptables $op FORWARD -i $dev -o eth0 -d $instIp -m state \
|
|
--state NEW -j ACCEPT &>> $OUTFILE || [ "$op" == "-D" ]) &&
|
|
(sudo iptables -t nat $op2 POSTROUTING $rulenum -s $instIp -j SNAT \
|
|
-o $dev --to-source $publicIp &>> $OUTFILE || [ "$op" == "-D" ]) &&
|
|
(doHairpinNat $publicIp $proto "all" $instIp "0:65535" $op)
|
|
|
|
result=$?
|
|
logger -t cloud "$(basename $0): done static nat entry public ip=$publicIp op=$op result=$result"
|
|
return $result
|
|
}
|
|
|
|
|
|
|
|
rflag=
|
|
Pflag=
|
|
pflag=
|
|
tflag=
|
|
lflag=
|
|
dflag=
|
|
sflag=
|
|
Gflag=
|
|
op=""
|
|
|
|
while getopts 'ADr:P:p:t:l:d:s:G' OPTION
|
|
do
|
|
case $OPTION in
|
|
A) op="-A"
|
|
;;
|
|
D) op="-D"
|
|
;;
|
|
r) rflag=1
|
|
instanceIp="$OPTARG"
|
|
;;
|
|
P) Pflag=1
|
|
protocol="$OPTARG"
|
|
;;
|
|
p) pflag=1
|
|
ports="$OPTARG"
|
|
;;
|
|
t) tflag=1
|
|
icmptype="$OPTARG"
|
|
;;
|
|
l) lflag=1
|
|
publicIp="$OPTARG"
|
|
;;
|
|
s) sflag=1
|
|
cidrs="$OPTARG"
|
|
;;
|
|
d) dflag=1
|
|
dport="$OPTARG"
|
|
;;
|
|
G) Gflag=1
|
|
;;
|
|
?) usage
|
|
unlock_exit 2 $lock $locked
|
|
;;
|
|
esac
|
|
done
|
|
|
|
DEV_LIST=$(get_dev_list)
|
|
OUTFILE=$(mktemp)
|
|
|
|
#Firewall ports for one-to-one/static NAT
|
|
if [ "$Gflag" == "1" ]
|
|
then
|
|
if [ "$protocol" == "" ]
|
|
then
|
|
static_nat $publicIp $instanceIp $op
|
|
else
|
|
one_to_one_fw_entry $publicIp $instanceIp $protocol $dport $op
|
|
fi
|
|
result=$?
|
|
if [ "$result" -ne 0 ] && [ "$op" != "-D" ]; then
|
|
cat $OUTFILE >&2
|
|
fi
|
|
rm -f $OUTFILE
|
|
if [ "$op" == "-D" ];then
|
|
result=0
|
|
fi
|
|
unlock_exit $result $lock $locked
|
|
fi
|
|
|
|
if [ "$sflag" != "1" ]
|
|
then
|
|
cidrs="0/0"
|
|
fi
|
|
|
|
case $protocol in
|
|
tcp|udp)
|
|
tcp_or_udp_entry $instanceIp $dport $publicIp $ports $op $protocol $cidrs
|
|
result=$?
|
|
if [ "$result" -ne 0 ] && [ "$op" != "-D" ];then
|
|
cat $OUTFILE >&2
|
|
fi
|
|
rm -f $OUTFILE
|
|
if [ "$op" == "-D" ];then
|
|
result=0
|
|
fi
|
|
unlock_exit $result $lock $locked
|
|
;;
|
|
"icmp")
|
|
|
|
icmp_entry $instanceIp $icmptype $publicIp $op
|
|
if [ "$op" == "-D" ];then
|
|
result=0
|
|
fi
|
|
unlock_exit $? $lock $locked
|
|
;;
|
|
*)
|
|
printf "Invalid protocol-- must be tcp, udp or icmp\n" >&2
|
|
unlock_exit 5 $lock $locked
|
|
;;
|
|
esac
|
|
|
|
unlock_exit 0 $lock $locked
|