462 lines
9.9 KiB
Bash
Executable file
462 lines
9.9 KiB
Bash
Executable file
#!/bin/sh
|
|
BP_SYSCTL="${SYSCTL_CONF_FILE:-/etc/sysctl.d/bypasser-forwarding.conf}"
|
|
BP_WG_INTERFACE_PREFIX="bp-"
|
|
BP_WG_DIR="${BP_WG_DIR:-/etc/wireguard}"
|
|
BP_WG_PEERS_DIR="$BP_WG_DIR"/peers
|
|
BP_WG_DEFAULT_MIN_PORT="${BP_WG_DEFAULT_MIN_PORT:-55107}"
|
|
BP_WG_DEFAULT_MAX_PORT="${BP_WG_DEFAULT_MAX_PORT:-55207}"
|
|
BG_WG_SUBNET_PREFIX="69.0"
|
|
BG_WG_INTERFACE_SUBNET_MASK="24"
|
|
BG_WG_PEER_SUBNET_MASK="32"
|
|
_IPV4_RULE="net.ipv4.ip_forward"
|
|
_IPV6_RULE="net.ipv6.conf.all.forwarding"
|
|
|
|
OPT_TARGET="$1"
|
|
OPT_ACTION="$2"
|
|
OPT_ARG="$3"
|
|
|
|
if [ "$(whoami)" != "root" ]; then
|
|
echo "Error, must run as root"
|
|
exit 1
|
|
fi
|
|
|
|
_print_help() {
|
|
echo "Usage:"
|
|
echo " bypasser [vpn|peer] [<vpn_name>|<vpn_name>:<peer_name>]"
|
|
echo
|
|
echo "Examples:"
|
|
echo
|
|
echo "Add a new VPN:"
|
|
echo " bypasser vpn new myvpn"
|
|
echo
|
|
echo "Add a new peer:"
|
|
echo " bypasser peer new myvpn:mypeer"
|
|
}
|
|
|
|
_get_vpn_name() {
|
|
printf "Enter a name for the new virtual network"
|
|
read -r _name
|
|
|
|
if [ -z $_name ]; then
|
|
return 1
|
|
fi
|
|
|
|
echo "$_name"
|
|
}
|
|
|
|
_vpn_name_available() {
|
|
_vpn_name="$1"
|
|
[ -z $_vpn_name ] && return 1
|
|
|
|
_vpn_file="$BP_WG_DIR"/"$BP_WG_INTERFACE_PREFIX$_vpn_name".conf
|
|
! [ -f "$_vpn_file" ]
|
|
}
|
|
|
|
_enable_forwarding() {
|
|
printf "%s = 1\n%s = 1" "$_IPV4_RULE" "$_IPV6_RULE" >"$BP_SYSCTL"
|
|
sysctl -p >/dev/null 2>&1
|
|
sysctl --system >/dev/null 2>&1
|
|
}
|
|
|
|
_check_forwarding() {
|
|
_v4="$(sysctl "$_IPV4_RULE")"
|
|
_v4="${_v4##*= }"
|
|
_v6="$(sysctl "$_IPV6_RULE")"
|
|
_v6="${_v6##*= }"
|
|
|
|
if [ "$_v4" = 0 ] || [ "$_v6" = 0 ]; then
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
_vpn_list() {
|
|
echo "Available VPNs in '$BP_WG_DIR':"
|
|
|
|
if ! [ -d "$BP_WG_DIR" ] || [ -z "$BP_WG_DIR" ]; then
|
|
return 0
|
|
fi
|
|
|
|
for f in "$BP_WG_DIR"/*; do
|
|
if [ -f "$f" ]; then
|
|
vpn="${f##*/}"
|
|
vpn="${vpn#${BP_WG_INTERFACE_PREFIX}}"
|
|
vpn="${vpn%.conf}"
|
|
echo " - $vpn"
|
|
fi
|
|
done
|
|
}
|
|
|
|
_peer_list() {
|
|
echo "Available Peers in '$BP_WG_PEERS_DIR':"
|
|
|
|
if ! [ -d "$BP_WG_PEERS_DIR" ] || [ -z "$BP_WG_PEERS_DIR" ]; then
|
|
return 0
|
|
fi
|
|
|
|
for f in "$BP_WG_PEERS_DIR"/*; do
|
|
if [ -f "$f" ]; then
|
|
peer="${f##*/}"
|
|
peer="${peer#${BP_WG_INTERFACE_PREFIX}}"
|
|
peer="${peer%.conf}"
|
|
peer="${peer%.conf}"
|
|
vpn="${peer%%-*}"
|
|
peer="${peer##*-}"
|
|
echo " - $vpn:$peer"
|
|
fi
|
|
done
|
|
}
|
|
|
|
_vpn_restart() {
|
|
_vpn_name="$1"
|
|
_vpn_interface="$BP_WG_INTERFACE_PREFIX$_vpn_name"
|
|
|
|
wg-quick down "$_vpn_interface"
|
|
systemctl restart wg-quick@"$_vpn_interface"
|
|
}
|
|
|
|
_vpn_enable() {
|
|
_vpn_name="$1"
|
|
_vpn_interface="$BP_WG_INTERFACE_PREFIX$_vpn_name"
|
|
|
|
wg-quick down "$_vpn_interface"
|
|
systemctl enable --now wg-quick@"$_vpn_interface"
|
|
}
|
|
|
|
_vpn_disable() {
|
|
_vpn_name="$1"
|
|
_vpn_interface="$BP_WG_INTERFACE_PREFIX$_vpn_name"
|
|
|
|
wg-quick down "$_vpn_interface"
|
|
systemctl disable --now wg-quick@"$_vpn_interface"
|
|
}
|
|
|
|
_get_available_port() {
|
|
_avail_port="$((BP_WG_DEFAULT_MIN_PORT - 1))"
|
|
|
|
for f in "$BP_WG_DIR"/*; do
|
|
if [ -f "$f" ]; then
|
|
while read -r line; do
|
|
case "$line" in
|
|
*ListenPort*)
|
|
_port="${line##* = }"
|
|
|
|
if [ -n "$_port" ] && [ "$_port" -gt "$_avail_port" ]; then
|
|
_avail_port="$_port"
|
|
fi
|
|
|
|
;;
|
|
esac
|
|
done <"$f"
|
|
fi
|
|
done
|
|
|
|
echo "$((_avail_port + 1))"
|
|
}
|
|
|
|
_vpn_new() {
|
|
if ! [ -d "$BP_WG_DIR" ] || [ -z "$BP_WG_DIR" ]; then
|
|
echo "Error: directory '$BP_WG_DIR' nonexistent, create it (and set 600 permissions) to add new VPNs"
|
|
return 1
|
|
fi
|
|
|
|
_vpn_name="$1"
|
|
_vpn_file="$BP_WG_DIR"/"$BP_WG_INTERFACE_PREFIX$_vpn_name".conf
|
|
|
|
if [ -f "$_vpn_file" ]; then
|
|
echo "Error: vpn name '$_vpn_name' already exists in '$_vpn_file'"
|
|
return 1
|
|
fi
|
|
|
|
_port="$(_get_available_port)"
|
|
if [ "$?" != 0 ] || [ -z "$_port" ]; then
|
|
echo "Error: could not get an available port"
|
|
return 1
|
|
fi
|
|
|
|
_server_sec_key=$(wg genkey)
|
|
if [ "$?" != 0 ]; then
|
|
echo "Error: could not generate private key"
|
|
return 1
|
|
fi
|
|
|
|
_highest="$(_highest_interface)"
|
|
_new=$((_highest + 1))
|
|
|
|
echo "[Interface]
|
|
PrivateKey = ${_server_sec_key}
|
|
ListenPort = ${_port}
|
|
Address = ${BG_WG_SUBNET_PREFIX}.${_new}.1/${BG_WG_INTERFACE_SUBNET_MASK}" >"$_vpn_file"
|
|
|
|
chmod 600 "$_vpn_file"
|
|
|
|
_vpn_enable "$_vpn_name"
|
|
}
|
|
|
|
_vpn_rm() {
|
|
if ! [ -d "$BP_WG_DIR" ] || [ -z "$BP_WG_DIR" ]; then
|
|
return 1
|
|
fi
|
|
|
|
_vpn_name="$1"
|
|
_vpn_file="$BP_WG_DIR"/"$BP_WG_INTERFACE_PREFIX$_vpn_name".conf
|
|
|
|
_vpn_disable "$_vpn_name"
|
|
|
|
if [ -f "$_vpn_file" ]; then
|
|
sudo rm -f "$_vpn_file"
|
|
else
|
|
echo "Error: file '$_vpn_file' nonexistent"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
_peer_rm() {
|
|
if ! [ -d "$BP_WG_PEERS_DIR" ] || [ -z "$BP_WG_PEERS_DIR" ]; then
|
|
return 1
|
|
fi
|
|
|
|
_peer_name="$1"
|
|
_vpn_name="${_peer_name%%:*}"
|
|
_peer_name="${_peer_name##*:}"
|
|
_peer_file="$BP_WG_PEERS_DIR"/"$BP_WG_INTERFACE_PREFIX$_vpn_name-$_peer_name".conf
|
|
|
|
if ! [ -f "$_peer_file" ]; then
|
|
echo "Error: peer file '$_peer_file' nonexistent"
|
|
return 1
|
|
fi
|
|
|
|
if [ -f "$_peer_file" ]; then
|
|
sudo rm -f "$_peer_file"
|
|
else
|
|
echo "Error: file '$_peer_file' nonexistent"
|
|
return 1
|
|
fi
|
|
|
|
_vpn_restart "$_vpn_name"
|
|
}
|
|
|
|
_get_host_addr() {
|
|
_addr="$1"
|
|
_addr="${_addr%%/*}"
|
|
_host="${_addr#${BG_WG_SUBNET_PREFIX}.}"
|
|
echo "$_host"
|
|
}
|
|
|
|
_get_interface_octet() {
|
|
_host="$(_get_host_addr "$1")"
|
|
echo "${_host%.*}"
|
|
}
|
|
|
|
_get_peer_octet() {
|
|
_host="$(_get_host_addr "$1")"
|
|
echo "${_host#*.}"
|
|
}
|
|
|
|
_highest_interface() {
|
|
_highest=0
|
|
for f in "$BP_WG_DIR"/*; do
|
|
if [ -f "$f" ]; then
|
|
while read -r line; do
|
|
case "$line" in
|
|
*Address*)
|
|
_addr="${line##* = }"
|
|
_interface_octet="$(_get_interface_octet "$_addr")"
|
|
|
|
if [ -n "$_interface_octet" ] && [ "$_interface_octet" -gt "$_highest" ]; then
|
|
_highest="$_interface_octet"
|
|
fi
|
|
|
|
;;
|
|
esac
|
|
done <"$f"
|
|
fi
|
|
done
|
|
|
|
echo "$_highest"
|
|
}
|
|
|
|
_highest_peer_in_vpn() {
|
|
_highest=1
|
|
_interface_conf="$BP_WG_DIR"/"$BP_WG_INTERFACE_PREFIX$1".conf
|
|
|
|
if [ -f "$_interface_conf" ]; then
|
|
while read -r line; do
|
|
case "$line" in
|
|
*AllowedIPs*)
|
|
_addr="${line##* = }"
|
|
_peer_octet="$(_get_peer_octet "$_addr")"
|
|
|
|
if [ -n "$_peer_octet" ] && [ "$_peer_octet" -gt "$_highest" ]; then
|
|
_highest="$_peer_octet"
|
|
fi
|
|
|
|
;;
|
|
esac
|
|
done <"$_interface_conf"
|
|
fi
|
|
|
|
echo "$_highest"
|
|
}
|
|
|
|
_vpn_exists() {
|
|
[ -f "$BP_WG_DIR"/"$1" ]
|
|
}
|
|
|
|
_get_interface_address() {
|
|
_interface_file="$BP_WG_DIR"/"$BP_WG_INTERFACE_PREFIX$1".conf
|
|
|
|
if [ -f "$_interface_file" ]; then
|
|
while read -r line; do
|
|
case "$line" in
|
|
*Address*)
|
|
_addr="${line##* = }"
|
|
echo "$(_get_interface_octet "$_addr")"
|
|
break
|
|
;;
|
|
esac
|
|
done <"$_interface_file"
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
_peer_new() {
|
|
_arg="$1"
|
|
|
|
if ! [ -d "$BP_WG_DIR" ] || [ -z "$BP_WG_DIR" ]; then
|
|
echo "Error: directory '$BP_WG_DIR' nonexistent, create it (and set 600 permissions) to add new VPNs"
|
|
return 1
|
|
fi
|
|
|
|
if ! [ -d "$BP_WG_PEERS_DIR" ] || [ -z "$BP_WG_PEERS_DIR" ]; then
|
|
echo "Error: directory '$BP_WG_PEERS_DIR' nonexistent, create it (and set 600 permissions) to add new VPNs"
|
|
return 1
|
|
fi
|
|
|
|
_vpn_name="${_arg%%:*}"
|
|
_peer_name="${_arg##*:}"
|
|
_vpn_file="$BP_WG_DIR"/"$BP_WG_INTERFACE_PREFIX$_vpn_name".conf
|
|
_peer_file="$BP_WG_PEERS_DIR"/"$BP_WG_INTERFACE_PREFIX$_vpn_name"-"$_peer_name".conf
|
|
|
|
if [ -z "$_vpn_name" ] || [ -z "$_peer_name" ]; then
|
|
echo "Error: invalid vpn or peer name"
|
|
return 1
|
|
fi
|
|
|
|
if [ -f "$_peer_file" ]; then
|
|
echo "Error: peer file '$_peer_file' already exists"
|
|
return 1
|
|
fi
|
|
|
|
_server_sec_key=
|
|
while read -r line; do
|
|
case "$line" in
|
|
*PrivateKey*)
|
|
_server_sec_key="${line##* = }"
|
|
break
|
|
;;
|
|
esac
|
|
done <"$_vpn_file"
|
|
if [ -z "$_server_sec_key" ]; then
|
|
echo "Error: could not find interface private key"
|
|
return 1
|
|
fi
|
|
_server_pub_key=$(echo "${_server_sec_key}" | wg pubkey)
|
|
|
|
_server_port=
|
|
while read -r line; do
|
|
case "$line" in
|
|
*ListenPort*)
|
|
_server_port="${line##* = }"
|
|
break
|
|
;;
|
|
esac
|
|
done <"$_vpn_file"
|
|
if [ -z "$_server_port" ]; then
|
|
echo "Error: could not find interface ListenPort"
|
|
return 1
|
|
fi
|
|
|
|
_highest="$(_highest_peer_in_vpn "$_vpn_name")"
|
|
_new_octet=$((_highest + 1))
|
|
|
|
_addr="$(_get_interface_address "$_vpn_name")"
|
|
_vpn_octet="$(_get_interface_octet "$_addr")"
|
|
|
|
if [ -z "$_vpn_octet" ]; then
|
|
echo "Error: invalid vpn name"
|
|
return 1
|
|
fi
|
|
|
|
_peer_sec_key=$(wg genkey)
|
|
_peer_pub_key=$(echo "${_peer_sec_key}" | wg pubkey)
|
|
_peer_pre_key=$(wg genpsk)
|
|
|
|
_server_ipv4="$(ip -4 addr | sed -ne 's|^.* inet \([^/]*\)/.* scope global.*$|\1|p' | awk '{print $1}' | head -1)"
|
|
|
|
echo "[Peer]
|
|
PublicKey = ${_peer_pub_key}
|
|
PresharedKey = ${_peer_pre_key}
|
|
AllowedIPs = ${BG_WG_SUBNET_PREFIX}.${_vpn_octet}.${_new_octet}/${BG_WG_PEER_SUBNET_MASK}" >>"$_vpn_file"
|
|
|
|
echo
|
|
echo "=== NEW CLIENT CONFIGURATION ==="
|
|
echo "Location: $_peer_file"
|
|
echo "================================"
|
|
echo
|
|
|
|
echo "[Interface]
|
|
PrivateKey = ${_peer_sec_key}
|
|
Address = ${BG_WG_SUBNET_PREFIX}.${_vpn_octet}.${_new_octet}" >"$_peer_file"
|
|
echo "[Peer]
|
|
PublicKey = ${_server_pub_key}
|
|
PresharedKey = ${_peer_pre_key}
|
|
AllowedIPs = ${BG_WG_SUBNET_PREFIX}.${_vpn_octet}.0/${BG_WG_INTERFACE_SUBNET_MASK}
|
|
Endpoint = ${_server_ipv4}:${_server_port}
|
|
PersistentKeepalive = 25" >>"$_peer_file"
|
|
|
|
while read -r line; do
|
|
echo "$line"
|
|
done <"$_peer_file"
|
|
echo
|
|
echo "================================"
|
|
echo
|
|
|
|
_vpn_restart "$_vpn_name"
|
|
}
|
|
|
|
if ! _check_forwarding; then
|
|
printf "Forwarding is not enabled, enable in '$BP_SYSCTL'? [y/n]: "
|
|
read -r _opt
|
|
|
|
case "$_opt" in
|
|
y | Y) _enable_forwarding ;;
|
|
*) exit 1 ;;
|
|
esac
|
|
fi
|
|
|
|
case "$OPT_TARGET" in
|
|
|
|
vpn)
|
|
case "$OPT_ACTION" in
|
|
new) _vpn_new "$OPT_ARG" ;;
|
|
rm) _vpn_rm "$OPT_ARG" ;;
|
|
list) _vpn_list ;;
|
|
"") _vpn_list ;;
|
|
*) _vpn_new "$OPT_ACTION" ;;
|
|
esac
|
|
;;
|
|
|
|
peer)
|
|
case "$OPT_ACTION" in
|
|
new) _peer_new "$OPT_ARG" ;;
|
|
rm) _peer_rm "$OPT_ARG" ;;
|
|
list) _peer_list ;;
|
|
"") _peer_list ;;
|
|
*) _print_help ;;
|
|
esac
|
|
;;
|
|
|
|
*) _print_help ;;
|
|
|
|
esac
|