feat: dynamic DNS updater sidecar (auto-updates A + AAAA every 5min)
- update-dns.sh: detects public IPv4/IPv6, upserts CF records - Runs on container start + every 5 minutes - Only updates if IP actually changed (skips if unchanged) - python:3-alpine container with curl Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -82,6 +82,18 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- backend
|
- backend
|
||||||
|
|
||||||
|
# ─── Dynamic DNS updater (keeps A + AAAA current) ───
|
||||||
|
dns-updater:
|
||||||
|
image: python:3-alpine
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./update-dns.sh:/update-dns.sh:ro
|
||||||
|
secrets:
|
||||||
|
- cf_api_token
|
||||||
|
entrypoint: ["/bin/sh", "/update-dns.sh"]
|
||||||
|
environment:
|
||||||
|
DNS_UPDATE_INTERVAL: "300" # 5 minutes
|
||||||
|
|
||||||
secrets:
|
secrets:
|
||||||
cf_api_token:
|
cf_api_token:
|
||||||
file: ./cf_api_token.txt
|
file: ./cf_api_token.txt
|
||||||
|
|||||||
98
warzone/deploy/docker/update-dns.sh
Executable file
98
warzone/deploy/docker/update-dns.sh
Executable file
@@ -0,0 +1,98 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Updates voip.manko.yoga DNS records with current public IPs.
|
||||||
|
# Runs once on startup, then every 5 minutes.
|
||||||
|
# Reads CF token from /run/secrets/cf_api_token or CF_API_TOKEN env.
|
||||||
|
|
||||||
|
DOMAIN="voip.manko.yoga"
|
||||||
|
ZONE="manko.yoga"
|
||||||
|
INTERVAL="${DNS_UPDATE_INTERVAL:-300}"
|
||||||
|
|
||||||
|
get_token() {
|
||||||
|
if [ -f /run/secrets/cf_api_token ]; then
|
||||||
|
cat /run/secrets/cf_api_token | tr -d '\n'
|
||||||
|
else
|
||||||
|
echo "$CF_API_TOKEN"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
get_zone_id() {
|
||||||
|
curl -s "https://api.cloudflare.com/client/v4/zones?name=$ZONE" \
|
||||||
|
-H "Authorization: Bearer $(get_token)" | \
|
||||||
|
python3 -c "import sys,json; print(json.load(sys.stdin)['result'][0]['id'])" 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
get_public_ipv4() {
|
||||||
|
curl -4 -s --connect-timeout 5 https://api.ipify.org 2>/dev/null || \
|
||||||
|
curl -4 -s --connect-timeout 5 https://ifconfig.me 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
get_public_ipv6() {
|
||||||
|
curl -6 -s --connect-timeout 5 https://api6.ipify.org 2>/dev/null || \
|
||||||
|
curl -6 -s --connect-timeout 5 https://ifconfig.co 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
upsert_record() {
|
||||||
|
local zone_id="$1" type="$2" content="$3" token
|
||||||
|
token=$(get_token)
|
||||||
|
|
||||||
|
# Check existing
|
||||||
|
local existing
|
||||||
|
existing=$(curl -s "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records?name=$DOMAIN&type=$type" \
|
||||||
|
-H "Authorization: Bearer $token")
|
||||||
|
|
||||||
|
local rec_id current
|
||||||
|
rec_id=$(echo "$existing" | python3 -c "import sys,json; r=json.load(sys.stdin)['result']; print(r[0]['id'] if r else '')" 2>/dev/null)
|
||||||
|
current=$(echo "$existing" | python3 -c "import sys,json; r=json.load(sys.stdin)['result']; print(r[0]['content'] if r else '')" 2>/dev/null)
|
||||||
|
|
||||||
|
if [ "$current" = "$content" ]; then
|
||||||
|
echo " $type: $content (unchanged)"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$rec_id" ]; then
|
||||||
|
curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records/$rec_id" \
|
||||||
|
-H "Authorization: Bearer $token" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
--data "{\"type\":\"$type\",\"name\":\"$DOMAIN\",\"content\":\"$content\",\"ttl\":120,\"proxied\":false}" > /dev/null
|
||||||
|
echo " $type: $current -> $content (updated)"
|
||||||
|
else
|
||||||
|
curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records" \
|
||||||
|
-H "Authorization: Bearer $token" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
--data "{\"type\":\"$type\",\"name\":\"$DOMAIN\",\"content\":\"$content\",\"ttl\":120,\"proxied\":false}" > /dev/null
|
||||||
|
echo " $type: $content (created)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
echo "[$(date -u +%H:%M:%S)] Checking DNS for $DOMAIN..."
|
||||||
|
local zone_id
|
||||||
|
zone_id=$(get_zone_id)
|
||||||
|
if [ -z "$zone_id" ]; then
|
||||||
|
echo " ERROR: cannot get zone ID"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
local ipv4 ipv6
|
||||||
|
ipv4=$(get_public_ipv4)
|
||||||
|
ipv6=$(get_public_ipv6)
|
||||||
|
|
||||||
|
if [ -n "$ipv4" ]; then
|
||||||
|
upsert_record "$zone_id" "A" "$ipv4"
|
||||||
|
else
|
||||||
|
echo " A: no IPv4 detected"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$ipv6" ]; then
|
||||||
|
upsert_record "$zone_id" "AAAA" "$ipv6"
|
||||||
|
else
|
||||||
|
echo " AAAA: no IPv6 detected"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run immediately, then loop
|
||||||
|
update
|
||||||
|
while true; do
|
||||||
|
sleep "$INTERVAL"
|
||||||
|
update
|
||||||
|
done
|
||||||
Reference in New Issue
Block a user