211 lines
4.8 KiB
Bash
Executable File
211 lines
4.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
DOMAIN="${GODADDY_DOMAIN:-bytelyst.com}"
|
|
TTL="${GODADDY_DNS_TTL:-600}"
|
|
TARGET_IP="${GODADDY_DNS_TARGET_IP:-}"
|
|
AUTO_IP=false
|
|
DRY_RUN=false
|
|
VALIDATE=false
|
|
HOSTS=("api" "gitea" "admin" "tracker")
|
|
|
|
CONFIG_FILE="${XDG_CONFIG_HOME:-$HOME/.config}/godaddypy/credentials.yaml"
|
|
|
|
usage() {
|
|
cat <<'USAGE'
|
|
Upsert ByteLyst GoDaddy A records for the Azure VM cutover.
|
|
|
|
Usage:
|
|
scripts/godaddy-sync-bytelyst-dns.sh [options]
|
|
|
|
Options:
|
|
--ip <address> Target IPv4 address for all managed A records
|
|
--auto-ip Detect the current public IP with api.ipify.org
|
|
--domain <domain> GoDaddy zone to update (default: bytelyst.com)
|
|
--ttl <seconds> TTL for the managed A records (default: 600)
|
|
--hosts <csv> Hostnames to manage (default: api,gitea,admin,tracker)
|
|
--validate Run dig validation after changes
|
|
--dry-run Print the API operations without applying them
|
|
-h, --help Show help
|
|
|
|
Credentials:
|
|
- Preferred: GODADDY_API_KEY and GODADDY_API_SECRET
|
|
- Fallback: ~/.config/godaddypy/credentials.yaml
|
|
|
|
Examples:
|
|
scripts/godaddy-sync-bytelyst-dns.sh --ip 20.81.12.34 --validate
|
|
scripts/godaddy-sync-bytelyst-dns.sh --auto-ip --dry-run
|
|
USAGE
|
|
}
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--ip)
|
|
TARGET_IP="${2:-}"
|
|
shift 2
|
|
;;
|
|
--auto-ip)
|
|
AUTO_IP=true
|
|
shift
|
|
;;
|
|
--domain)
|
|
DOMAIN="${2:-}"
|
|
shift 2
|
|
;;
|
|
--ttl)
|
|
TTL="${2:-}"
|
|
shift 2
|
|
;;
|
|
--hosts)
|
|
IFS=',' read -r -a HOSTS <<<"${2:-}"
|
|
shift 2
|
|
;;
|
|
--validate)
|
|
VALIDATE=true
|
|
shift
|
|
;;
|
|
--dry-run)
|
|
DRY_RUN=true
|
|
shift
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "Unknown arg: $1" >&2
|
|
usage
|
|
exit 2
|
|
;;
|
|
esac
|
|
done
|
|
|
|
trim() {
|
|
local value="$1"
|
|
value="${value#"${value%%[![:space:]]*}"}"
|
|
value="${value%"${value##*[![:space:]]}"}"
|
|
printf '%s' "$value"
|
|
}
|
|
|
|
load_credential_from_file() {
|
|
local key_name="$1"
|
|
|
|
if [[ ! -f "$CONFIG_FILE" ]]; then
|
|
return 1
|
|
fi
|
|
|
|
sed -nE "s/^[[:space:]]*${key_name}:[[:space:]]*['\"]?([^'\"]+)['\"]?[[:space:]]*$/\\1/p" "$CONFIG_FILE" | head -n 1
|
|
}
|
|
|
|
GODADDY_API_KEY="${GODADDY_API_KEY:-}"
|
|
GODADDY_API_SECRET="${GODADDY_API_SECRET:-}"
|
|
|
|
if [[ -z "$GODADDY_API_KEY" ]]; then
|
|
GODADDY_API_KEY="$(load_credential_from_file key || true)"
|
|
fi
|
|
|
|
if [[ -z "$GODADDY_API_SECRET" ]]; then
|
|
GODADDY_API_SECRET="$(load_credential_from_file secret || true)"
|
|
fi
|
|
|
|
if [[ -z "$GODADDY_API_KEY" || -z "$GODADDY_API_SECRET" ]]; then
|
|
echo "Missing GoDaddy credentials. Set GODADDY_API_KEY and GODADDY_API_SECRET, or configure $CONFIG_FILE." >&2
|
|
exit 2
|
|
fi
|
|
|
|
if [[ "$AUTO_IP" == true ]]; then
|
|
TARGET_IP="$(curl -fsS https://api.ipify.org)"
|
|
fi
|
|
|
|
if [[ -z "$TARGET_IP" ]]; then
|
|
echo "Missing target IP. Use --ip <address> or --auto-ip." >&2
|
|
exit 2
|
|
fi
|
|
|
|
if [[ ! "$TARGET_IP" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
|
|
echo "Target IP does not look like an IPv4 address: $TARGET_IP" >&2
|
|
exit 2
|
|
fi
|
|
|
|
if [[ ! "$TTL" =~ ^[0-9]+$ ]]; then
|
|
echo "TTL must be numeric: $TTL" >&2
|
|
exit 2
|
|
fi
|
|
|
|
if ! command -v curl >/dev/null 2>&1; then
|
|
echo "curl is required." >&2
|
|
exit 1
|
|
fi
|
|
|
|
api_request() {
|
|
local method="$1"
|
|
local path="$2"
|
|
local body="${3:-}"
|
|
|
|
local -a curl_args=(
|
|
-fsS
|
|
-X "$method"
|
|
-H "Authorization: sso-key ${GODADDY_API_KEY}:${GODADDY_API_SECRET}"
|
|
-H "Accept: application/json"
|
|
"https://api.godaddy.com${path}"
|
|
)
|
|
|
|
if [[ -n "$body" ]]; then
|
|
curl_args+=(-H "Content-Type: application/json" --data "$body")
|
|
fi
|
|
|
|
curl "${curl_args[@]}"
|
|
}
|
|
|
|
printf 'Domain: %s\n' "$DOMAIN"
|
|
printf 'Target IP: %s\n' "$TARGET_IP"
|
|
printf 'TTL: %s\n' "$TTL"
|
|
printf 'Hosts: %s\n' "$(IFS=,; printf '%s' "${HOSTS[*]}")"
|
|
|
|
for raw_host in "${HOSTS[@]}"; do
|
|
host="$(trim "$raw_host")"
|
|
if [[ -z "$host" ]]; then
|
|
continue
|
|
fi
|
|
|
|
path="/v1/domains/${DOMAIN}/records/A/${host}"
|
|
payload=$(printf '[{"data":"%s","ttl":%s}]' "$TARGET_IP" "$TTL")
|
|
|
|
echo
|
|
printf 'Record: %s.%s\n' "$host" "$DOMAIN"
|
|
|
|
if [[ "$DRY_RUN" == true ]]; then
|
|
printf 'Dry run: PUT %s %s\n' "$path" "$payload"
|
|
continue
|
|
fi
|
|
|
|
current_records="$(api_request GET "$path" || true)"
|
|
if [[ -n "$current_records" ]]; then
|
|
printf 'Existing A records: %s\n' "$current_records"
|
|
else
|
|
echo "Existing A records: none"
|
|
fi
|
|
|
|
api_request PUT "$path" "$payload" >/dev/null
|
|
echo "Updated A record."
|
|
done
|
|
|
|
if [[ "$VALIDATE" == true ]]; then
|
|
echo
|
|
echo "Validation:"
|
|
if ! command -v dig >/dev/null 2>&1; then
|
|
echo "dig not found; skipping DNS validation." >&2
|
|
exit 0
|
|
fi
|
|
|
|
for raw_host in "${HOSTS[@]}"; do
|
|
host="$(trim "$raw_host")"
|
|
if [[ -z "$host" ]]; then
|
|
continue
|
|
fi
|
|
|
|
resolved_ip="$(dig +short "${host}.${DOMAIN}" | tail -n 1)"
|
|
printf '%s.%s -> %s\n' "$host" "$DOMAIN" "${resolved_ip:-<no answer>}"
|
|
done
|
|
fi
|