segfault/sfbin/rportfw.sh
SkyperTHC a6cc1186fa
p2
2023-06-23 10:30:41 +01:00

242 lines
6.0 KiB
Bash
Executable File

#! /bin/bash
# CONTEXT: VPN context. Call from portd.sh (sf-portd context)
# Executed by portd.sh inside VPN context.
# Set the FW and routing for reverse ip port forwarding.
source "/sf/bin/funcs.sh"
source "/sf/bin/funcs_redis.sh"
ipbydev()
{
local _ip
_ip="$(ip addr show "${1}")"
_ip="${_ip#*inet }"
_ip="${_ip%%/*}"
[[ -n $_ip ]] && { echo "$_ip"; return; }
echo -e >&2 "IP for dev '${1}' not found. Using ${2:-ERROR}"
echo "${2:?}"
}
# Remove a single iptable line and associated forward rules.
# ["output of iptables -L -n"] as a single string.
fw_del_single()
{
local line
local c_ip
local port
line="$1"
a=($line)
c_ip="${a[7]##*:}"
port="${a[6]##*:}"
iptables -t nat -D PREROUTING -i wg0 -p "${a[5]}" -d "${a[4]}" --dport "${port}" -j DNAT --to-destination "${c_ip}"
iptables -D FORWARD -i wg0 -p "${a[5]}" -d "${c_ip}" --dport "${port}" -j ACCEPT
}
# Delete all Port Forwarding rules matching this R-PORT
# [R-PORT]
fw_del()
{
local port
port="$1"
local line
iptables -t nat -L PREROUTING -n | grep -F "dpt:${port}" | while read line; do
fw_del_single "$line"
done
return
}
# [IP] - String matches such as "10.11." or "10.11.0.8"] are permitted.
fw_del_byip()
{
local match
match="$1"
iptables -t nat -L PREROUTING -n | while read x; do
[[ "${a[4]}" != "${match}"* ]] && continue
del_single "$x"
done
return
}
# Remove the Port Forward & FW rules for a list of ports.
# Called from portd.sh when a container exited (by sf-destructor)
#
# [<IPPORT>...]
cmd_delipports()
{
local ipport
local r_port
local res
local err
[[ "${PROVIDER,,}" != "cryptostorm" ]] && return
DEBUGF "cmd_delipports ${PROVIDER} '${*}'"
err=1
for ipport in "$@"; do
r_port="${ipport##*:}"
res=$(curl -fsSL --retry 3 --max-time 10 http://10.31.33.7/fwd "-ddelfwd=${r_port}" 2>/dev/null) && {
[[ $res == *"has been removed"* ]] && unset err
}
[[ -n $err ]] && {
ERR "${PROVIDER} Failed to remove Port Forward (${ipport})"
echo "------------------------"
echo "${res:0:1024}"
echo "------------------------"
}
fw_del "${r_port}"
done
}
# Add firewall/routing information for this port.
#
# [R-IP] [PORT] [CONTAINER-IP] [LID]
cmd_fwport()
{
local port
local r_ip
local c_ip
local wg_ip
local lid
r_ip="$1"
port="$2"
c_ip="$3"
lid="$4"
[[ -z $c_ip || -z $port ]] && { echo "Bad IP:PORT. ip='${c_ip}' port='$port'"; return 255; }
fw_del "${port}"
wg_ip=$(ipbydev wg0 "")
[[ -z $wg_ip ]] && { echo "Could not retrieve my own wg0 address."; return 255; }
for proto in tcp udp; do
iptables -t nat -A PREROUTING -i wg0 -p ${proto} -d "${wg_ip}" --dport "${port}" -j DNAT --to-destination "${c_ip}" || break
iptables -A FORWARD -i wg0 -p ${proto} -d "${c_ip}" --dport "${port}" -j ACCEPT || break
done
[[ $? -ne 0 ]] && { echo "iptables failed with $?."; return 255; }
LOG "${lid}" "Forwarding ${r_ip}:${port} -> ${c_ip}:${port}"
return 0
}
# Delete stale ports
# This can happen if there was a timeout to reach the VPN provider.
# Check through all ports and compare to assigned ports in ReDis
# Testing: delstale_cs "$(curl -fsSL --retry 3 --max-time 10 http://10.31.33.7/fwd)"
# [res] [port]
# + +---- The last port that got added (and not yet added to portd:ports and thus must be ignored)
# +----------- The result from curl -dport=asdf output
delstale_cs()
{
local res
local IFS_old
local arr
local rarr
local r
local str
local skip_port
res="$1"
skip_port="$2"
IFS_old="$IFS"
IFS=$'\n'
# Assigned to containers
rarr=($(redr SMEMBERS "portd:assigned-CryptoStorm"))
for str in "${rarr[@]}"; do
r+=("${str##* }")
done
# Assigned in pool of available ports
rarr=($(redr SMEMBERS "portd:ports"))
for str in "${rarr[@]}"; do
[[ "${str%% *}" != "CryptoStorm" ]] && continue
r+=("${str##* }")
done
# Check if CS's return of forwarded port are all
# still in our list "r" and delete from CS if they are not.
arr=($res)
for str in "${arr[@]}"; do
[[ "$str" != *" ->"* ]] && continue
ipport="${str%% *}"
[[ ${r[*]} == *"$ipport"* ]] && continue
port=${ipport##*:}
[[ "$port" == "$skip_port" ]] && continue
WARN "${PROVIDER}: Removing STALE ${ipport}"
[[ "$port" =~ [^0-9] ]] && continue
curl -fsSL --max-time 5 http://10.31.33.7/fwd -ddelfwd="${port}" >/dev/null
done
IFS="$IFS_old"
}
# Try to request [NUMBER] more ports from the provider.
# Return ="[PROVIDER] ip:ports"= (with quotes) to STDOUT and 0 if
# any port was successfully requested.
#
# Return 255 if this provider should never be tried again.
# Return 0 on success.
#
# [NUMBER]
cmd_moreports()
{
local members
local members_num
local req_num
local err
err=200
req_num="$1"
[[ "${PROVIDER,,}" != "cryptostorm" ]] && return 255
local i
i=0
members_num=0
# Try 5x the number requested in case we accidentally request a port
# that was already requested (by us or somebody else).
while [[ $i -lt $((req_num * 5)) ]]; do
port=$((30000 + RANDOM % 35534))
res=$(curl -fsSL --retry 3 --max-time 10 http://10.31.33.7/fwd -dport="$port") || break
# Check and delete and stale ports
[[ $i -eq 0 ]] && delstale_cs "$res" "$port" >/dev/null
((i++))
# You already have 100 forwards. The max is 100. Please delete some of the existing ones first.
[[ "$res" == *"You already have "* ]] && { ERR "${PROVIDER} Out of ports!!!"; err=255; break; } # Max Port Forward reached.
[[ "$res" != *"is now forwarding"* ]] && { WARN "${PROVIDER} Failed to get port=${port}."; continue; } # Failed. Try again.
res="${res%% is now forwarding*}"
ip="${res##* }"
# Must sanitize
[[ "$ip" =~ [^0-9.] ]] && break
members+="${PROVIDER} ${ip}:${port}"$'\n'
((members_num++))
[[ $members_num -ge $req_num ]] && break
done
# Could be a temporary failure of curl (200) or fatal (255, out of ports)
[[ $members_num -le 0 ]] && return "$err"
echo "${members[*]}"
return 0
}
cmd="$1"
shift 1
[[ "$cmd" == fwport ]] && { cmd_fwport "$@"; exit; }
[[ "$cmd" == moreports ]] && { cmd_moreports "$@"; exit; }
[[ "$cmd" == delipports ]] && { cmd_delipports "$@"; exit; } # [<IPPORT> ...]
[[ "$cmd" == fw_delall ]] && { fw_del_byip "10.11."; exit; }