Created
January 20, 2024 13:51
-
-
Save pd12bbf7608ae1/6bb7bd62ab24038e34e64a4eeb8aa011 to your computer and use it in GitHub Desktop.
Wireguard Dynamic
This file contains 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 | |
# 动态更新指定wg接口对端IP | |
# debug=1 | |
# 配置 | |
interfaceName="wg0" # wg 命令中的端口名称 | |
# 对端配置 | |
publicKey="123456=" # 对端公钥 | |
domains=( | |
"example.org" | |
"example.com" | |
) # 对端域名 可有多个 | |
recordType="A" # 或者AAAA | |
sshPort="22" | |
sshPublicKey="1234546=" # 对端ssh 公钥 sha256 验证IP准确性 | |
sshPublicKeyType="ed25519" | |
# 对端检测用 | |
pingTimeout=2 # 超时时间 | |
pingCount=3 # ping计数 | |
pingIp="192.168.1.1" # 对端检测用IP | |
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 | |
} | |
# 使用 jq 1.7 速度更快 https://github.com/jqlang/jq/ | |
function GetAliDns { # 获取 ali dns 结果 输入 域名 类型 | |
local domain="$1" | |
local type="$(echo "$2" | tr 'a-z' 'A-Z')" | |
debug "AliDns $domain $type" 1>&2 | |
if [ "$type" == 'AAAA' ]; then # 将类型改为编号 | |
type=28 | |
else | |
type=1 | |
fi | |
# 发起请求 最长2秒 | |
local curlInfo curlCode | |
curlInfo=$(curl --max-time 2 -w "Code:%{response_code}" -X GET "https://dns.alidns.com/resolve?name=${domain}&type=${type}") | |
curlCode="$?" | |
debug "$curlInfo" 1>&2 | |
if [ "$curlCode" -ne 0 ]; then | |
echoRed "curl 错误 Code:${curlCode}" 1>&2 | |
return 1 | |
fi | |
local responseCode=$(echo "${curlInfo}" | tr -d "\n" | sed -e "s/.*Code://g") | |
if [ "$responseCode" != "200" ]; then | |
echoRed "响应码错误 Code:${responseCode}" 1>&2 | |
return 1 | |
fi | |
curlInfo=$(echo "${curlInfo}" | sed -e "s/Code:200$//g") | |
local jqInfo jqCode | |
jqInfo=$(echo "${curlInfo}" | jq --raw-output ".Answer.[] | select(.type == ${type}) | .data") | |
jqCode="$?" | |
# debug "$jqInfo" 1>&2 | |
if [ "$jqCode" -ne 0 ]; then | |
echoRed "jq 解析错误 Code:${jqCode}" 1>&2 | |
return 1 | |
fi | |
# 解析成功 取第一个 | |
echo "${jqInfo}" | head -1 | |
} | |
function GetTencentDns { # 获取 doh.pub 结果 输入 域名 类型 | |
local domain="$1" | |
local type="$(echo "$2" | tr 'a-z' 'A-Z')" | |
debug "TencentDns $domain $type" 1>&2 | |
if [ "$type" == 'AAAA' ]; then # 将类型改为编号 | |
type=28 | |
else | |
type=1 | |
fi | |
# 发起请求 最长2秒 | |
local curlInfo curlCode | |
curlInfo=$(curl --max-time 2 -w "Code:%{response_code}" -X GET "https://doh.pub/resolve?name=${domain}&type=${type}") | |
curlCode="$?" | |
debug "$curlInfo" 1>&2 | |
if [ "$curlCode" -ne 0 ]; then | |
echoRed "curl 错误 Code:${curlCode}" 1>&2 | |
return 1 | |
fi | |
local responseCode=$(echo "${curlInfo}" | tr -d "\n" | sed -e "s/.*Code://g") | |
if [ "$responseCode" != "200" ]; then | |
echoRed "响应码错误 Code:${responseCode}" 1>&2 | |
return 1 | |
fi | |
curlInfo=$(echo "${curlInfo}" | sed -e "s/Code:200$//g") | |
local jqInfo jqCode | |
jqInfo=$(echo "${curlInfo}" | jq --raw-output ".Answer.[] | select(.type == ${type}) | .data") | |
jqCode="$?" | |
# debug "$jqInfo" 1>&2 | |
if [ "$jqCode" -ne 0 ]; then | |
echoRed "jq 解析错误 Code:${jqCode}" 1>&2 | |
return 1 | |
fi | |
# 解析成功 取第一个 | |
echo "${jqInfo}" | head -1 | |
} | |
function GetCloudflareDns { # 获取 Cloudflare dns 结果 输入 域名 类型 | |
local domain="$1" | |
local type="$(echo "$2" | tr 'a-z' 'A-Z')" | |
debug "CloudflareDns $domain $type" 1>&2 | |
if [ "$type" == 'AAAA' ]; then # 将类型改为编号 | |
type=28 | |
else | |
type=1 | |
fi | |
# 发起请求 最长2秒 | |
local curlInfo curlCode | |
curlInfo=$(curl --max-time 8 -w "Code:%{response_code}" -H "accept: application/dns-json" -X GET "https://1.0.0.1/dns-query?name=${domain}&type=${type}") | |
curlCode="$?" | |
debug "$curlInfo" 1>&2 | |
if [ "$curlCode" -ne 0 ]; then | |
echoRed "curl 错误 Code:${curlCode}" 1>&2 | |
return 1 | |
fi | |
local responseCode=$(echo "${curlInfo}" | tr -d "\n" | sed -e "s/.*Code://g") | |
if [ "$responseCode" != "200" ]; then | |
echoRed "响应码错误 Code:${responseCode}" 1>&2 | |
return 1 | |
fi | |
curlInfo=$(echo "${curlInfo}" | sed -e "s/Code:200$//g") | |
local jqInfo jqCode | |
jqInfo=$(echo "${curlInfo}" | jq --raw-output ".Answer.[] | select(.type == ${type}) | .data") | |
jqCode="$?" | |
# debug "$jqInfo" 1>&2 | |
if [ "$jqCode" -ne 0 ]; then | |
echoRed "jq 解析错误 Code:${jqCode}" 1>&2 | |
return 1 | |
fi | |
# 解析成功 取第一个 | |
echo "${jqInfo}" | head -1 | |
} | |
# Debian 默认只有 resolvectl | |
# ubuntu 18.04 没有 resolvectl 20.04 存在 | |
# debian 没有测试 | |
function GetLocalDns { # 获取本地 DNS 结果 输入 域名 类型 | |
local domain="$1" | |
local type="$(echo "$2" | tr 'a-z' 'A-Z')" | |
debug "LocalDns $domain $type" 1>&2 | |
# resolvectl 不需要 type 编号 | |
local resolveInfo | |
resolveInfo=$(resolvectl query --type=${type} --legend=false ${domain}) | |
debug "${resolveInfo}" 1>&2 | |
if [ -z "${resolveInfo}" ]; then | |
echoRed "resolvectl 无解析结果" 1>&2 | |
return 1 | |
fi | |
# 解析成功 取第一个 | |
echo "${resolveInfo}" | head -1 | awk '{print $4}' | |
} | |
function CheckEndpoint { # 获取目标是否为可用主机 输入 ip | |
local ip="$1" | |
local fingerPrint=$(ssh-keyscan -T 3 -t "${sshPublicKeyType}" -p "${sshPort}" "$ip" 2>/dev/null | ssh-keygen -l -E sha256 -f - 2>/dev/null | cut -d ' ' -f 2 | cut -d ':' -f 2) | |
if [ "$fingerPrint" == "$sshPublicKey" ]; then | |
echoGreen "${ip} 有效" 1>&2 | |
return 0 | |
fi | |
echoRed "${ip} 无效" 1>&2 | |
return 1 | |
} | |
function GetDomainIp { # 获取IP并进行检查 返回检查成功的IP | |
for domain in "${domains[@]}"; do | |
# echo $domain | |
echoBlue "尝试域名 ${domain}" 1>&2 | |
echoBlue "查询AliDns..." 1>&2 | |
local aliIp=$(GetAliDns $domain $recordType) | |
if [ -n "$aliIp" ]; then # 获得可能可用IP | |
CheckEndpoint $aliIp | |
if [ "$?" -eq 0 ]; then # 可用 | |
echo "$aliIp" | |
return 0 | |
fi | |
fi | |
echoBlue "查询TencentDns..." 1>&2 | |
local tencentIp=$(GetTencentDns $domain $recordType) | |
if [ -n "$tencentIp" ]; then # 获得可能可用IP | |
CheckEndpoint $tencentIp | |
if [ "$?" -eq 0 ]; then # 可用 | |
echo "$tencentIp" | |
return 0 | |
fi | |
fi | |
echoBlue "查询本地DNS..." 1>&2 | |
local localIp=$(GetLocalDns $domain $recordType) | |
if [ -n "$localIp" ]; then # 获得可能可用IP | |
CheckEndpoint $localIp | |
if [ "$?" -eq 0 ]; then # 可用 | |
echo "$localIp" | |
return 0 | |
fi | |
fi | |
echoBlue "查询CloudflareDNS..." 1>&2 | |
local cloudflareIp=$(GetCloudflareDns $domain $recordType) | |
if [ -n "$cloudflareIp" ]; then # 获得可能可用IP | |
CheckEndpoint $cloudflareIp | |
if [ "$?" -eq 0 ]; then # 可用 | |
echo "$cloudflareIp" | |
return 0 | |
fi | |
fi | |
echoRed "域名 ${domain}失败" 1>&2 | |
done | |
echoRed "域名全部失败" 1>&2 | |
return 1 | |
} | |
function Main { | |
dateString=$(date "+%m-%d %H:%M:%S") | |
echo "$dateString" 1>&2 | |
# 检测接口是否启动 | |
ip link show "${interfaceName}" >/dev/null 2>/dev/null | |
if [ $? -ne 0 ]; then | |
echoRed "找不到接口" 1>&2 | |
return | |
fi | |
echoGreen "测试对端连通性..." 1>&2 | |
ping -c ${pingCount} -W ${pingTimeout} $pingIp 1>&2 | |
if [ $? -eq 0 ]; then | |
echoGreen "可以联通 退出" 1>&2 | |
return | |
fi | |
# 需要重设 | |
echoRed "无法联通" 1>&2 | |
currentEndpoint=$(wg show "${interfaceName}" endpoints | grep "^${publicKey}" | awk '{print $2}' | head -1) | |
if [ -z "$currentEndpoint" ]; then | |
echoRed "找不到对端 退出" 1>&2 | |
return | |
fi | |
echoGreen "当前对端 ${currentEndpoint}" 1>&2 | |
ipv6Address=$(echo "$currentEndpoint" | grep -o '\[.*\]') | |
debug "可能的 ipv6 ${ipv6Address}" 1>&2 | |
if [ -z "$ipv6Address" ]; then # 使用 ipv4 | |
debug "ipv4 对端" 1>&2 | |
currentIp=$(echo "$currentEndpoint" | cut -d ':' -f 1) | |
currentPort=$(echo "$currentEndpoint" | cut -d ':' -f 2) | |
else # 使用 ipv6 | |
debug "ipv6 对端" 1>&2 | |
currentIp=$(echo "${ipv6Address}" | tr -d '[]') | |
currentPort=$(echo "$currentEndpoint" | sed -e 's/.*://g') | |
fi | |
echoGreen "当前IP ${currentIp} 当前端口 ${currentPort}" 1>&2 | |
if [[ -z "${currentIp}" || -z "${currentPort}" ]]; then | |
echoRed "找不到对端IP或端口 退出" 1>&2 | |
return | |
fi | |
# 已有目前的 对端 IP 端口 | |
newIp=$(GetDomainIp) | |
if [[ $? -ne 0 || -z "$newIp" ]]; then # 为空或者执行失败 | |
echoRed "找不到可用IP 退出" 1>&2 | |
return | |
fi | |
# 找到可用地址 | |
if [ "$recordType" == 'AAAA' ]; then | |
newIp="[${newIp}]" | |
fi | |
# 更改接口地址 | |
wg set $interfaceName peer $publicKey endpoint "${newIp}:${currentPort}" | |
sleep 5 | |
# 测试对端连接性 | |
ping -c ${pingCount} -W ${pingTimeout} $pingIp 1>&2 | |
if [ $? -eq 0 ]; then | |
echoGreen "联通成功" 1>&2 | |
else | |
echoRed "联通失败" 1>&2 | |
fi | |
} | |
Main | |
# 1-59/2 * * * * /root/bin/cronjob/wgDynamic.sh >/dev/null 2>&1 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment