Files
webgui/etc/rc.d/rc.library.source
2023-12-01 02:38:36 +01:00

246 lines
7.5 KiB
Bash

#!/bin/bash
#
# script: rc.library.source
#
# Library used by nfsd, ntpd, rpc, samba, nginx, sshd, avahidaemon, show_interfaces
#
# Bergware - created for Unraid OS, October 2023
WIREGUARD="/etc/wireguard"
NETWORK_INI="/var/local/emhttp/network.ini"
NETWORK_EXTRA="/boot/config/network-extra.cfg"
ipv(){
T=${1//[^:]}
[[ ${#T} -le 1 ]] && echo 4 || echo 6
}
this(){
case $CALLER in
'avahi')
grep -Pom1 "^$1=\K.*" $CONF
;;
'smb')
grep -Pom1 "^$1 = \K.*" $CONF
;;
'ntp'|'ssh')
grep -Po "^$1 \K\S+" $CONF | tr '\n' ' ' | sed 's/ $//'
;;
'nfs')
grep -Pom1 "^RPC_NFSD_OPTS=\"$OPTIONS \K[^\"]+" $NFS
;;
'rpc')
grep -Pom1 "^RPCBIND_OPTS=\"\K[^\"]+" $RPC
;;
'nginx')
NOW=();
for ADDR in $(awk '$1=="listen" && $2~/^[0-9]|\[/ && $0~/http2; #.*$/{print $2}' $SERVERS 2>/dev/null); do
# extract ipv4 / ipv6 address
[[ $(ipv $ADDR) == 4 ]] && ADDR=${ADDR%:*} || ADDR=${ADDR#*[} ADDR=${ADDR%]*}
NOW+=($ADDR)
done
# return addresses
echo ${NOW[@]}
;;
esac
}
scan(){
grep -Pom1 "^$1=\"?\K[^\"]+" $2
}
good(){
DATA=
for i in ${BIND[@]}; do
[[ $i == $1 || $1 == 0 || ${1:0:4} == fe80 ]] && DATA=1
done
if [[ -n $2 ]]; then
for i in ${NETS[@]}; do
[[ $i == $2 || $2 == 0 || ${2:0:4} == fe80 ]] && DATA=2
done
fi
echo $DATA
}
max6(){
# ipv6 addresses in long notation
FILL=:ffff:
for INPUT in $*; do
read ADDR MASK < <(IFS=/; echo $INPUT)
[[ $ADDR =~ "$FILL" && $ADDR =~ "." ]] && BITS=${ADDR#*$FILL} ADDR=${ADDR%$FILL*}$FILL:0 || BITS=
SIZE=${ADDR//[^:]/}
[[ ${ADDR:0:1} == : ]] && ADDR=0${ADDR}
[[ ${ADDR:${#ADDR}-1} == : ]] && ADDR=${ADDR}0
ADDR=${ADDR/::/:$(for((i=1;i<=$((8-${#SIZE}));i++)); do printf "0:"; done)}
MARK= ADDR=$(for QUAD in ${ADDR//:/ }; do printf "$MARK%04x" "0x$QUAD"; MARK=:; done)
[[ -n $BITS ]] && MARK= ADDR=${ADDR%$FILL*}${FILL}$(for QUAD in ${BITS//./ }; do printf "$MARK%03x" "0x$QUAD"; MARK=.; done)
[[ -z $MASK ]] && echo $ADDR || echo $ADDR/$MASK
done
}
min6(){
# ipv6 address in short notation
FILL=:ffff:
[[ -n $1 ]] && read ADDR MASK < <(IFS=/; echo $1) || return
[[ $ADDR =~ "$FILL" && $ADDR =~ "." ]] && BITS=${ADDR#*$FILL} ADDR=${ADDR%$FILL*}$FILL || BITS=
MARK= ADDR=:$(for QUAD in ${ADDR//:/ }; do printf "$MARK%x" "0x$QUAD"; MARK=:; done)
ADDR=${ADDR/$(grep -Po ':(0(:|$)){2,8}' <<< $ADDR | sort | tail -1)/::}
[[ ${ADDR:0:2} != :: ]] && ADDR=${ADDR:1}
[[ -n $BITS ]] && MARK= ADDR=${ADDR%$FILL*}:$(for QUAD in ${BITS//./ }; do printf "$MARK%x" "0x$QUAD"; MARK=.; done)
[[ -z $MASK ]] && echo $ADDR || echo $ADDR/$MASK
}
wipe(){
WET=($*)
# remove temporary (privacy extensions) and host ipv6 addresses
for TMP in $(ip -br -6 addr show scope global temporary dev $WET 2>/dev/null | sed -r 's/metric [0-9]+//g' | awk '{$1=$2="";print}'); do
for i in ${!WET[@]}; do
[[ ${WET[$i]} == $TMP || (${WET[$i]} =~ '::' && ${WET[$i]#*/} == 128) ]] && unset 'WET[i]'
done
done
# return cleaned-up list without interface name
echo ${WET[@]/$WET}
}
main(){
min6 $(max6 $(wipe $*) | sort | head -1)
}
show(){
case $# in
1) ip -br addr show scope global to $1 2>/dev/null | awk '{print $1;exit}';;
2) ip -br addr show scope global $1 $2 2>/dev/null | awk '{print $3;exit}';;
3) if [[ $1 == -6 ]]; then main $(ip -br -6 addr show scope global $2 $3 2>/dev/null | sed -r 's/metric [0-9]+//g' | awk '{$2="";print;exit}'); else ip -br -4 addr show scope global $2 $3 2>/dev/null | awk '{print $3;exit}'; fi;;
esac
}
sub(){
[[ -z $1 ]] && return
if [[ $CALLER == smb && -z $DENY6 ]]; then
# replace netmask
[[ $(ipv $1) == 4 ]] && echo ${1/\/32/\/24} || echo ${1/\/128/\/64}
else
# remove netmask
echo ${1/\/*}
fi
}
remove(){
[[ -z $1 ]] && return
for i in ${!BIND[@]}; do
[[ ${BIND[$i]} == $1 ]] && unset 'BIND[i]'
done
}
isname(){
[[ -z ${1//[^.:]} || ${1//[^.:]} == . ]] && return 0 || return 1
}
extra_name(){
for NET in $include_interfaces; do
if $(isname $NET); then
# NET is an interface name, validate
[[ $CALLER != ntp && -n $(show dev $NET) && -z $(good $NET) ]] && BIND+=($NET)
else
# NET is an IP address, convert to name
NET=$(show $NET)
[[ $CALLER != ntp && -n $NET && -z $(good $NET) ]] && BIND+=($NET)
fi
done
for NET in $exclude_interfaces; do
if $(isname $NET); then
# NET is an interface name, remove
remove $NET
else
# NET is an IP address, convert to name and remove
remove $(show $NET)
fi
done
}
extra_addr(){
for NET in $include_interfaces; do
if $(isname $NET); then
# NET is an interface name, get IP addresses
NET4=$(sub $(show -4 dev $NET))
NET6=$(sub $(show -6 dev $NET))
else
# NET is an IP address, validate
NET4=$(sub $(show -4 to $NET))
NET6=$(sub $(show -6 to $NET))
fi
[[ $CALLER != ntp && -n $NET4 && -z $(good $NET4) ]] && IPV4=yes BIND+=($NET4)
[[ $CALLER != ntp && -n $NET6 && -z $(good $NET6) ]] && IPV6=yes BIND+=($NET6)
done
for NET in $exclude_interfaces; do
if $(isname $NET); then
# NET is an interface name, get IP addresses
remove $(sub $(show -4 dev $NET))
remove $(sub $(show -6 dev $NET))
else
# NET is an IP address
remove $(sub $(show to $NET))
fi
done
}
check(){
# quick check
[[ -n $BIND ]] && return 0;
# preset return values
BIND=(); IPV4=no; IPV6=no; FAMILY=any;
# active IPV4 interfaces (including wireguard)
NETS=()
while IFS=$'\n' read -r NET; do
NET=($NET)
# exclude wireguard tunnels for ntp
[[ ${NET:0:2} == wg && $CALLER == ntp ]] && continue
# exclude wireguard VPN docker tunnels
[[ ${NET:0:2} == wg && $(grep -Pom1 '^TYPE:1="\K[^"]+' $WIREGUARD/$NET.cfg) == 8 ]] && continue
NET1=$(sub ${NET[1]})
if [[ "avahi show" =~ "$CALLER" ]]; then
[[ -n $NET && -n $NET1 && -z $(good $NET $NET1) ]] && BIND+=($NET)
[[ -n $NET1 && $NET1 != 0 ]] && IPV4=yes NETS+=($NET1)
else
[[ -n $NET1 && -z $(good $NET1) ]] && IPV4=yes BIND+=($NET1)
fi
done <<< $(ip -br -4 addr show scope global | awk '/^(br|bond|eth|wg)[0-9]+(\.[0-9]+)?/ {print $1,$3}' | sort)
# active IPV6 interfaces (including wireguard)
NETS=()
while IFS='\n' read -r NET; do
NET=($NET)
# exclude wireguard tunnels for ntp
[[ ${NET:0:2} == wg && $CALLER == ntp ]] && continue
# exclude wireguard VPN docker tunnels
[[ ${NET:0:2} == wg && $(grep -Pom1 '^TYPE:1="\K[^"]+' $WIREGUARD/$NET.cfg) == 8 ]] && continue
NET1=$(sub $(main ${NET[@]}))
if [[ "avahi show" =~ "$CALLER" ]]; then
[[ -n $NET && -n $NET1 && -z $(good $NET $NET1) ]] && BIND+=($NET)
[[ -n $NET1 && $NET1 != 0 ]] && IPV6=yes NETS+=($NET1)
else
[[ -z $DENY6 && -n $NET1 && -z $(good $NET1) ]] && IPV6=yes BIND+=($NET1)
fi
done <<< $(ip -br -6 addr show scope global | sed -r 's/metric [0-9]+//g' | awk '/^(br|bond|eth|wg)[0-9]+(\.[0-9]+)?/{$2="";print}' | sort)
# add loopback interface
if [[ "smb nfs" =~ "$CALLER" ]]; then
[[ $IPV4 == yes ]] && BIND+=(127.0.0.1)
[[ $IPV6 == yes ]] && BIND+=(::1)
fi
# add user defined interfaces
if [[ -f $NETWORK_EXTRA ]]; then
. <(fromdos <$NETWORK_EXTRA)
[[ "avahi show" =~ "$CALLER" ]] && extra_name || extra_addr
fi
if [[ $CALLER == ssh ]]; then
# BIND stays array
BIND=(${BIND[@]})
[[ $IPV4 == yes && $IPV6 == no ]] && FAMILY=inet
[[ $IPV6 == yes && $IPV4 == no ]] && FAMILY=inet6
else
# convert array to string
BIND=${BIND[@]}
[[ $CALLER == avahi ]] && BIND=${BIND// /,}
fi
return 0
}