segfault/master/cgi-bin/rpc

513 lines
13 KiB
Plaintext
Raw Normal View History

2023-01-12 11:36:23 +00:00
#! /bin/bash
# Executed on MASTER
WG_PORT_MIN=32768
WG_PORT_MAX=65535
2023-01-13 18:57:40 +00:00
COLOR="always"
2023-01-12 11:36:23 +00:00
source /sf/bin/funcs.sh
source /sf/bin/funcs_redis.sh
[[ ! -d "/config/db" ]] && ERREXIT 255 "Not found: /config/db"
2023-01-13 18:57:40 +00:00
[[ ! -d "/config/db/wg" ]] && mkdir -p "/config/db/wg"
2023-01-12 11:36:23 +00:00
[[ -z $SF_FQDN ]] && SF_FQDN="SF_FQDN-NOT-SET.hack.segfault.net"
echo -en "Content-Type: text/plain\r\n\r\n"
# BAIL <STDOUT-MSG> <STDERR-MSG> <INFO MSG>
# STDOUT goes to user.
# STDERR is logged.
BAIL()
{
echo -e "$1"
2023-01-13 18:57:40 +00:00
[[ -n $2 ]] && echo -e >&2 "[${CB}${LID:-$REMOTE_ADDR}${CN}] ${CR}$2${CN}$3"
2023-01-12 11:36:23 +00:00
exit 255
}
2023-01-13 18:57:40 +00:00
Sanitize()
{
REQUEST_BODY="${REQUEST_BODY//[^[:alnum:]_+=/&]}"
REQUEST_URI="${REQUEST_URI//[^[:alnum:]_+=\/]}"
[[ "${#REQUEST_BODY}" -gt 512 ]] && BAIL "To long" "To Long" ": REQUEST_BODY(${#REQUEST_BODY})=${REQUEST_BODY:0:32}..."
[[ "${#REQUEST_URI}" -gt 512 ]] && BAIL "To long!" "ATTACK" ": REQUEST_URI(${#REQUEST_URI})=${REQUEST_URI:0:32}..."
}
GetFormVars()
{
local ifs
ifs=$IFS
IFS=\& arr=(${REQUEST_BODY})
local i
local str
while [[ $i -lt ${#arr[@]} ]]; do
str="${arr[$i]}"
((i++))
key=${str%%=*}
[[ ${#key} -le 0 ]] && BAIL "Bad Request" "ERROR: " "Body contains bad variable: '$str'"
val=${str#*=}
[[ ${key} == "nocolor" ]] && unset COLOR
[[ ${key} == "verbose" ]] && IS_VERBOSE=1
[[ ${key} == "port" ]] && { val=$((${val//[^0-9]})); [[ $val -ge 32768 && $val -le 65535 ]] && R_PORT="$val"; }
[[ ${key} == "portsecret" ]] && R_PORTSECRET="${val//[^[:alnum:]]}"
[[ ${key} == "peerprivate" ]] && R_WT_PRIVATE="${val//[^[:alnum:]+\/]}="
[[ ${key} == "private" ]] && R_WG_PRIVATE="${val//[^[:alnum:]+\/]}="
2023-01-15 11:24:52 +00:00
[[ ${key} == "name" ]] && { val="${val//[^[:alnum:]]}"; R_WT_NAME="${val:0:13}"; }
2023-01-13 18:57:40 +00:00
done
IFS=$ifs
}
2023-01-12 11:36:23 +00:00
# Load PID of WireGuard container
load_config()
{
source /dev/shm/config.txt && return
2023-01-13 18:57:40 +00:00
BAIL "${R}ERROR${N}: Not ready. SF is still booting up..." "Failed to load: " "/dev/shm/config.txt"
2023-01-12 11:36:23 +00:00
}
GenSecret()
{
local len
len=16
[[ -n $1 ]] && len=$1
str=$(head -c $((len*2)) </dev/urandom | base64 -w0)
str=${str//[^[:alnum:]]}
str=${str:0:$len}
echo $str
}
2023-01-13 18:57:40 +00:00
cmd_net_list()
{
local fn
local str
local name
local upstr
load_port
echo -en "\
Port : ${Y}$WG_PORT${N}
Portsecret: ${Y}$PORTSECRET${N}
"
ifaces=$(nsenter -t "$PID" -n wg show interfaces)
# List all configured names
echo -e "\
Name | Private (Peer) | Public
---------------+----------------------------------------------+---------------------------------------------"
for fn in "/config/db/db-${LID}/wg/wg-"*; do
[[ ! -f "$fn" ]] && break
str=$(basename "$fn")
name="${str#*-}"
[[ $fn != "/config/db/db-${LID}/wg/wg-${name}" ]] && continue # BAD
load_wt "$name"
load_wg "$name"
str="${name} "
unset upstr
[[ $ifaces == *"wg${name}"* ]] && {
upstr="${G}"
}
echo -e "${upstr}${str:0:14}${N} | $WT_PRIVATE | $WG_PUBLIC"
done
exit
}
cmd_net_init_print_info()
2023-01-12 11:36:23 +00:00
{
echo -en "\
2023-01-13 18:57:40 +00:00
Port : ${Y}$WG_PORT${N}
Portsecret: ${Y}$PORTSECRET${N}
Private : ${Y}$WG_PRIVATE${N}
Name : ${Y}$WT_NAME${N}
Bring up the Interface:
2023-01-15 11:24:52 +00:00
${C}curl rpc/net/up -d name=${WT_NAME}${N}
2023-01-13 18:57:40 +00:00
Show status:
${C}curl rpc/net/show${N}
2023-01-15 11:24:52 +00:00
To connect with ${M}Wiretap${N} use this command on the remote host:
2023-01-13 18:57:40 +00:00
${C}wiretap serve --private ${WT_PRIVATE} \\
--public ${WG_PUBLIC} \\
--endpoint ${SF_FQDN}:${WG_PORT}${N}
2023-01-15 11:24:52 +00:00
To connect with ${M}WireGuard${N} use this configuration on the remote host:${F}
2023-01-13 18:57:40 +00:00
[Interface]
PrivateKey = ${WT_PRIVATE}
Address = 192.168.0.2/32
Address = fd::2/128
[Peer]
PublicKey = ${WG_PUBLIC}
AllowedIPs = 0.0.0.0/0, ::/0
EndPoint = ${SF_FQDN}:${WG_PORT}
PersistentKeepalive = 25${N}
2023-01-12 11:36:23 +00:00
"
}
write_portfile()
{
2023-01-13 18:57:40 +00:00
echo -n "\
WG_PORT=\"${WG_PORT}\"
ASSIGNED_LID=\"${LID}\"
PORTSECRET=\"${PORTSECRET}\"
" >"/config/db/db-${LID}/wg/port" || BAIL "Failed to store WireGuard Port."
2023-01-12 11:36:23 +00:00
2023-01-13 18:57:40 +00:00
echo "WG_PORT=${WG_PORT}" >"/config/db/wg/sec2port-${PORTSECRET}"
# Link to LID:
ln -sf "../db-${LID}/wg/port" "/config/db/wg/port-${WG_PORT}"
2023-01-12 11:36:23 +00:00
}
2023-01-13 18:57:40 +00:00
# [WT_NAME]
write_wgfile()
2023-01-12 11:36:23 +00:00
{
2023-01-13 18:57:40 +00:00
local name
name="$1"
2023-01-12 11:36:23 +00:00
2023-01-13 18:57:40 +00:00
echo -n "\
WG_PRIVATE=\"${WG_PRIVATE}\"
WG_PUBLIC=\"${WG_PUBLIC}\"
WG_PORT=\"${WG_PORT}\"
" >"/config/db/db-${LID}/wg/wg-${name}" || BAIL "Failed to store WG information."
ln -sf "../db-${LID}/wg/wg-${name}" "/config/db/wg/wg-${name}"
2023-01-12 11:36:23 +00:00
}
2023-01-13 18:57:40 +00:00
# [WT_NAME]
write_wtfile()
2023-01-12 11:36:23 +00:00
{
2023-01-13 18:57:40 +00:00
local name
name="$1"
2023-01-12 11:36:23 +00:00
2023-01-13 18:57:40 +00:00
echo -n "\
WT_PRIVATE=\"${WT_PRIVATE}\"
WT_PUBLIC=\"${WT_PUBLIC}\"
WG_PORT=\"${WG_PORT}\"
" >"/config/db/db-${LID}/wg/wt-${name}" || BAIL "Failed to store WT information."
2023-01-12 11:36:23 +00:00
2023-01-13 18:57:40 +00:00
ln -sf "../db-${LID}/wg/wt-${name}" "/config/db/wg/wt-${name}"
}
2023-01-12 11:36:23 +00:00
2023-01-13 18:57:40 +00:00
# Assign port to _this_ LID
# [portsecret]
cmd_net_init_move()
{
local sec
local psec
psec="$1"
2023-01-12 11:36:23 +00:00
2023-01-13 18:57:40 +00:00
source "/config/db/wg/sec2port-${psec}" 2>/dev/null || BAIL "Portsecret ${psec} is not known."
[[ ${ASSIGNED_LID} == $LID ]] && return # Already assigned to this LID
ln -sf "../db-${LID}/wg/port" "/config/db/wg/port-${WG_PORT}"
2023-01-12 11:36:23 +00:00
}
# rpc/net/init
# Assign/Retrieve WireGuard port for this LID
cmd_net_init()
{
local n
2023-01-13 18:57:40 +00:00
local err
local arr
arr=($(echo /config/db/db-${LID}/wg/wt-*))
[[ ${#arr[@]} -gt 10 ]] && BAIL "${R}ERROR${N}: To many Peers. Delete some first." "${R}PEERS-MAX${N} " "Limit: ${#arr[@]}"
[[ -n ${R_PORTSECRET} ]] && cmd_net_init_move "${R_PORTSECRET}"
if source "/config/db/db-${LID}/wg/port" 2>/dev/null; then
# HERE: Port already assigned to this LID,
[[ -n ${R_PORT} ]] && [[ $R_PORT -ne $WG_PORT ]] && echo -e "${Y}WARNING:${N} Ignoring request for Port ${R_PORT}. Port already set to ${WG_PORT}."
else
# HERE: No Port yet assigned to this LID.
# Allow user to pick a port.
if [[ -n ${R_PORT} ]]; then
[[ -e "/config/db/wg/port-${R_PORT}" ]] && BAIL "\
Port ${R_PORT} is already in use. You can assign it to this server like so:\
2023-01-15 11:24:52 +00:00
${C}curl rpc/net/init -d portsecret=<SECRET>${N}"
2023-01-13 18:57:40 +00:00
WG_PORT=${R_PORT}
else
# Assign random port 32768...65535
n=0
while :; do
WG_PORT="$((WG_PORT_MIN + RANDOM % (WG_PORT_MAX - WG_PORT_MIN + 1)))"
[[ ! -e "/config/db/wg/port-${WG_PORT}" ]] && break
((n++))
[[ $n -gt 5 ]] && BAIL "Failed to find free WireGuard Port."
done
fi
fi
2023-01-12 11:36:23 +00:00
2023-01-13 18:57:40 +00:00
PORTSECRET="${WG_PORT}_$(GenSecret)"
write_portfile
[[ -e "/config/db/wg/wt-${R_WT_NAME}" ]] && {
WT_NAME=${R_WT_NAME}
2023-01-15 11:24:52 +00:00
echo -e "${R}ERROR${N}: '$R_WT_NAME' already exists. Delete it first with ${C}curl rpc/net/del -d name=${R_WT_NAME}${N}"
2023-01-13 18:57:40 +00:00
load_wt "${R_WT_NAME}"
load_wg "${R_WT_NAME}"
cmd_net_init_print_info
2023-01-12 11:36:23 +00:00
exit
}
2023-01-13 18:57:40 +00:00
[[ -n $R_WT_PRIVATE ]] && WT_PRIVATE=$R_WT_PRIVATE || WT_PRIVATE=$(wg genkey)
WT_PUBLIC=$(echo "$WT_PRIVATE" | wg pubkey)
2023-01-15 11:24:52 +00:00
[[ -n $R_WT_NAME ]] && WT_NAME="${R_WT_NAME}" || { val="${WT_PUBLIC//[^[:alnum:]]}"; WT_NAME="${val:0:4}"; }
2023-01-13 18:57:40 +00:00
write_wtfile "${WT_NAME}"
2023-01-12 11:36:23 +00:00
WG_PRIVATE=$(wg genkey)
WG_PUBLIC=$(echo "$WG_PRIVATE" | wg pubkey)
2023-01-13 18:57:40 +00:00
write_wgfile "${WT_NAME}"
cmd_net_init_print_info
2023-01-12 11:36:23 +00:00
exit
}
2023-01-13 18:57:40 +00:00
load_port()
2023-01-12 11:36:23 +00:00
{
2023-01-13 18:57:40 +00:00
source "/config/db/db-${LID}/wg/port" 2>/dev/null || BAIL "${R}ERROR${N}: No port found. Use ${C}curl rpc/net/init${N} first."
2023-01-12 11:36:23 +00:00
}
2023-01-13 18:57:40 +00:00
load_wt()
2023-01-12 11:36:23 +00:00
{
2023-01-15 11:24:52 +00:00
source "/config/db/db-${LID}/wg/wt-${1}" || BAIL "Not found: ${1}. Try ${C}curl rpc/net/init -d name=${1}${N}" "ERROR: " "Not found: db-${LID}/wg/wt-${1}"
2023-01-12 11:36:23 +00:00
}
2023-01-13 18:57:40 +00:00
load_wg()
2023-01-12 11:36:23 +00:00
{
2023-01-13 18:57:40 +00:00
source "/config/db/db-${LID}/wg/wg-${1}" || BAIL "Not found." "ERROR: " "Not found: db-${LID}/wg/wg-${1}"
}
2023-01-12 11:36:23 +00:00
2023-01-13 18:57:40 +00:00
xrm()
{
local fn
local err
err=0
for fn in "$@"; do
[[ ! -f "$fn" ]] && { err=255; continue; }
rm -f "$fn"
done
return $err
}
net_down()
{
local name
name="$1"
# Shut down WG interface
if [[ -z $name || $name == "all" ]]; then
nsenter -t "${PID}" -n ip link delete group 31337
else
# Return early if device did not exist.
nsenter -t "${PID}" -n ip link delete "wg${name}" || return
fi
2023-01-12 11:36:23 +00:00
# Restore default routing
2023-01-13 18:57:40 +00:00
echo -e "${Y}WARNING${N}: All traffic exits via the DEFAULT ROUTE now."
2023-01-12 11:36:23 +00:00
nsenter -t "${PID}" -n ip route add default via "${SF_NET_LG_ROUTER_IP}"
2023-01-13 18:57:40 +00:00
}
2023-01-12 11:36:23 +00:00
2023-01-13 18:57:40 +00:00
cmd_net_del()
{
load_port
local fn
2023-01-15 11:24:52 +00:00
[[ -z $R_WT_NAME ]] && BAIL "Use ${C}-d name=<name>${N}"
2023-01-13 18:57:40 +00:00
net_down "$R_WT_NAME"
if [[ "$R_WT_NAME" == "all" ]]; then
# Delete PORT assignment
xrm "/config/db/wg/sec2port-${PORTSECRET}" "/config/db/wg/port-${WG_PORT}" "/config/db/db-${LID}/wg/port"
# Delete all private keys
for fn in "/config/db/db-${LID}/wg/wg-"*; do
[[ ! -f "$fn" ]] && break
str=$(basename "$fn")
name="${str#*-}"
[[ $fn != "/config/db/db-${LID}/wg/wg-${name}" ]] && continue # BAD
# Delete all links
xrm "/config/db/wg/wg-${name}" "/config/db/wg/wt-${name}"
done
rm -rf "/config/db/db-${LID}/wg"
echo -en "All private keys deleted."
exit
fi
2023-01-12 11:36:23 +00:00
2023-01-13 18:57:40 +00:00
[[ ! -f "/config/db/db-${LID}/wg/wg-${R_WT_NAME}" ]] && BAIL "${R}Not found${N}: ${R_WT_NAME}"
xrm "/config/db/wg/wg-${R_WT_NAME}" "/config/db/db-${LID}/wg/wg-${R_WT_NAME}"
xrm "/config/db/wg/wt-${R_WT_NAME}" "/config/db/db-${LID}/wg/wt-${R_WT_NAME}"
echo -en "\
${G}Private key deleted${N} ($R_WT_NAME).
"
2023-01-12 11:36:23 +00:00
exit
}
cmd_net()
{
load_port
cmd_net_print_info
exit
}
cmd_net_show()
{
local str
2023-01-13 18:57:40 +00:00
unset IFS
if [[ -z $COLOR ]]; then
str=$(nsenter -t "${PID}" -n wg show)
else
# Use 'script' to force color output
str=$(script -q -c "nsenter -t \"${PID}\" -n wg show" /dev/null </dev/null)
fi
2023-01-12 11:36:23 +00:00
[[ -n $str ]] && { echo "$str"; exit; }
echo -e "\
No Peer connected. To connect a peer:
2023-01-15 11:24:52 +00:00
${C}curl rpc/net/up -d name=<NAME>${N}"
2023-01-12 11:36:23 +00:00
exit
}
cmd_net_down()
{
local dev
2023-01-13 18:57:40 +00:00
net_down "${R_WT_NAME}"
exit
}
cmd_net_help()
{
echo -en "\
2023-01-15 11:24:52 +00:00
Create new peer : ${C}curl rpc/net/init -d name=<NAME>${N}
Delete peer : ${C}curl rpc/net/del -d name=<NAME>${N}
2023-01-13 18:57:40 +00:00
List peers : ${C}curl rpc/net/list${N}
Show connections: ${C}curl rpc/net/show${N}
Move port to a new server:
2023-01-15 11:24:52 +00:00
${C}curl rpc/net/init -d portsecret=<portsecret>${N}
2023-01-13 18:57:40 +00:00
Connect a peer:
2023-01-15 11:24:52 +00:00
${C}curl rpc/net/up -d name=<NAME>${N}
Disconnect a peer:
${C}curl rpc/net/down -d name=<NAME>${N}
2023-01-13 18:57:40 +00:00
"
2023-01-12 11:36:23 +00:00
exit
}
2023-01-13 18:57:40 +00:00
0<&- # Close STDIN
Sanitize
GetFormVars
[[ -n $COLOR ]] && {
# COLOR is set (to 'always')
Y=$CDY
C=$CDC
R=$CR
G=$CDG
2023-01-15 11:24:52 +00:00
M=$CDM
2023-01-13 18:57:40 +00:00
N=$CN
F=$CF
}
2023-01-12 11:36:23 +00:00
# Split into arguments
_IFS=$IFS
2023-01-13 18:57:40 +00:00
IFS=/ ARGS=(${REQUEST_URI:1}) # Ignore first '/'. Split into arguements.
2023-01-12 11:36:23 +00:00
IFS=$_IFS
[[ "${FCGI_CMD}" == "dmesg" ]] && {
# dmesg --color=always -f kern --level err,warn -e | tail -n100
2023-01-13 18:57:40 +00:00
dmesg --color="${COLOR:-never}" -f kern --level err -e | tail -n20
2023-01-12 11:36:23 +00:00
exit
}
[[ -n $SF_DEBUG ]] && [[ "${FCGI_CMD}" == "env" ]] && { env; exit; }
2023-01-13 18:57:40 +00:00
# /net/init
# INPUT : <port|portsecret> <WT PRIVATE> <Name>
# OUTPUT: WG-Config & WT-Line
#
# /net/up <Name>
# - Do everything fromo INIT.
# -
2023-01-12 11:36:23 +00:00
# /net -> Show port assignment
# /net/init -> Assigned port to this LID or create new port.
# /net/up -> Create WireGuard interface
# /net/show -> Show WireGuard peers
2023-01-13 18:57:40 +00:00
# /net/down
# /net/del
# /net/list
2023-01-12 11:36:23 +00:00
[[ "${FCGI_CMD}" == "net" ]] && {
# Retrieve (LID CID PID)
arr=($(redr GET "ip:${REMOTE_ADDR}")) || BAIL "Bad Value" "Bad Value: " "ret=$?, ${#arr[@]}"
[[ ${#arr[@]} -ne 3 ]] && BAIL "Value != 3" "Value != 3: " "${#arr[@]}"
LID="${arr[0]}"
2023-01-13 18:57:40 +00:00
[[ ! -d "/config/db/db-${LID}/wg" ]] && mkdir "/config/db/db-${LID}/wg"
2023-01-12 11:36:23 +00:00
# CID="${arr[1]}"
PID="${arr[2]}"
2023-01-13 18:57:40 +00:00
DEBUGF "LID=$LID PID=$PID"
2023-01-12 11:36:23 +00:00
# Show current port configuration
[[ ${ARGS[0]} == 'net' && ${ARGS[1]} == 'show' ]] && cmd_net_show
# Initialize or set port
[[ ${ARGS[1]} == 'init' ]] && cmd_net_init
[[ ${ARGS[1]} == 'del' ]] && cmd_net_del
[[ ${ARGS[1]} == 'down' ]] && cmd_net_down
2023-01-13 18:57:40 +00:00
[[ ${ARGS[1]} == 'help' ]] && cmd_net_help
[[ ${ARGS[1]} == 'list' ]] && cmd_net_list
2023-01-12 11:36:23 +00:00
# NOT 'up' -> EXIT
2023-01-13 18:57:40 +00:00
[[ ${ARGS[1]} != 'up' ]] && { echo -e "${R}ERROR${N}: Unknown command."; cmd_net_help; }
2023-01-12 11:36:23 +00:00
2023-01-15 11:24:52 +00:00
[[ -z $R_WT_NAME ]] && BAIL "${R}ERROR:${N} Use ${C}curl rpc/net/up -d name=<Name>${N}."
2023-01-13 18:57:40 +00:00
WT_NAME="$R_WT_NAME"
2023-01-12 11:36:23 +00:00
load_port
2023-01-13 18:57:40 +00:00
load_wt "${WT_NAME}"
load_wg "${WT_NAME}"
2023-01-12 11:36:23 +00:00
2023-01-13 18:57:40 +00:00
dev="wg${WT_NAME}"
2023-01-12 11:36:23 +00:00
load_config
# Delete old interface in either namespace:
nsenter -t "${WG_PID}" -n ip link del "${dev}" 2>/dev/null
nsenter -t "${PID}" -n ip link del "${dev}" 2>/dev/null
err=$(nsenter -t "${WG_PID}" -n ip link add "${dev}" type wireguard 2>&1) || BAIL "Failed: ip link add $dev ($err)." "Failed $dev" ": $err"
2023-01-13 18:57:40 +00:00
nsenter -t "${WG_PID}" -n ip link set "${dev}" group 31337 || BAIL "FAIL"
2023-01-12 11:36:23 +00:00
echo "$WG_PRIVATE" >/dev/shm/private.$$
2023-01-13 18:57:40 +00:00
nsenter -t "${WG_PID}" -n wg set "${dev}" listen-port "${WG_PORT}" private-key "/dev/shm/private.$$" peer "${WT_PUBLIC}" allowed-ips 0.0.0.0/0 || BAIL "Failed: wg set"
2023-01-12 11:36:23 +00:00
rm -f /dev/shm/private.$$
# Move Interface to user's container:
err=$(nsenter -t "${WG_PID}" -n ip link set "${dev}" netns "${PID}" 2>&1) || BAIL "Failed to move $dev." "Failed $dev netns $PID" ": $err"
# Configure interface after moving
nsenter -t "${PID}" -n ip -4 address add 192.168.0.2/32 dev "${dev}"
err=$(nsenter -t "${PID}" -n ip -6 address add fd::2/128 dev "${dev}" 2>&1) || echo >&2 "ip -6: $err"
nsenter -t "${PID}" -n ip link set mtu 1420 up dev "${dev}"
# Add static routes for RPC
# nsenter -t "${PID}" -n ip route add "${RPC_IP}/32" dev eth0 # NOT NEEDED: RPC is on same network
2023-01-13 18:57:40 +00:00
nsenter -t "${PID}" -n ip route add "${SF_DNS}" via "${SF_NET_LG_ROUTER_IP}" 2>/dev/null
2023-01-12 11:36:23 +00:00
nsenter -t "${PID}" -n ip route del default 2>/dev/null
nsenter -t "${PID}" -n ip route add default dev "${dev}"
2023-01-13 18:57:40 +00:00
# nsenter -t "${PID}" -n ip --color=${COLOR:-never} addr show "${dev}"
2023-01-12 11:36:23 +00:00
2023-01-13 18:57:40 +00:00
echo -e "${G}SUCCESS${N}: All traffic is now routed via ${WT_NAME}."
2023-01-12 11:36:23 +00:00
exit
}