| #!/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. |
| |
| source /root/func.sh |
| |
| vpnconfdir="/etc/ipsec.d" |
| vpnoutmark="0x525" |
| vpninmark="0x524" |
| |
| usage() { |
| printf "Usage: %s: (-A|-D) -l <left-side vpn peer> -n <left-side guest cidr> -g <left-side next hop> -r <right-side vpn peer> -N <right-side private subnets> -e <esp policy> -i <ike policy> -t <ike lifetime> -T <esp lifetime> -s <pre-shared secret> -d <dpd 0 or 1> [ -p <passive or not> -c <check if up on creation> -S <disable vpn ports iptables> -C <ike split connections or not, default not> -v <ike version, default:'ike'> ]\n" $(basename $0) >&2 |
| } |
| |
| #set -x |
| |
| start_ipsec() { |
| systemctl is-active ipsec > /dev/null |
| if [ $? -ne 0 ] |
| then |
| systemctl start ipsec > /dev/null |
| #Wait until ipsec started, 5 seconds at most |
| for i in {1..5} |
| do |
| logger -t cloud "$(basename $0): waiting ipsec start..." |
| systemctl is-active ipsec > /dev/null |
| result=$? |
| if [ $result -eq 0 ] |
| then |
| break |
| fi |
| sleep 1 |
| done |
| fi |
| systemctl is-active ipsec > /dev/null |
| return $? |
| } |
| |
| enable_iptables_subnets() { |
| for net in $rightnets |
| do |
| sudo iptables -I FORWARD -t mangle -s $leftnet -d $net -j MARK --set-mark $vpnoutmark |
| sudo iptables -A OUTPUT -t mangle -s $leftnet -d $net -j MARK --set-mark $vpnoutmark |
| sudo iptables -I FORWARD -t mangle -s $net -d $leftnet -j MARK --set-mark $vpninmark |
| sudo iptables -A INPUT -t mangle -s $net -d $leftnet -j MARK --set-mark $vpninmark |
| done |
| return 0 |
| } |
| |
| # |
| # Add the right side here to close the gap, so we're sure no one else comes in |
| # also double check the default behaviour of ipsec to drop if wrong.... |
| check_and_enable_iptables() { |
| sudo iptables-save | grep "A INPUT -i $outIf -p udp -m udp --dport 500 -j ACCEPT" |
| if [ $? -ne 0 ] |
| then |
| sudo iptables -A INPUT -i $outIf -p udp -m udp --dport 500 $iptables_secure -j ACCEPT |
| sudo iptables -A INPUT -i $outIf -p udp -m udp --dport 4500 $iptables_secure -j ACCEPT |
| sudo iptables -A INPUT -i $outIf -p 50 $iptables_secure -j ACCEPT |
| # Prevent NAT on "marked" VPN traffic, so need to be the first one on POSTROUTING chain |
| sudo iptables -t nat -I POSTROUTING -t nat -o $outIf -m mark --mark $vpnoutmark -j ACCEPT |
| fi |
| return 0 |
| } |
| |
| disable_iptables_subnets() { |
| for net in $rightnets |
| do |
| sudo iptables -D FORWARD -t mangle -s $leftnet -d $net -j MARK --set-mark $vpnoutmark |
| sudo iptables -D OUTPUT -t mangle -s $leftnet -d $net -j MARK --set-mark $vpnoutmark |
| sudo iptables -D FORWARD -t mangle -s $net -d $leftnet -j MARK --set-mark $vpninmark |
| sudo iptables -D INPUT -t mangle -s $net -d $leftnet -j MARK --set-mark $vpninmark |
| done |
| return 0 |
| } |
| |
| check_and_disable_iptables() { |
| find $vpnconfdir -name "ipsec.vpn*.conf" | grep ipsec |
| if [ $? -ne 0 ] |
| then |
| #Nobody else use s2s vpn now, so delete the iptables rules |
| sudo iptables -D INPUT -i $outIf -p udp -m udp --dport 500 $iptables_secure -j ACCEPT |
| sudo iptables -D INPUT -i $outIf -p udp -m udp --dport 4500 $iptables_secure -j ACCEPT |
| sudo iptables -D INPUT -i $outIf -p 50 $iptables_secure -j ACCEPT |
| sudo iptables -t nat -D POSTROUTING -t nat -o $outIf -m mark --mark $vpnoutmark -j ACCEPT |
| fi |
| return 0 |
| } |
| |
| ipsec_tunnel_del() { |
| disable_iptables_subnets |
| sudo ipsec auto --down vpn-$rightpeer |
| sudo ipsec auto --delete vpn-$rightpeer |
| outIp=$leftpeer |
| local vpnconffile=$vpnconfdir/ipsec.vpn-$rightpeer.conf |
| local vpnsecretsfile=$vpnconfdir/ipsec.vpn-$rightpeer.secrets |
| logger -t cloud "$(basename $0): removing configuration for ipsec tunnel to $rightpeer" |
| sudo rm -f $vpnconffile |
| sudo rm -f $vpnsecretsfile |
| sudo ipsec auto --rereadall |
| check_and_disable_iptables |
| return 0 |
| } |
| |
| ipsec_tunnel_add() { |
| #need to unify with remote access VPN |
| start_ipsec |
| |
| if [ $? -ne 0 ] |
| then |
| logger -t cloud "$(basename $0): Failed to start ipsec service!" |
| return 1 |
| fi |
| |
| outIp=$leftpeer |
| sudo mkdir -p $vpnconfdir |
| local vpnconffile=$vpnconfdir/ipsec.vpn-$rightpeer.conf |
| local vpnsecretsfile=$vpnconfdir/ipsec.vpn-$rightpeer.secrets |
| |
| logger -t cloud "$(basename $0): creating configuration for ipsec tunnel: left peer=$leftpeer \ |
| left net=$leftnet left gateway=$leftnexthop right peer=$rightpeer right network=$rightnets phase1 policy=$ikepolicy \ |
| phase2 policy=$esppolicy secret=$secret" |
| |
| [ "$op" == "-A" ] && ipsec_tunnel_del |
| |
| check_and_enable_iptables |
| |
| rsubnets=" rightsubnets={$rightnets}" |
| if [ $splitconnections -eq 1 ] |
| then |
| rsubnetarr=(${rightnets}) |
| rsubnets=" rightsubnet=${rsubnetarr[0]}" |
| fi |
| |
| sudo echo "conn vpn-$rightpeer" > $vpnconffile && |
| sudo echo " left=$leftpeer" >> $vpnconffile && |
| sudo echo " leftsubnet=$leftnet" >> $vpnconffile && |
| sudo echo " right=$rightpeer" >> $vpnconffile && |
| sudo echo $rsubnets >> $vpnconffile && |
| sudo echo " type=tunnel" >> $vpnconffile && |
| sudo echo " authby=secret" >> $vpnconffile && |
| sudo echo " keyexchange=${ikeversion:-ike}" >> $vpnconffile && |
| sudo echo " ike=$ikepolicy" >> $vpnconffile && |
| sudo echo " ikelifetime=${ikelifetime}s" >> $vpnconffile && |
| sudo echo " esp=$esppolicy" >> $vpnconffile && |
| sudo echo " salifetime=${esplifetime}s" >> $vpnconffile && |
| sudo echo " keyingtries=2" >> $vpnconffile && |
| sudo echo " auto=route" >> $vpnconffile && |
| sudo echo "$leftpeer $rightpeer: PSK \"$secret\"" > $vpnsecretsfile && |
| sudo chmod 0400 $vpnsecretsfile |
| |
| if [ $dpd -ne 0 ] |
| then |
| sudo echo " dpddelay=30" >> $vpnconffile && |
| sudo echo " dpdtimeout=120" >> $vpnconffile && |
| sudo echo " dpdaction=restart" >> $vpnconffile |
| fi |
| |
| if [ $splitconnections -eq 1 ] |
| then |
| # Split out all but the first right subnet into their own statements |
| subnetidx=2 |
| for rsubnet in ${rsubnetarr[@]:1}; do |
| sudo echo "" >> $vpnconffile && |
| sudo echo "conn vpn-$rightpeer-$subnetidx" >> $vpnconffile && |
| sudo echo " also=vpn-$rightpeer" >> $vpnconffile && |
| sudo echo " auto=route" >> $vpnconffile && |
| sudo echo " rightsubnet=$rsubnet" >> $vpnconffile |
| ((++subnetidx)) |
| done |
| fi |
| |
| enable_iptables_subnets |
| |
| sudo ipsec auto --rereadall |
| sudo ipsec auto --add vpn-$rightpeer |
| |
| logger -t cloud "$(basename $0): done ipsec tunnel entry for right peer=$rightpeer right networks=$rightnets" |
| |
| result=0 |
| |
| if [ $passive -eq 0 ] |
| then |
| sudo ipsec auto --up vpn-$rightpeer & |
| fi |
| if [ $checkup -eq 1 ] |
| then |
| |
| #5 seconds for checking if it's ready |
| for i in {1..5} |
| do |
| logger -t cloud "$(basename $0): checking connection status..." |
| /opt/cloud/bin/checks2svpn.sh $rightpeer |
| result=$? |
| if [ $result -eq 0 ] |
| then |
| break |
| fi |
| sleep 1 |
| done |
| if [ $result -eq 0 ] |
| then |
| logger -t cloud "$(basename $0): connect to remote successful" |
| else |
| logger -t cloud "$(basename $0): fail to connect to remote, status code: $result" |
| logger -t cloud "$(basename $0): would stop site-to-site VPN connection" |
| ipsec_tunnel_del |
| fi |
| fi |
| return $result |
| } |
| |
| rflag= |
| pflag= |
| eflag= |
| Eflag= |
| lflag= |
| iflag= |
| Iflag= |
| sflag= |
| passive=0 |
| op="" |
| checkup=0 |
| secure=1 |
| ikeversion="ike" |
| splitconnections=0 |
| |
| while getopts 'ACDSpcl:n:g:r:N:e:i:t:T:s:d:v:' OPTION |
| do |
| case $OPTION in |
| A) opflag=1 |
| op="-A" |
| ;; |
| D) opflag=2 |
| op="-D" |
| ;; |
| l) lflag=1 |
| leftpeer="$OPTARG" |
| ;; |
| n) nflag=1 |
| leftnet="$OPTARG" |
| ;; |
| g) gflag=1 |
| leftnexthop="$OPTARG" |
| ;; |
| r) rflag=1 |
| rightpeer="$OPTARG" |
| ;; |
| N) Nflag=1 |
| rightnets="$OPTARG" |
| ;; |
| e) eflag=1 |
| esppolicy="$OPTARG" |
| ;; |
| v) ikeversion="$OPTARG" |
| ;; |
| i) iflag=1 |
| ikepolicy="$OPTARG" |
| ;; |
| t) tflag=1 |
| ikelifetime="$OPTARG" |
| ;; |
| T) Tflag=1 |
| esplifetime="$OPTARG" |
| ;; |
| s) sflag=1 |
| secret="$OPTARG" |
| ;; |
| d) dflag=1 |
| dpd="$OPTARG" |
| ;; |
| p) passive=1 |
| ;; |
| c) checkup=1 |
| ;; |
| S) secure=0 |
| ;; |
| C) splitconnections=1 |
| ;; |
| ?) usage |
| exit 2 |
| ;; |
| esac |
| done |
| |
| logger -t cloud "$(basename $0): parameters $*" |
| if [ $secure -eq 1 ] |
| then |
| iptables_secure=" -s $rightpeer -d $leftpeer " |
| fi |
| |
| # get interface for public ip |
| ip link|grep BROADCAST|grep -v eth0|cut -d ":" -f 2 > /tmp/iflist |
| while read i |
| do |
| ip addr show $i|grep "$leftpeer" |
| if [ $? -eq 0 ] |
| then |
| outIf=$i |
| break |
| fi |
| done < /tmp/iflist |
| |
| rightnets=${rightnets//,/ } |
| |
| ret=0 |
| #Firewall ports for one-to-one/static NAT |
| if [ "$opflag" == "1" ] |
| then |
| ipsec_tunnel_add |
| ret=$? |
| elif [ "$opflag" == "2" ] |
| then |
| ipsec_tunnel_del |
| ret=$? |
| else |
| printf "Invalid action specified, must choose -A or -D to add/del tunnels\n" >&2 |
| exit 5 |
| fi |
| |
| exit $ret |