Last active
August 9, 2021 08:51
-
-
Save pd12bbf7608ae1/3deb39d26112a2d43c2a3fd838ec0b3b to your computer and use it in GitHub Desktop.
批量生成wireguard服务和客户端配置(专业用户使用)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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