aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Tuomi <jan@jantuomi.fi>2025-07-09 14:49:01 +0300
committerJan Tuomi <jan@jantuomi.fi>2025-07-09 14:49:01 +0300
commitb062211887bda338f14cb663c2b3392a52d0df88 (patch)
tree3dd9c3ae32b312c10854c05598a17313e3d6efd6
parent64f0adc8bb8d758c07fdd89b937466b1ab5ab43e (diff)
Add digitalocean dyndns rc script
-rw-r--r--playbook.yml1
-rw-r--r--tasks/general.yml2
-rw-r--r--tasks/network.yml38
-rw-r--r--templates/etc_rc.conf.j24
-rw-r--r--templates/usr_local_bin_do_dyndns.sh.j251
-rw-r--r--templates/usr_local_etc_do_dyndns_auth.j21
-rw-r--r--templates/usr_local_etc_rc.d_do_dyndns.j253
7 files changed, 150 insertions, 0 deletions
diff --git a/playbook.yml b/playbook.yml
index 8ceed7a..df8393b 100644
--- a/playbook.yml
+++ b/playbook.yml
@@ -20,6 +20,7 @@
dma_mail_hostname: "{{ lookup('env', 'DMA_MAILNAME') }}"
dma_to_address: "{{ lookup('env', 'DMA_TO_ADDRESS') }}"
jan_systems_html_dir: "{{ lookup('env', 'JAN_SYSTEMS_HTML_DIR') }}"
+ do_api_token: "{{ lookup('env', 'DO_API_TOKEN') }}"
lan_ipv4_cidr: 192.168.0.10/16
lan_ipv4_gateway: 192.168.0.1
lan_search_domain: local.jan.systems
diff --git a/tasks/general.yml b/tasks/general.yml
index 2131059..e9c1ef3 100644
--- a/tasks/general.yml
+++ b/tasks/general.yml
@@ -6,6 +6,8 @@
- rsync
- nginx
- dma
+ - jq
+ - curl
- name: Set up periodic.conf
template:
diff --git a/tasks/network.yml b/tasks/network.yml
index d68344d..45f44e6 100644
--- a/tasks/network.yml
+++ b/tasks/network.yml
@@ -104,3 +104,41 @@
owner: root
group: wheel
mode: "0644"
+
+- name: Set up /usr/local/etc/do_dyndns_auth
+ template:
+ src: usr_local_etc_do_dyndns_auth.j2
+ dest: /usr/local/etc/do_dyndns_auth
+ owner: root
+ group: wheel
+ mode: "0600"
+
+- name: Set up /usr/local/bin/do_dyndns.sh
+ template:
+ src: usr_local_bin_do_dyndns.sh.j2
+ dest: /usr/local/bin/do_dyndns.sh
+ owner: root
+ group: wheel
+ mode: "0755"
+ register: do_dyndns_sh
+
+- name: Set up /usr/local/etc/rc.d/do_dyndns
+ template:
+ src: usr_local_etc_rc.d_do_dyndns.j2
+ dest: /usr/local/etc/rc.d/do_dyndns
+ owner: root
+ group: wheel
+ mode: "0755"
+ register: do_dyndns_rc
+
+- name: Start do_dyndns
+ service:
+ name: do_dyndns
+ state: started
+ register: do_dyndns_start
+
+- name: Restart do_dyndns
+ service:
+ name: do_dyndns
+ state: restarted
+ when: not do_dyndns_start.changed and (do_dyndns_sh.changed or do_dyndns_rc.changed)
diff --git a/templates/etc_rc.conf.j2 b/templates/etc_rc.conf.j2
index 26ae032..8d21ff6 100644
--- a/templates/etc_rc.conf.j2
+++ b/templates/etc_rc.conf.j2
@@ -28,3 +28,7 @@ clear_tmp_enable="YES"
cleanvar_enable="YES"
auditd_enable="YES"
devd_enable="YES"
+do_dyndns_enable="YES"
+do_dyndns_domain="jan.systems"
+do_dyndns_hostname="pursotin"
+do_dyndns_if4="wan0"
diff --git a/templates/usr_local_bin_do_dyndns.sh.j2 b/templates/usr_local_bin_do_dyndns.sh.j2
new file mode 100644
index 0000000..8579ff3
--- /dev/null
+++ b/templates/usr_local_bin_do_dyndns.sh.j2
@@ -0,0 +1,51 @@
+#!/bin/sh
+set -eu
+
+: "${DO_DYNDNS_DOMAIN:?DO_DYNDNS_DOMAIN must be set}"
+: "${DO_DYNDNS_HOSTNAME:?DO_DYNDNS_HOSTNAME must be set}"
+: "${DO_DYNDNS_IF4:?DO_DYNDNS_IF4 must be set}"
+: "${DO_DYNDNS_INTERVAL:?DO_DYNDNS_INTERVAL must be set}"
+: "${DO_DYNDNS_AUTH_FILE:?DO_DYNDNS_AUTH_FILE must be set}"
+
+if [ ! -r "$DO_DYNDNS_AUTH_FILE" ]; then
+ echo "[ERROR] Auth file '$DO_DYNDNS_AUTH_FILE' not readable" >&2
+ exit 1
+fi
+
+DOMAIN="$DO_DYNDNS_DOMAIN"
+HOSTNAME="$DO_DYNDNS_HOSTNAME"
+IF4="$DO_DYNDNS_IF4"
+INTERVAL="$DO_DYNDNS_INTERVAL"
+
+while :; do
+ IP4=$(ifconfig "$IF4" | awk '/inet / { print $2 }' | head -n1)
+ DIGITALOCEAN_TOKEN=$(awk '{$1=$1; print}' "$DO_DYNDNS_AUTH_FILE")
+
+ echo "[INFO] Updating A record for $DOMAIN → $IP4"
+
+ RECORD_ID=$(curl -s -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \
+ "https://api.digitalocean.com/v2/domains/$DOMAIN/records?per_page=999999" |
+ jq -r ".domain_records[] | select(.type==\"A\" and .name==\"$HOSTNAME\") | .id")
+
+ if [ -n "$RECORD_ID" ]; then
+ curl -s -X PUT \
+ -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \
+ -H "Content-Type: application/json" \
+ -d "{\"data\":\"$IP4\"}" \
+ "https://api.digitalocean.com/v2/domains/$DOMAIN/records/$RECORD_ID"
+ echo "[INFO] Record $RECORD_ID updated"
+ else
+ echo "[WARN] No A record found for hostname \"$HOSTNAME\" in domain \"$DOMAIN\". Skipping."
+ fi
+
+ SLEEP_SEC=$(printf "%s\n" "$INTERVAL" | awk '
+ /^[0-9]+s$/ { gsub(/s$/, ""); print $0; exit }
+ /^[0-9]+m$/ { gsub(/m$/, ""); print $0 * 60; exit }
+ /^[0-9]+h$/ { gsub(/h$/, ""); print $0 * 3600; exit }
+ /^[0-9]+d$/ { gsub(/d$/, ""); print $0 * 86400; exit }
+ /^[0-9]+$/ { print $0; exit }
+ { print "invalid"; exit 1 }
+ ')
+
+ sleep "$SLEEP_SEC"
+done
diff --git a/templates/usr_local_etc_do_dyndns_auth.j2 b/templates/usr_local_etc_do_dyndns_auth.j2
new file mode 100644
index 0000000..2a9cbc5
--- /dev/null
+++ b/templates/usr_local_etc_do_dyndns_auth.j2
@@ -0,0 +1 @@
+{{ do_api_token }}
diff --git a/templates/usr_local_etc_rc.d_do_dyndns.j2 b/templates/usr_local_etc_rc.d_do_dyndns.j2
new file mode 100644
index 0000000..0198f9d
--- /dev/null
+++ b/templates/usr_local_etc_rc.d_do_dyndns.j2
@@ -0,0 +1,53 @@
+#!/bin/sh
+#
+# PROVIDE: do_dyndns
+# REQUIRE: NETWORKING
+# KEYWORD: shutdown
+
+. /etc/rc.subr
+
+name="do_dyndns"
+rcvar=do_dyndns_enable
+
+extra_commands="status"
+rcvars="do_dyndns_enable do_dyndns_domain do_dyndns_hostname do_dyndns_if4 do_dyndns_interval do_dyndns_auth_file"
+stop_cmd="do_dyndns_stop"
+
+load_rc_config $name
+
+: "${do_dyndns_enable:=NO}"
+: "${do_dyndns_interval:=5m}"
+: "${do_dyndns_hostname:=@}"
+: "${do_dyndns_auth_file:=/usr/local/etc/do_dyndns_auth}"
+
+pidfile="/var/run/${name}.pid"
+logfile="/var/log/${name}"
+
+command="/usr/sbin/daemon"
+command_args="-cfr -P ${pidfile} -o ${logfile} /bin/sh -c '
+ export PATH=\"/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin\"
+ export DO_DYNDNS_DOMAIN=\"${do_dyndns_domain}\"
+ export DO_DYNDNS_HOSTNAME=\"${do_dyndns_hostname}\"
+ export DO_DYNDNS_IF4=\"${do_dyndns_if4}\"
+ export DO_DYNDNS_INTERVAL=\"${do_dyndns_interval}\"
+ export DO_DYNDNS_AUTH_FILE=\"${do_dyndns_auth_file}\"
+
+ exec /usr/local/bin/do_dyndns.sh
+'"
+
+do_dyndns_stop() {
+ echo "Stopping ${name} with SIGINT..."
+ if [ -f "${pidfile}" ]; then
+ kill -INT "$(cat "${pidfile}")"
+ sleep 5
+ if kill -0 "$(cat "${pidfile}")" 2>/dev/null; then
+ echo "Process didn't exit, sending SIGKILL..."
+ kill -KILL "$(cat "${pidfile}")"
+ fi
+ rm -f "${pidfile}"
+ else
+ echo "${name}: no pidfile found"
+ fi
+}
+
+run_rc_command "$1"