Skip to content

Instantly share code, notes, and snippets.

@pd12bbf7608ae1
Created January 20, 2024 13:51
Show Gist options
  • Save pd12bbf7608ae1/6bb7bd62ab24038e34e64a4eeb8aa011 to your computer and use it in GitHub Desktop.
Save pd12bbf7608ae1/6bb7bd62ab24038e34e64a4eeb8aa011 to your computer and use it in GitHub Desktop.
Wireguard Dynamic
#!/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