Skip to content

Instantly share code, notes, and snippets.

@pd12bbf7608ae1
Last active August 9, 2021 08:51
Show Gist options
  • Select an option

  • Save pd12bbf7608ae1/3deb39d26112a2d43c2a3fd838ec0b3b to your computer and use it in GitHub Desktop.

Select an option

Save pd12bbf7608ae1/3deb39d26112a2d43c2a3fd838ec0b3b to your computer and use it in GitHub Desktop.
批量生成wireguard服务和客户端配置(专业用户使用)
#!/bin/bash
# debug=1
# 配置生成地址
serverConfFile='server.conf'
clientConfDir='clients'
# 服务端常量设置
serverInterfaceConfig='
ListenPort = 51234
PostUp = iptables -I FORWARD -i eth0 -o %i -j ACCEPT
PostUp = iptables -I FORWARD -i %i -o eth0 -j ACCEPT
PostUp = ip6tables -I FORWARD -i eth0 -o %i -j ACCEPT
PostUp = ip6tables -I FORWARD -i %i -o eth0 -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -s 172.18.0.0/16 -o eth0 -j MASQUERADE
PostUp = ip6tables -t nat -A POSTROUTING -s fd63:fc6c:e839:02dc::/64 -o eth0 -j MASQUERADE
PreDown = iptables -D FORWARD -i eth0 -o %i -j ACCEPT
PreDown = iptables -D FORWARD -i %i -o eth0 -j ACCEPT
PreDown = ip6tables -D FORWARD -i eth0 -o %i -j ACCEPT
PreDown = ip6tables -D FORWARD -i %i -o eth0 -j ACCEPT
PreDown = iptables -t nat -D POSTROUTING -s 172.18.0.0/16 -o eth0 -j MASQUERADE
PreDown = ip6tables -t nat -D POSTROUTING -s fd63:fc6c:e839:02dc::/64 -o eth0 -j MASQUERADE
' # 对应 [Interface] 字段
# 客户端常量设置
clientInterfaceConfig='
DNS = 223.5.5.5, 240c::6666
' # 对应 [Interface] 字段
clientPeerConfig='
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = example.org:51234
PersistentKeepalive = 30
' # 对应 [Peer] 字段
# 配置文件变量设置
# 地址区域
useIPv4=true # true or false
useIPv6=true # true or false
inet4Network='172.18.0.0/16' # CIDR 无压缩
inet6Network='fd63:fc6c:e839:02dc::/64' # CIDR 可压缩 仅支持16倍数的>=64前缀 可以从 https://simpledns.plus/private-ipv6 获取
# 使用预共享密钥加密
usePreSharedKeys=true # true or false
fontRed='\033[31m'
fontGreen='\033[32m'
fontBlue='\033[36m'
fontNormal='\033[0m'
function echoRed() {
echo -e "${fontRed}${*}${fontNormal}"
}
function echoBlue() {
echo -e "${fontBlue}${*}${fontNormal}"
}
function echoGreen() {
echo -e "${fontGreen}${*}${fontNormal}"
}
function debug() {
if [ "$debug" == "1" ]; then
echo "$*"
fi
}
function CheckFile() { # 检查输出文件
if [[ -e "$serverConfFile" ]]; then
local confirm="N"
echoRed "检测到存在服务端配置文件,继续将清空该文件" 1>&2
read -p '继续? [Y/N] ' -t 5 confirm
if [[ "$confirm" != 'Y' && "$confirm" != 'y' ]]; then
echoGreen '退出...' 1>&2
exit 1
fi
rm -f -R "$serverConfFile"
if [[ "$?" -ne '0' ]]; then
echoRed '删除失败' 1>&2
exit 1
fi
fi
if [[ -e "$clientConfDir" ]]; then
local confirm="N"
echoRed "检测到存在客户端配置目录,继续将清空该目录" 1>&2
read -p '继续? [Y/N] ' -t 5 confirm
if [[ "$confirm" != 'Y' && "$confirm" != 'y' ]]; then
echoGreen '退出...' 1>&2
exit 1
fi
rm -f -R "$clientConfDir"
if [[ "$?" -ne '0' ]]; then
echoRed '删除失败' 1>&2
exit 1
fi
fi
return 0
}
function GetClientMaxNumber() { # 获取客户端最大数量 输入 主机地址长度
local hostLength="$1"
local maxNumber=1
for ((i = 1; i <= ${hostLength}; i++)); do
maxNumber=$((maxNumber * 2))
done
maxNumber=$((maxNumber - 3)) # 网络号 网关 广播
# debug "客户端最大数量:${maxNumber}" 1>&2
echo ${maxNumber}
return 0
}
function Dec2Hex() { # 十进制到十六进制(4位) 最大 65535
local source="$1"
local target=(0 0 0 0)
target[0]=$((source / 4096))
target[1]=$((source % 4096 / 256))
target[2]=$((source % 256 / 16))
target[3]=$((source % 16))
target=($(echo "${target[@]}" | sed -e 's/10/a/g' -e 's/11/b/g' -e 's/12/c/g' -e 's/13/d/g' -e 's/14/e/g' -e 's/15/f/g'))
echo "${target[0]}${target[1]}${target[2]}${target[3]}"
return 0
}
function GetInet4NetworkAddress() { # 获取ipv4网络地址 输入 ipv4 CIDR 返回数组 网络地址
local inet4Network="$1"
local inet4NetworkLength="$(echo "${inet4Network}" | cut -d '/' -f 2)"
local inet4NetworkAddress=($(echo "${inet4Network}" | cut -d '/' -f 1 | tr '.' ' '))
debug "计算IPv4网络号..." 1>&2
debug "修改前组合: ${inet4NetworkAddress[0]}.${inet4NetworkAddress[1]}.${inet4NetworkAddress[2]}.${inet4NetworkAddress[3]}/${inet4NetworkLength}" 1>&2
local arrayNumber=$(( ${inet4NetworkLength} / 8 ))
debug "修改从数组${arrayNumber}号元素开始" 1>&2
for (( i = ${arrayNumber} + 1; i <= 3; i++ )); do # 去除主机号
inet4NetworkAddress[${i}]=0
done
local j=$((8 - ${inet4NetworkLength} % 8))
local arrayMod=1
for ((i = 1; i <= j; i++)); do
arrayMod=$((arrayMod * 2))
done
inet4NetworkAddress[${arrayNumber}]=$((inet4NetworkAddress[${arrayNumber}] - inet4NetworkAddress[${arrayNumber}] % arrayMod))
debug "修改后组合: ${inet4NetworkAddress[0]}.${inet4NetworkAddress[1]}.${inet4NetworkAddress[2]}.${inet4NetworkAddress[3]}/${inet4NetworkLength}" 1>&2
echo "${inet4NetworkAddress[@]}"
return 0
}
function GetInet6NetworkAddress() { # 获取ipv6网络地址 输入 ipv6 CIDR 返回数组 网络地址(无省略)
local inet6Network="$1"
local inet6NetworkLength="$(echo "${inet6Network}" | cut -d '/' -f 2)"
if [[ $((inet6NetworkLength % 16)) -ne '0' ]]; then
echoRed "IPv6前缀长度需要为16的倍数" 1>&2
exit 1
fi
local inet6NetworkAddressCompressed="$(echo "${inet6Network}" | cut -d '/' -f 1)"
debug "计算IPv6网络号..." 1>&2
if [[ $(echo ${inet6NetworkAddressCompressed} | grep -c '::') -ne 0 ]]; then # 地址压缩处理
debug "检测到IPv6地址压缩" 1>&2
local delimiterNum=$(echo ${inet6NetworkAddressCompressed} | grep -o ':' | wc -l)
local fillingNum=$((8-${delimiterNum}))
for ((i = 0; i < fillingNum; i++)); do
inet6NetworkAddressCompressed=$(echo "${inet6NetworkAddressCompressed}" | sed -e 's/::/:0::/g')
done
inet6NetworkAddressCompressed=$(echo "${inet6NetworkAddressCompressed}" | sed -e 's/::/:/g')
debug "展开后的地址为: ${inet6NetworkAddressCompressed}" 1>&2
fi
local inet6NetworkAddress=($(echo "${inet6NetworkAddressCompressed}" | tr ':' ' '))
debug "修改前组合: ${inet6NetworkAddress[0]}:${inet6NetworkAddress[1]}:${inet6NetworkAddress[2]}:${inet6NetworkAddress[3]}:${inet6NetworkAddress[4]}:${inet6NetworkAddress[5]}:${inet6NetworkAddress[6]}:${inet6NetworkAddress[7]}/${inet6NetworkLength}" 1>&2
local arrayNumber=$(( ${inet6NetworkLength} / 16 ))
debug "修改从数组${arrayNumber}号元素开始" 1>&2
for (( i = ${arrayNumber} ; i <= 7; i++ )); do # 去除主机号
inet6NetworkAddress[${i}]=0
done
debug "修改后组合: ${inet6NetworkAddress[0]}:${inet6NetworkAddress[1]}:${inet6NetworkAddress[2]}:${inet6NetworkAddress[3]}:${inet6NetworkAddress[4]}:${inet6NetworkAddress[5]}:${inet6NetworkAddress[6]}:${inet6NetworkAddress[7]}/${inet6NetworkLength}" 1>&2
echo "${inet6NetworkAddress[@]}"
return 0
}
function GetInet4Address() { # 全局调用ipv4网络地址 输入 主机编号(数字)从1开始 返回地址(无前缀)
local number="$1"
local inet4HostAddress=(0 0 0 0)
inet4HostAddress[0]=$((number / 16777216))
inet4HostAddress[1]=$((number % 16777216 / 65536))
inet4HostAddress[2]=$((number % 65536 / 256))
inet4HostAddress[3]=$((number % 256))
# debug "生成IPv4主机地址为: ${inet4HostAddress[0]}.${inet4HostAddress[1]}.${inet4HostAddress[2]}.${inet4HostAddress[3]}" 1>&2
echo "$((inet4NetworkAddress[0]+inet4HostAddress[0])).$((inet4NetworkAddress[1]+inet4HostAddress[1])).$((inet4NetworkAddress[2]+inet4HostAddress[2])).$((inet4NetworkAddress[3]+inet4HostAddress[3]))"
return 0
}
function GetInet6Address() { # 全局调用ipv6网络地址 输入 主机编号(数字)从1开始 返回地址(无前缀)
local number="$1"
local inet6HostAddress=(0 0 0 0 0 0 0 0)
inet6HostAddress[4]=$((number / 281474976710656))
inet6HostAddress[5]=$((number % 281474976710656 / 4294967296))
inet6HostAddress[6]=$((number % 4294967296 / 65536))
inet6HostAddress[7]=$((number % 65536))
inet6HostAddress[4]=$(Dec2Hex "${inet6HostAddress[4]}")
inet6HostAddress[5]=$(Dec2Hex "${inet6HostAddress[5]}")
inet6HostAddress[6]=$(Dec2Hex "${inet6HostAddress[6]}")
inet6HostAddress[7]=$(Dec2Hex "${inet6HostAddress[7]}")
# debug "生成IPv6主机地址为: ${inet6HostAddress[0]}:${inet6HostAddress[1]}:${inet6HostAddress[2]}:${inet6HostAddress[3]}:${inet6HostAddress[4]}:${inet6HostAddress[5]}:${inet6HostAddress[6]}:${inet6HostAddress[7]}" 1>&2
local arrayNumber=$(( ${inet6NetworkLength} / 16 ))
# debug "合并从数组${arrayNumber}号元素开始" 1>&2
local inet6Combination=("${inet6NetworkAddress[@]}")
for (( i = arrayNumber; i<=7; i++ )); do
inet6Combination[${i}]="${inet6HostAddress[${i}]}"
done
echo "${inet6Combination[0]}:${inet6Combination[1]}:${inet6Combination[2]}:${inet6Combination[3]}:${inet6Combination[4]}:${inet6Combination[5]}:${inet6Combination[6]}:${inet6Combination[7]}"
return 0
}
function GetServerInterfaceConfig() { # 生成server Interface 字段 输入 PrivateKey ipv4Address(CIDR) ipv6Address(CIDR)
local privateKey="$1"
local ipv4Address="$2"
local ipv6Address="$3"
echo '[Interface]'
echo "PrivateKey = ${privateKey}"
if [[ "${useIPv4}" == "true" ]]; then
echo "Address = ${ipv4Address}"
fi
if [[ "${useIPv6}" == "true" ]]; then
echo "Address = ${ipv6Address}"
fi
echo "${serverInterfaceConfig}"
return 0
}
function GetServerPeerConfig() { # 生成server Peer 字段 输入 PublicKey PresharedKey ipv4HostAddress(无前缀) ipv6HostAddress(无前缀)
local publicKey="$1"
local presharedKey="$2"
local ipv4HostAddress="$3"
local ipv6HostAddress="$4"
echo '[Peer]'
echo "PublicKey = ${publicKey}"
if [[ "$usePreSharedKeys" == 'true' ]]; then
echo "PresharedKey = ${presharedKey}"
fi
if [[ "${useIPv4}" == "true" ]]; then
echo "AllowedIPs = ${ipv4HostAddress}/32"
fi
if [[ "${useIPv6}" == "true" ]]; then
echo "AllowedIPs = ${ipv6HostAddress}/128"
fi
return 0
}
function GetClientConfig() { # 生成client 配置 输入 PrivateKey ipv4Address(CIDR) ipv6Address(CIDR) PublicKey PresharedKey
local privateKey="$1"
local ipv4Address="$2"
local ipv6Address="$3"
local publicKey="$4"
local presharedKey="$5"
echo '[Interface]'
echo "PrivateKey = ${privateKey}"
if [[ "${useIPv4}" == "true" ]]; then
echo "Address = ${ipv4Address}"
fi
if [[ "${useIPv6}" == "true" ]]; then
echo "Address = ${ipv6Address}"
fi
echo "${clientInterfaceConfig}"
echo '[Peer]'
echo "PublicKey = ${publicKey}"
if [[ "$usePreSharedKeys" == 'true' ]]; then
echo "PresharedKey = ${presharedKey}"
fi
echo "${clientPeerConfig}"
return 0
}
function ShowInfo() { # 展示脚本使用的配置参数
# echoBlue "#############################"
echoBlue "将使用如下参数生成配置文件:"
if [[ "${useIPv4}" == "true" ]]; then
echoGreen "ipv4网络地址: ${inet4NetworkAddress[0]}.${inet4NetworkAddress[1]}.${inet4NetworkAddress[2]}.${inet4NetworkAddress[3]}/${inet4HostLength}"
echoGreen "最大客户端数量: ${maxNumber}"
fi
if [[ "${useIPv6}" == "true" ]]; then
echoGreen "ipv6网络地址: ${inet6NetworkAddress[0]}:${inet6NetworkAddress[1]}:${inet6NetworkAddress[2]}:${inet6NetworkAddress[3]}:${inet6NetworkAddress[4]}:${inet6NetworkAddress[5]}:${inet6NetworkAddress[6]}:${inet6NetworkAddress[7]}/${inet6HostLength}"
fi
if [[ "${usePreSharedKeys}" == "true" ]]; then
echoGreen "使用预共享密钥"
fi
echoBlue "信息打印结束"
# echoBlue "#############################"
return 0
}
function GetExample() { # 生成一对server client 作为样例
echoBlue "生成配置文件样例..." 1>&2
echoGreen "# 服务端配置文件"
local serverPrivateKey=$(wg genkey)
local serverPublicKey=$(echo "${serverPrivateKey}" | wg pubkey)
if [[ "${useIPv4}" == "true" ]]; then
local serverInet4="$(GetInet4Address 1)/${inet4NetworkLength}"
fi
if [[ "${useIPv6}" == "true" ]]; then
local serverInet6="$(GetInet6Address 1)/${inet6NetworkLength}"
fi
GetServerInterfaceConfig "${serverPrivateKey}" "${serverInet4}" "${serverInet6}"
local peerPrivateKey=$(wg genkey)
local peerPublicKey=$(echo "${peerPrivateKey}" | wg pubkey)
if [[ "$usePreSharedKeys" == 'true' ]]; then
local peerPresharedKey=$(wg genpsk)
fi
if [[ "${useIPv4}" == "true" ]]; then
local peerInet4="$(GetInet4Address 2)"
fi
if [[ "${useIPv6}" == "true" ]]; then
local peerInet6="$(GetInet6Address 2)"
fi
GetServerPeerConfig "${peerPublicKey}" "${peerPresharedKey}" "${peerInet4}" "${peerInet6}"
echoGreen "# 服务端配置文件结束"
echo
echoGreen "# 客户端配置文件"
GetClientConfig "${peerPrivateKey}" "${peerInet4}/32" "${peerInet6}/128" "${serverPublicKey}" "${peerPresharedKey}"
echoGreen "# 客户端配置文件结束"
return 0
}
function GetClientNumber() { # 获得用户客户端数量
local clientNumber
if [[ -z ${maxNumber} ]]; then
read -p "输入客户端数量:" clientNumber
echo "$clientNumber"
return 0
else
read -p "输入客户端数量(最大${maxNumber}):" clientNumber
if [[ "$clientNumber" -gt ${maxNumber} ]]; then
echoRed "数量超过最大数量,按照最大数量进行生成" 1>&2
clientNumber="${maxNumber}"
fi
echo "$clientNumber"
fi
# read -i "输入客户端数量:"
}
function GenerateConfig() { # 生成配置文件
echoBlue "生成服务端配置文件..." 1>&2
local serverPrivateKey=$(wg genkey)
local serverPublicKey=$(echo "${serverPrivateKey}" | wg pubkey)
if [[ "${useIPv4}" == "true" ]]; then
local serverInet4="$(GetInet4Address 1)/${inet4NetworkLength}"
fi
if [[ "${useIPv6}" == "true" ]]; then
local serverInet6="$(GetInet6Address 1)/${inet6NetworkLength}"
fi
GetServerInterfaceConfig "${serverPrivateKey}" "${serverInet4}" "${serverInet6}" >> ${serverConfFile}
local peerPrivateKey peerPublicKey peerPresharedKey peerInet4 peerInet6 clientName
for (( i = 1; i <= clientNumber; i++ )); do
clientName=$(printf "client_%0${#clientNumber}d" "$i")
echoBlue "生成 ${clientName} 客户端文件..." 1>&2
peerPrivateKey=$(wg genkey)
peerPublicKey=$(echo "${peerPrivateKey}" | wg pubkey)
if [[ "$usePreSharedKeys" == 'true' ]]; then
peerPresharedKey=$(wg genpsk)
fi
if [[ "${useIPv4}" == "true" ]]; then
peerInet4="$(GetInet4Address $(( i + 1 )))"
fi
if [[ "${useIPv6}" == "true" ]]; then
peerInet6="$(GetInet6Address $(( i + 1 )))"
fi
echo "# ${clientName}" >> ${serverConfFile}
GetServerPeerConfig "${peerPublicKey}" "${peerPresharedKey}" "${peerInet4}" "${peerInet6}" >> ${serverConfFile}
echo >> ${serverConfFile}
echo "# ${clientName}" >> "${clientConfDir}/${clientName}.conf"
GetClientConfig "${peerPrivateKey}" "${peerInet4}/32" "${peerInet6}/128" "${serverPublicKey}" "${peerPresharedKey}" >> "${clientConfDir}/${clientName}.conf"
done
return 0
}
CheckFile
umask 0077
touch ${serverConfFile}
if [[ "$?" -ne '0' ]]; then
echoRed "文件创建失败" 1>&2
exit 1
fi
mkdir ${clientConfDir}
if [[ "$?" -ne '0' ]]; then
echoRed "文件夹创建失败" 1>&2
exit 1
fi
if [[ "${useIPv4}" != "true" && "${useIPv6}" != "true" ]]; then
echoRed "至少需要使用一种IP协议" 1>&2
exit 1
fi
if [[ "${useIPv4}" == "true" ]]; then
inet4NetworkLength="$(echo "${inet4Network}" | cut -d '/' -f 2)"
inet4HostLength=$((32-inet4NetworkLength))
debug "使用IPv4" 1>&2
debug "网络地址长度${inet4NetworkLength} 主机地址长度${inet4HostLength}" 1>&2
maxNumber=$(GetClientMaxNumber ${inet4HostLength})
debug "最大客户端数量为${maxNumber}" 1>&2
inet4NetworkAddress=($(GetInet4NetworkAddress ${inet4Network}))
debug "数组形式 IPv4: ${inet4NetworkAddress[@]}" 1>&2
fi
if [[ "${useIPv6}" == "true" ]]; then
inet6NetworkLength="$(echo "${inet6Network}" | cut -d '/' -f 2)"
inet6HostLength=$((128-inet6NetworkLength))
debug "使用IPv6" 1>&2
debug "网络地址长度${inet6NetworkLength} 主机地址长度${inet6HostLength}" 1>&2
debug "IPv6不限制最大客户端数量" 1>&2
inet6NetworkAddress=($(GetInet6NetworkAddress ${inet6Network}))
debug "数组形式 IPv6: ${inet6NetworkAddress[@]}" 1>&2
fi
ShowInfo
GetExample
read -p "继续? [Y/N] " confirm
if [[ "$confirm" != 'Y' && "$confirm" != 'y' ]]; then
echoBlue '退出...' 1>&2
exit 0
fi
clientNumber=$(GetClientNumber)
GenerateConfig
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment