Add Cloudflare DDNS updater scripts and systemd configurations
This commit is contained in:
0
dyndns/.env.example
Normal file
0
dyndns/.env.example
Normal file
76
dyndns/cf-ddns.sh
Normal file
76
dyndns/cf-ddns.sh
Normal file
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env bash
|
||||
# cf-ddns.sh - Cloudflare DDNS updater
|
||||
set -euo pipefail
|
||||
|
||||
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
if [ -f "${DIR}/.env" ]; then
|
||||
# shellcheck disable=SC1091
|
||||
source "${DIR}/.env"
|
||||
else
|
||||
echo "$(date -Iseconds) ERROR: missing ${DIR}/.env (create from .env.example)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
: "${CF_API_TOKEN:?need CF_API_TOKEN in .env}"
|
||||
: "${ZONE:?need ZONE in .env}"
|
||||
: "${RECORDS:?need RECORDS in .env}"
|
||||
: "${IP_URL:=https://ipv4.icanhazip.com}"
|
||||
: "${PROXIED:=false}"
|
||||
|
||||
AUTH_HEADER="Authorization: Bearer ${CF_API_TOKEN}"
|
||||
CT_HEADER="Content-Type: application/json"
|
||||
|
||||
log() { echo "$(date -Iseconds) $*"; }
|
||||
|
||||
IP=$(curl -fsS "${IP_URL}" | tr -d '[:space:]')
|
||||
if [[ -z "${IP}" ]]; then
|
||||
log "ERROR: could not determine public IP"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ZONE_ID=$(curl -fsS -X GET "https://api.cloudflare.com/client/v4/zones?name=${ZONE}" \
|
||||
-H "${AUTH_HEADER}" -H "${CT_HEADER}" | jq -r '.result[0].id // empty')
|
||||
|
||||
if [[ -z "${ZONE_ID}" ]]; then
|
||||
log "ERROR: zone not found: ${ZONE}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
IFS=',' read -r -a RECORD_ARR <<< "${RECORDS}"
|
||||
for fullrec in "${RECORD_ARR[@]}"; do
|
||||
rec=$(echo "${fullrec}" | xargs)
|
||||
log "Processing ${rec}"
|
||||
info=$(curl -fsS -X GET "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records?name=${rec}&type=A" \
|
||||
-H "${AUTH_HEADER}" -H "${CT_HEADER}")
|
||||
|
||||
RECORD_ID=$(echo "$info" | jq -r '.result[0].id // empty')
|
||||
CURRENT_CONTENT=$(echo "$info" | jq -r '.result[0].content // empty')
|
||||
|
||||
if [[ -z "${RECORD_ID}" ]]; then
|
||||
log "Creating ${rec} -> ${IP}"
|
||||
create_resp=$(curl -fsS -X POST "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records" \
|
||||
-H "${AUTH_HEADER}" -H "${CT_HEADER}" \
|
||||
--data "{\"type\":\"A\",\"name\":\"${rec}\",\"content\":\"${IP}\",\"ttl\":1,\"proxied\":${PROXIED}}")
|
||||
ok=$(echo "$create_resp" | jq -r '.success // false')
|
||||
if [[ "$ok" != "true" ]]; then
|
||||
log "ERROR creating ${rec}: ${create_resp}"
|
||||
else
|
||||
log "Created ${rec}"
|
||||
fi
|
||||
else
|
||||
if [[ "${CURRENT_CONTENT}" == "${IP}" ]]; then
|
||||
log "${rec} unchanged (${IP})"
|
||||
else
|
||||
log "Updating ${rec} -> ${IP}"
|
||||
upd_resp=$(curl -fsS -X PUT "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records/${RECORD_ID}" \
|
||||
-H "${AUTH_HEADER}" -H "${CT_HEADER}" \
|
||||
--data "{\"type\":\"A\",\"name\":\"${rec}\",\"content\":\"${IP}\",\"ttl\":1,\"proxied\":${PROXIED}}")
|
||||
ok=$(echo "$upd_resp" | jq -r '.success // false')
|
||||
if [[ "$ok" != "true" ]]; then
|
||||
log "ERROR updating ${rec}: ${upd_resp}"
|
||||
else
|
||||
log "Updated ${rec}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
19
dyndns/deploy.sh
Normal file
19
dyndns/deploy.sh
Normal file
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
# deploy.sh - copy systemd units and on_boot entry into place, reload systemd, enable timers
|
||||
set -euo pipefail
|
||||
|
||||
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SYSTEMD_DIR="/etc/systemd/system"
|
||||
|
||||
echo "Deploying from ${REPO_DIR}"
|
||||
|
||||
if [[ -d "${REPO_DIR}/systemd" ]]; then
|
||||
cp -f "${REPO_DIR}/systemd/"* "${SYSTEMD_DIR}/"
|
||||
fi
|
||||
|
||||
chmod +x "${REPO_DIR}/cf-ddns.sh"
|
||||
systemctl daemon-reload || true
|
||||
systemctl enable --now cf-ddns.timer || true
|
||||
systemctl enable --now git-sync.timer || true
|
||||
|
||||
echo "Deployment complete."
|
||||
29
dyndns/git-sync.sh
Normal file
29
dyndns/git-sync.sh
Normal file
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env bash
|
||||
# git-sync.sh - pull changes and run deploy.sh if updated
|
||||
set -euo pipefail
|
||||
|
||||
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
cd "${REPO_DIR}"
|
||||
|
||||
git fetch --all --prune
|
||||
|
||||
UPDATED=0
|
||||
if git rev-parse --abbrev-ref --symbolic-full-name @{u} >/dev/null 2>&1; then
|
||||
LOCAL="$(git rev-parse @)"
|
||||
REMOTE="$(git rev-parse @{u})"
|
||||
if [ "${LOCAL}" != "${REMOTE}" ]; then
|
||||
UPDATED=1
|
||||
fi
|
||||
else
|
||||
out="$(git pull --rebase 2>&1 || true)"
|
||||
if ! echo "${out}" | grep -q "Already up"; then
|
||||
UPDATED=1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "${UPDATED}" -eq 1 ]; then
|
||||
echo "$(date -Iseconds) Repo updated -> running deploy.sh"
|
||||
"${REPO_DIR}/deploy.sh"
|
||||
else
|
||||
echo "$(date -Iseconds) Repo unchanged"
|
||||
fi
|
||||
9
dyndns/systemd/cf-ddns.service
Normal file
9
dyndns/systemd/cf-ddns.service
Normal file
@@ -0,0 +1,9 @@
|
||||
[Unit]
|
||||
Description=Cloudflare DDNS updater
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/opt/cloudflare-ddns/cf-ddns.sh
|
||||
WorkingDirectory=/opt/cloudflare-ddns
|
||||
10
dyndns/systemd/cf-ddns.timer
Normal file
10
dyndns/systemd/cf-ddns.timer
Normal file
@@ -0,0 +1,10 @@
|
||||
[Unit]
|
||||
Description=Run Cloudflare DDNS every 5 minutes
|
||||
|
||||
[Timer]
|
||||
OnBootSec=1min
|
||||
OnUnitActiveSec=5min
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
9
dyndns/systemd/git-sync.service
Normal file
9
dyndns/systemd/git-sync.service
Normal file
@@ -0,0 +1,9 @@
|
||||
[Unit]
|
||||
Description=Git sync for cloudflare-ddns repo
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
WorkingDirectory=/opt/cloudflare-ddns
|
||||
ExecStart=/opt/cloudflare-ddns/git-sync.sh
|
||||
10
dyndns/systemd/git-sync.timer
Normal file
10
dyndns/systemd/git-sync.timer
Normal file
@@ -0,0 +1,10 @@
|
||||
[Unit]
|
||||
Description=Run git-sync every 1 minute
|
||||
|
||||
[Timer]
|
||||
OnBootSec=30s
|
||||
OnUnitActiveSec=60s
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
Reference in New Issue
Block a user