From b062211887bda338f14cb663c2b3392a52d0df88 Mon Sep 17 00:00:00 2001 From: Jan Tuomi Date: Wed, 9 Jul 2025 14:49:01 +0300 Subject: Add digitalocean dyndns rc script --- playbook.yml | 1 + tasks/general.yml | 2 ++ tasks/network.yml | 38 ++++++++++++++++++++++ templates/etc_rc.conf.j2 | 4 +++ templates/usr_local_bin_do_dyndns.sh.j2 | 51 +++++++++++++++++++++++++++++ templates/usr_local_etc_do_dyndns_auth.j2 | 1 + templates/usr_local_etc_rc.d_do_dyndns.j2 | 53 +++++++++++++++++++++++++++++++ 7 files changed, 150 insertions(+) create mode 100644 templates/usr_local_bin_do_dyndns.sh.j2 create mode 100644 templates/usr_local_etc_do_dyndns_auth.j2 create mode 100644 templates/usr_local_etc_rc.d_do_dyndns.j2 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" -- cgit v1.3