Compare commits
14 Commits
5c219e8f96
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 3bed4c4bbc | |||
| 2060f8379f | |||
| d0b389a4e6 | |||
| d2716eda75 | |||
| 7c1d05b5ed | |||
| af72435df2 | |||
| 532e63e038 | |||
| 3d2a0602e4 | |||
| a7b5490ea9 | |||
| 00dfd4bc02 | |||
| 6722b2b23e | |||
| 5fd6d6996e | |||
| 472b2ea2c8 | |||
| 0d4e612433 |
@@ -1,2 +1,3 @@
|
|||||||
DIGITALOCEAN_TOKEN
|
DIGITALOCEAN_TOKEN
|
||||||
DIGITALOCEAN_DOMAIN
|
DIGITALOCEAN_DOMAIN
|
||||||
|
REPEAT_CHECK_*
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
# digitalocean_dyndns
|
||||||
|
|
||||||
|
Forked from [dc25/digitalocean_dyndns](https://github.com/dc25/digitalocean_dyndns).
|
||||||
|
|
||||||
|
This script automatically keeps your DNS records for a HTTPS website working
|
||||||
|
when you're hosting from a network with a dynamically allocated IP address, such
|
||||||
|
as a typical home network.
|
||||||
|
|
||||||
|
To use this script, first mark the shell files as runnable:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
chmod +x ./dyndns.sh
|
||||||
|
chmod +x ./get_ip_addr.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Then to set things up, first write your domain name, e.g. `example.com`, to
|
||||||
|
`DIGITALOCEAN_DOMAIN`. Secondly, go to your DigitalOcean account and generate a
|
||||||
|
Personal Access Token with scopes for creating, reading, and deleting domains.
|
||||||
|
Write this to `DIGITALOCEAN_TOKEN`.
|
||||||
|
|
||||||
|
The script should now run. Try it out:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./dyndns.sh 4 # for IPv$
|
||||||
|
./dyndns.sh 6 # for IPv$
|
||||||
|
```
|
||||||
|
|
||||||
|
Go to the control panel in DigitalOcean for your domain, and check the A and
|
||||||
|
AAAA records are what you expect. If they are, the next step is to get this
|
||||||
|
script to run regularly.
|
||||||
|
|
||||||
|
You could do this in a number of ways. Probably the easiest is with crontab.
|
||||||
|
Run `crontab -e` to open your crontab for editing, and add these two lines:
|
||||||
|
|
||||||
|
```cron
|
||||||
|
* * * * * /home/.../dyndns.sh 4 > /tmp/dyndns.sh.4.log 2>&1
|
||||||
|
* * * * * /home/.../dyndns.sh 6 > /tmp/dyndns.sh.6.log 2>&1
|
||||||
|
```
|
||||||
|
|
||||||
|
Wait one minute, and check the logs. If you want to see that it works, delete
|
||||||
|
the REPEAT_CHECK_A and REPEAT_CHECK_AAAA files and wait for the script to run
|
||||||
|
again. You may also want to have an MTA set up, as cron will try to send any
|
||||||
|
unhandled output to your inbox.
|
||||||
|
|
||||||
|
If you want to use this script for more than one domain, you can supply a domain
|
||||||
|
name as a second argument. For example:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./dyndns.sh 4 example.com
|
||||||
|
```
|
||||||
-93
@@ -1,93 +0,0 @@
|
|||||||
#! /bin/bash
|
|
||||||
|
|
||||||
# Work with DigitalOcean api to update "A" record for a domain.
|
|
||||||
# Store DigitalOcean API token in file : DIGITALOCEAN_TOKEN
|
|
||||||
# Store domain to be updated in file : DIGITALOCEAN_DOMAIN
|
|
||||||
|
|
||||||
SCRIPT="${BASH_SOURCE[0]}"
|
|
||||||
SCRIPT_DIR=$(builtin cd "${SCRIPT%/*}" && pwd )
|
|
||||||
SCRIPT_NAME=${SCRIPT##*/}
|
|
||||||
|
|
||||||
REPEAT_CHECK_DIR=$HOME/repeat_check
|
|
||||||
REPEAT_CHECK=$REPEAT_CHECK_DIR/$SCRIPT_NAME # file to save argument in if successful
|
|
||||||
|
|
||||||
DIGITALOCEAN_DOMAIN=`cat $SCRIPT_DIR/DIGITALOCEAN_DOMAIN`
|
|
||||||
DIGITALOCEAN_TOKEN=`cat $SCRIPT_DIR/DIGITALOCEAN_TOKEN`
|
|
||||||
|
|
||||||
# Get IP from command line if available. Otherwise use curl to get public IP.
|
|
||||||
IP=""
|
|
||||||
if [ "$#" == "1" ]; then
|
|
||||||
IP=$1
|
|
||||||
else
|
|
||||||
IP=`curl -4 icanhazip.com 2> /dev/null`
|
|
||||||
fi
|
|
||||||
|
|
||||||
# If $IP does not look like a valid address then complain and exit.
|
|
||||||
# For regular expression, thank you to : https://tecadmin.net/shell-script-validate-ipv4-addresses/
|
|
||||||
if ! [[ $IP =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
|
|
||||||
echo Invalid IP address format: $IP
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
get_a_record_ids() {
|
|
||||||
curl -X GET \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \
|
|
||||||
"https://api.digitalocean.com/v2/domains/$DIGITALOCEAN_DOMAIN/records" 2> /dev/null | jq '.domain_records | map(select(.type == "A" and .name == "@")) | map(.id) | .[]'
|
|
||||||
}
|
|
||||||
|
|
||||||
add_a_record() {
|
|
||||||
curl -X POST \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \
|
|
||||||
-d "{\"type\":\"A\" \
|
|
||||||
,\"name\":\"@\" \
|
|
||||||
,\"data\":\"$IP\" \
|
|
||||||
,\"priority\":null \
|
|
||||||
,\"port\":null \
|
|
||||||
,\"ttl\":1800 \
|
|
||||||
,\"weight\":null \
|
|
||||||
,\"flags\":null \
|
|
||||||
,\"tag\":null \
|
|
||||||
}" \
|
|
||||||
"https://api.digitalocean.com/v2/domains/$DIGITALOCEAN_DOMAIN/records" 2> /dev/null | jq '.domain_record.id'
|
|
||||||
}
|
|
||||||
|
|
||||||
delete_record_by_id() {
|
|
||||||
echo deleting existing A record for $DIGITALOCEAN_DOMAIN
|
|
||||||
curl -X DELETE \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \
|
|
||||||
"https://api.digitalocean.com/v2/domains/$DIGITALOCEAN_DOMAIN/records/$1" > /dev/null 2>&1
|
|
||||||
}
|
|
||||||
|
|
||||||
replace_a_record() {
|
|
||||||
for id in `get_a_record_ids`; do
|
|
||||||
delete_record_by_id $id
|
|
||||||
done
|
|
||||||
|
|
||||||
new_record_id=`add_a_record`
|
|
||||||
if [ "$new_record_id" == "null" ] ; then
|
|
||||||
echo unable to add new A record for $DIGITALOCEAN_DOMAIN with value : $1
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
echo added new A record for $DIGITALOCEAN_DOMAIN with value : $1
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# exit if previous successful call was with same argument
|
|
||||||
if [[ -e $REPEAT_CHECK ]]; then
|
|
||||||
PREVIOUS_IP=`cat $REPEAT_CHECK`
|
|
||||||
if [[ "$IP" == "$PREVIOUS_IP" ]]; then
|
|
||||||
echo previous update was also with argument: \"$IP\" so exiting now to avoid duplicate work.
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# attempt to replace A record and, if successful, save arguments to check against next time
|
|
||||||
if replace_a_record $IP; then
|
|
||||||
# if successful then save arguments.
|
|
||||||
echo update worked so saving arguments \"$IP\" in $REPEAT_CHECK
|
|
||||||
mkdir -p $REPEAT_CHECK_DIR
|
|
||||||
echo "$IP" > $REPEAT_CHECK
|
|
||||||
fi
|
|
||||||
@@ -0,0 +1,149 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Work with DigitalOcean api to update "A" and "AAAA" records for a domain.
|
||||||
|
# Store DigitalOcean API token in file : DIGITALOCEAN_TOKEN
|
||||||
|
# Store domain to be updated in file : DIGITALOCEAN_DOMAIN
|
||||||
|
|
||||||
|
ip_version="$1"
|
||||||
|
if [ -z "${ip_version:-}" ] || ([ "$ip_version" != 4 ] && [ "$ip_version" != 6 ]); then
|
||||||
|
echo "Error: no valid IP version provided" >&2
|
||||||
|
echo "Usage: dyndns.bash <4|6> [domain]" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
SCRIPT="$0"
|
||||||
|
SCRIPT_DIR=$(cd "${SCRIPT%/*}" && pwd )
|
||||||
|
SCRIPT_NAME=${SCRIPT##*/}
|
||||||
|
CACHE_DIR=${CACHE_DIR:-~/.cache/dyndns}
|
||||||
|
CONFIG_DIR=${CONFIG_DIR:-~/.config/dyndns}
|
||||||
|
mkdir -p ${CACHE_DIR}
|
||||||
|
mkdir -p ${CONFIG_DIR}
|
||||||
|
|
||||||
|
IP="$($SCRIPT_DIR/get_ip_addr.sh $ip_version)"
|
||||||
|
if [ -z "$IP" ]; then
|
||||||
|
echo "Error: could not find IP address" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
DIGITALOCEAN_TOKEN=`cat ${CONFIG_DIR}/DIGITALOCEAN_TOKEN`
|
||||||
|
DOMAIN=${2:-`cat ${CONFIG_DIR}/DIGITALOCEAN_DOMAIN`}
|
||||||
|
SUBDOMAIN=`echo $DOMAIN | sed "s/[^.]\+\.[^.]\+$//g"`
|
||||||
|
if [ -z "$SUBDOMAIN" ]; then
|
||||||
|
SUBDOMAIN="@"
|
||||||
|
ROOT_DOMAIN=$DOMAIN
|
||||||
|
elif [ "$SUBDOMAIN" = "@." ]; then
|
||||||
|
SUBDOMAIN="@"
|
||||||
|
ROOT_DOMAIN=`echo $DOMAIN | sed "s/^@.//"`
|
||||||
|
else
|
||||||
|
SUBDOMAIN=`echo $SUBDOMAIN | sed "s/\.$//g"`
|
||||||
|
ROOT_DOMAIN=`echo $DOMAIN | sed "s/^${SUBDOMAIN}\.//g"`
|
||||||
|
fi
|
||||||
|
|
||||||
|
REPEAT_CHECK_DIR=${CACHE_DIR}
|
||||||
|
REPEAT_CHECK_A=$REPEAT_CHECK_DIR/REPEAT_CHECK_${SUBDOMAIN}.${ROOT_DOMAIN}_A
|
||||||
|
REPEAT_CHECK_AAAA=$REPEAT_CHECK_DIR/REPEAT_CHECK_${SUBDOMAIN}.${ROOT_DOMAIN}_AAAA
|
||||||
|
REPEAT_CHECK="$([ $ip_version = 4 ] && echo $REPEAT_CHECK_A || echo $REPEAT_CHECK_AAAA)"
|
||||||
|
|
||||||
|
get_a_record_ids() {
|
||||||
|
curl -X GET \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \
|
||||||
|
"https://api.digitalocean.com/v2/domains/$ROOT_DOMAIN/records" 2> /dev/null | jq '.domain_records | map(select(.type == "A" and .name == "$SUBDOMAIN")) | map(.id) | .[]'
|
||||||
|
}
|
||||||
|
|
||||||
|
get_aaaa_record_ids() {
|
||||||
|
curl -X GET \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \
|
||||||
|
"https://api.digitalocean.com/v2/domains/$ROOT_DOMAIN/records" 2> /dev/null | jq '.domain_records | map(select(.type == "AAAA" and .name == "$SUBDOMAIN")) | map(.id) | .[]'
|
||||||
|
}
|
||||||
|
|
||||||
|
add_a_record() {
|
||||||
|
curl -X POST \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \
|
||||||
|
-d "{\"type\":\"A\" \
|
||||||
|
,\"name\":\"$SUBDOMAIN\" \
|
||||||
|
,\"data\":\"$IP\" \
|
||||||
|
,\"priority\":null \
|
||||||
|
,\"port\":null \
|
||||||
|
,\"ttl\":1800 \
|
||||||
|
,\"weight\":null \
|
||||||
|
,\"flags\":null \
|
||||||
|
,\"tag\":null \
|
||||||
|
}" \
|
||||||
|
"https://api.digitalocean.com/v2/domains/$ROOT_DOMAIN/records" 2> /dev/null | jq '.domain_record.id'
|
||||||
|
}
|
||||||
|
|
||||||
|
add_aaaa_record() {
|
||||||
|
curl -X POST \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \
|
||||||
|
-d "{\"type\":\"AAAA\" \
|
||||||
|
,\"name\":\"$SUBDOMAIN\" \
|
||||||
|
,\"data\":\"$IP\" \
|
||||||
|
,\"priority\":null \
|
||||||
|
,\"port\":null \
|
||||||
|
,\"ttl\":1800 \
|
||||||
|
,\"weight\":null \
|
||||||
|
,\"flags\":null \
|
||||||
|
,\"tag\":null \
|
||||||
|
}" \
|
||||||
|
"https://api.digitalocean.com/v2/domains/$ROOT_DOMAIN/records" 2> /dev/null | jq '.domain_record.id'
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_record_by_id() {
|
||||||
|
echo deleting existing A/AAAA record for ${SUBDOMAIN}.${ROOT_DOMAIN}
|
||||||
|
curl -X DELETE \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \
|
||||||
|
"https://api.digitalocean.com/v2/domains/$ROOT_DOMAIN/records/$1" > /dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
replace_a_records() {
|
||||||
|
for id in `get_a_record_ids`; do
|
||||||
|
delete_record_by_id $id
|
||||||
|
done
|
||||||
|
|
||||||
|
new_record_id=`add_a_record`
|
||||||
|
if [ "$new_record_id" = "null" ] ; then
|
||||||
|
echo unable to add new A record for ${SUBDOMAIN}.${ROOT_DOMAIN} with value: $IP
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
echo added new A record for ${SUBDOMAIN}.${ROOT_DOMAIN} with value: $IP
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
replace_aaaa_records() {
|
||||||
|
for id in `get_aaaa_record_ids`; do
|
||||||
|
delete_record_by_id $id
|
||||||
|
done
|
||||||
|
|
||||||
|
new_record_id=`add_aaaa_record`
|
||||||
|
if [ "$new_record_id" = "null" ] ; then
|
||||||
|
echo unable to add new AAAA record for ${SUBDOMAIN}.${ROOT_DOMAIN} with value: $IP
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
echo added new AAAA record for ${SUBDOMAIN}.${ROOT_DOMAIN} with value: $IP
|
||||||
|
}
|
||||||
|
|
||||||
|
replace_records() {
|
||||||
|
[ $ip_version = 4 ] && replace_a_records || replace_aaaa_records
|
||||||
|
}
|
||||||
|
|
||||||
|
# exit if previous successful call was with same argument
|
||||||
|
if [ -e $REPEAT_CHECK ]; then
|
||||||
|
PREVIOUS_IP=`cat $REPEAT_CHECK`
|
||||||
|
if [ "$IP" = "$PREVIOUS_IP" ]; then
|
||||||
|
echo previous update was also with argument: \"$IP\" so exiting now to avoid duplicate work.
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# attempt to replace records and, if successful, save arguments to check against next time
|
||||||
|
if replace_records $IP; then
|
||||||
|
# if successful then save arguments.
|
||||||
|
echo update worked so saving arguments \"$IP\" in $REPEAT_CHECK
|
||||||
|
mkdir -p $REPEAT_CHECK_DIR
|
||||||
|
echo "$IP" > $REPEAT_CHECK
|
||||||
|
fi
|
||||||
Executable
+52
@@ -0,0 +1,52 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
ip_version="$1"
|
||||||
|
if [ -z "${ip_version:-}" ] || ([ "$ip_version" != 4 ] && [ "$ip_version" != 6 ]); then
|
||||||
|
echo "Usage: get_ip_addr <4|6>" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $ip_version = 4 ]; then
|
||||||
|
dig +short myip.opendns.com @resolver1.opendns.com \
|
||||||
|
&& exit 0 \
|
||||||
|
|| exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
CONN_NAME=${CONN_NAME:-nevis}
|
||||||
|
CONN_DEVICE_NAME="${CONN_DEVICE_NAME:-$(nmcli --fields NAME,DEVICE con show | grep ^$CONN_NAME | sed 's/\s\+/ /g' | cut -d' ' -f 2)}"
|
||||||
|
if [ -z "${CONN_DEVICE_NAME:-}" ]; then
|
||||||
|
echo "Could not find the connection device name for connection: \"$CONN_NAME\"" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
device_line_no="$(ip addr | grep -En ^[0-9]+:\ $CONN_DEVICE_NAME | cut -d':' -f 1)"
|
||||||
|
if [ -z "${device_line_no:-}" ]; then
|
||||||
|
echo "Could not find device: \"$CONN_DEVICE_NAME\" in output of ip addr" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ip_addr_output_for_device="$(ip addr | tail -n +$(($device_line_no + 1)))"
|
||||||
|
|
||||||
|
device_no="$(ip addr | tail -n +$device_line_no | head -n 1 | cut -d':' -f 1)"
|
||||||
|
next_device_no=$(($device_no + 1))
|
||||||
|
next_device_line_no="$(ip addr | grep -En ^${next_device_no}:\ | cut -d':' -f 1)"
|
||||||
|
if [ -n "${next_device_line_no:-}" ]; then
|
||||||
|
len=$(($next_device_line_no - ($device_line_no + 1)))
|
||||||
|
ip_addr_output_for_device="$(echo $ip_addr_output_for_device | head -n $len)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
ipv4_regex="([a-z0-9]{1,3}\.){3}[a-z0-9]{1,3}"
|
||||||
|
ipv6_regex="([a-z0-9]{1,4}:){7}[a-z0-9]{1,4}"
|
||||||
|
if [ "$ip_version" = "4" ]; then
|
||||||
|
ip_regex="$ipv4_regex"
|
||||||
|
else
|
||||||
|
ip_regex="$ipv6_regex"
|
||||||
|
fi
|
||||||
|
ip="$(echo "$ip_addr_output_for_device" | grep "scope global" | grep -oE $ip_regex | head -n 1)"
|
||||||
|
if [ -z "${ip:-}" ]; then
|
||||||
|
echo "Could not find global IP in output of ip addr under device: $CONN_DEVICE_NAME ($CONN_NAME)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$ip"
|
||||||
Reference in New Issue
Block a user