plugins
plugins copied to clipboard
dns/ddclient: add AWS Route53
Important notices Before you add a new report, we ask you kindly to acknowledge the following:
- [x] I have read the contributing guide lines at https://github.com/opnsense/plugins/blob/master/CONTRIBUTING.md
- [x] I have searched the existing issues, open and closed, and I'm convinced that mine is new.
- [x] The title contains the plugin to which this issue belongs
Describe the bug re-opening #2892 because its still a bug despite the bot's opinion on the subject.
Original summary: Is your feature request related to a problem? Please describe. Legacy plugin set to be deprecated has Route53 as a provider option, but replacement plugin does not.
Describe the solution you'd like Add Route53 as a provider in the GUI.
Describe alternatives you've considered Use the legacy plugin but would like to switch to non-deprecated plugin.
To Reproduce Steps to reproduce the behavior:
- Go to 'ui/dyndns'
- Click on add button
- Service dropdown.
Expected behavior See Route53 or compatible option.
Screenshots

Environment OPNsense 22.7.2-amd64 FreeBSD 13.1-RELEASE-p1 OpenSSL 1.1.1q 5 Jul 2022
it's not a bug, it's a feature request, likely aimed in the wrong direction https://github.com/ddclient/ddclient/issues/422 https://github.com/awslabs/route53-dynamic-dns-with-lambda/issues/39
The plugin can only offer support for protocols handled by ddclient....
Ok, I think I understand what you're saying but there's still a disconnect somewhere. Users currently see this:

But there is no upgrade path which in my reading of things is a bug. Maybe the bug is in the legacy message, hopefully for me the "bug" is that the feature is pending... Somewhere there's a bug though.
also just realized that says "before 22.7 is released" which happened and the plugin still exists so I guess this is actually on someone's radar preventing the plugin's removal :laughing:
https://forum.opnsense.org/index.php?topic=26446.msg142428#msg142428 , upgrades shouldn't remove the old plugin by the way, at some point in time you just can't install it anymore from our repository on new installs (and we won't be offering any support)
I see. That does mitigate things but it means if I have to rebuild or upgrade my opnsense machine I'll be stranded without the feature or a long night of hacking to manually add it back.
... but it means if I have to rebuild or upgrade my opnsense machine I'll be stranded without the feature or a long night of hacking to manually add it back.
Likely yes, contributing to opensource projects (such as ddclient in this case) might help. Waiting for others to fix your problems just isn't always going to cut it (sometimes you're lucky sometimes you're not).
LOL no I get that. There's only so many hours in the day to contribute though
This issue has been automatically timed-out (after 180 days of inactivity).
For more information about the policies for this repository, please read https://github.com/opnsense/plugins/blob/master/CONTRIBUTING.md for further details.
If someone wants to step up and work on this issue, just let us know, so we can reopen the issue and assign an owner to it.
Hey, I started work on this issue Friday (9/08/23). I got the signature working without anything outside of the core Perl modules. Going to get the other logic finished up here and make a PR within the next two days. Formulating the signature isn't too bad, however, I don't program in Perl really so please provide me with some guidance once things are posted.
This was added to the native backend already. No need to use Perl/ddclient here. We‘re now using Amazon‘s Python glue.
Oh I thought I posted this to the ddclient issue sorry
EDIT: I just found out that the os-ddclient plugin has two backends and one of them does support AWS Route 53. So ignore the script below since using ddclient is the better option.
Apologies for posting to a closed issue. I thought to assist people affected with a workaround that I am using.
File: aws-dyndns.sh
#!/usr/bin/env bash
# This script is a modified version of the code available here: https://github.com/famzah/aws-dyndns
# Install notes:
# - SSH or open a shell on OPNsense box.
# - pkg install bash
# - mkdir /usr/local/opnsense/scripts/aws-dyndns
# - chmod a+rx /usr/local/opnsense/scripts/aws-dyndns
# - chown root:wheel /usr/local/opnsense/scripts/aws-dyndns
# - Copy this file (aws-dyndns.sh) to /usr/local/opnsense/scripts/aws-dyndns
# - chmod a+rx /usr/local/opnsense/scripts/aws-dyndns/aws-dyndns.sh
# - chown root:wheel /usr/local/opnsense/scripts/aws-dyndns/aws-dyndns.sh
# - Create a configd action file: /usr/local/opnsense/service/conf/actions.d/actions_aws-dyndns.conf
# with the following contents:
# ---snip---
# [update]
# command:/usr/local/opnsense/scripts/aws-dyndns/aws-dyndns.sh
# parameters:
# type:script
# message:Update AWS Route 53 DNS record
# description:Update AWS Route 53 DNS record
# ---snip---
# - chmod a+r /usr/local/opnsense/service/conf/actions.d/actions_aws-dyndns.conf
# - chown root:wheel /usr/local/opnsense/service/conf/actions.d/actions_aws-dyndns.conf
# - service configd restart
# - On web console, go to System > Settings > Cron
# - Add a new job to run the "Update AWS Route 53 DNS record" command when you want
#-------------------------------------------------------------------------------
# Configuration settings:
#-------------------------------------------------------------------------------
# The AWS access key id is the shorter value (20 characters).
AWS_ACCESS_KEY_ID=XXXXXXXXXXXXXXXXXXXX
# The AWS secret access key is the longer value, and is more private.
AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# The AWS Route 53 Zone ID
AWS_ZONE_ID=XXXXXXXXXXXXXX
# The hostname we wish to update with our public IP.
DNS_FQDN=home.example.com
# DNS TTL - The number of seconds an end-device will cache the record before
# checking with AWS again.
DNS_TTL=300
#-------------------------------------------------------------------------------
set -u
check_system_utility() {
local TOOL
for TOOL in "$@" ; do
if ! type "${TOOL}" &>/dev/null ; then
echo "Error: Required system utility is missing - ${TOOL}"
exit 1
fi
done
}
validate_ip() {
[[ $1 =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]
}
# https://stackoverflow.com/a/7385197/185257
hash_hmac_sha256() {
local KEY="$1"
local DATA="$2"
echo -n "${DATA}" | openssl dgst -sha256 -hmac "${KEY}" | cut -d' ' -f2
}
# https://stackoverflow.com/a/22369607/185257
hash_hmac_sha256_keyasbinary() {
local KEY="$1"
local DATA="$2"
echo -n "${DATA}" | openssl dgst -sha256 -mac HMAC -macopt "hexkey:${KEY}" | cut -d' ' -f2
}
aws_xml_payload() {
local AWS_PAYLOAD="<ChangeResourceRecordSetsRequest xmlns=\"https://${AWS_HOST}/doc/2013-04-01/\">"
AWS_PAYLOAD+="<ChangeBatch><Comment>DynDNS Update</Comment><Changes><Change>"
AWS_PAYLOAD+="<Action>UPSERT</Action>"
AWS_PAYLOAD+="<ResourceRecordSet>"
AWS_PAYLOAD+="<Name>${DNS_FQDN}</Name>"
AWS_PAYLOAD+="<Type>A</Type>"
AWS_PAYLOAD+="<TTL>${DNS_TTL}</TTL>"
AWS_PAYLOAD+="<ResourceRecords>"
AWS_PAYLOAD+="<ResourceRecord>"
AWS_PAYLOAD+="<Value>${NEW_IP}</Value>"
AWS_PAYLOAD+="</ResourceRecord>"
AWS_PAYLOAD+="</ResourceRecords>"
AWS_PAYLOAD+="</ResourceRecordSet>"
AWS_PAYLOAD+="</Change></Changes></ChangeBatch>"
AWS_PAYLOAD+="</ChangeResourceRecordSetsRequest>"
echo "${AWS_PAYLOAD}"
}
aws_xml_payload_hash() {
local AWS_PAYLOAD="$1"
echo -n "${AWS_PAYLOAD}" | openssl dgst -sha256 | cut -d' ' -f2
}
aws_canonical() {
local AWS_PAYLOAD_HASH="$1"
local AWS_CANONICAL="POST"$'\n'
AWS_CANONICAL+="/2013-04-01/hostedzone/${AWS_ZONE_ID}/rrset/"$'\n'
AWS_CANONICAL+=$'\n'
AWS_CANONICAL+="host:${AWS_HOST}"$'\n'
AWS_CANONICAL+="x-amz-date:${AWS_DATE}"$'\n'
AWS_CANONICAL+=$'\n'
AWS_CANONICAL+="${AWS_SIGNED_HEADERS}"$'\n'
AWS_CANONICAL+="${AWS_PAYLOAD_HASH}"
echo "${AWS_CANONICAL}"
}
aws_canonical_hash() {
local AWS_CANONICAL="$1"
echo -n "${AWS_CANONICAL}" | openssl dgst -sha256 | cut -d' ' -f2
}
aws_string_to_sign() {
local AWS_CANONICAL_HASH="$1"
local AWS_STRING_TO_SIGN="AWS4-HMAC-SHA256"$'\n'
AWS_STRING_TO_SIGN+="${AWS_DATE}"$'\n'
AWS_STRING_TO_SIGN+="${AWS_SCOPE_DATE}/${AWS_CLIENT_REGION}/${AWS_SERVICE}/aws4_request"$'\n'
AWS_STRING_TO_SIGN+="${AWS_CANONICAL_HASH}"
echo "${AWS_STRING_TO_SIGN}"
}
aws_signature() {
local AWS_STRING_TO_SIGN="$1"
local AWS_DATE_KEY AWS_DATE_REGION_KEY AWS_DATE_REGION_SERVICE_KEY
local AWS_SIGNING_KEY AWS_SIGNATURE
AWS_DATE_KEY="$(hash_hmac_sha256 "AWS4${AWS_SECRET_ACCESS_KEY}" "${AWS_SCOPE_DATE}")"
AWS_DATE_REGION_KEY="$(hash_hmac_sha256_keyasbinary "${AWS_DATE_KEY}" "${AWS_CLIENT_REGION}")"
AWS_DATE_REGION_SERVICE_KEY="$(hash_hmac_sha256_keyasbinary "${AWS_DATE_REGION_KEY}" "${AWS_SERVICE}")"
AWS_SIGNING_KEY="$(hash_hmac_sha256_keyasbinary "${AWS_DATE_REGION_SERVICE_KEY}" 'aws4_request')"
AWS_SIGNATURE="$(hash_hmac_sha256_keyasbinary "${AWS_SIGNING_KEY}" "${AWS_STRING_TO_SIGN}")"
echo "${AWS_SIGNATURE}"
}
AWS_DATE=
AWS_DATE_KEY=
AWS_CLIENT_REGION="us-east-1"
AWS_SERVICE="route53"
AWS_HOST="${AWS_SERVICE}.amazonaws.com"
AWS_SIGNED_HEADERS="host;x-amz-date"
aws_via_curl() {
local AWS_PAYLOAD AWS_PAYLOAD_HASH AWS_CANONICAL AWS_URL HEADERS
local AWS_CANONICAL_HASH AWS_STRING_TO_SIGN AWS_SIGNATURE AWS_CREDENTIAL
AWS_DATE="$(date -u +"%Y%m%dT%H%M%SZ")"
AWS_SCOPE_DATE="$(echo -n "${AWS_DATE}" | cut -d'T' -f1)"
AWS_PAYLOAD="$(aws_xml_payload)"
AWS_PAYLOAD_HASH="$(aws_xml_payload_hash "${AWS_PAYLOAD}")"
AWS_CANONICAL="$(aws_canonical "${AWS_PAYLOAD_HASH}")"
AWS_CANONICAL_HASH="$(aws_canonical_hash "${AWS_CANONICAL}")"
AWS_STRING_TO_SIGN="$(aws_string_to_sign "${AWS_CANONICAL_HASH}")"
AWS_SIGNATURE="$(aws_signature "${AWS_STRING_TO_SIGN}")"
AWS_CREDENTIAL="${AWS_ACCESS_KEY_ID}/${AWS_SCOPE_DATE}/${AWS_CLIENT_REGION}/${AWS_SERVICE}/aws4_request"
AWS_URL="https://${AWS_HOST}/2013-04-01/hostedzone/${AWS_ZONE_ID}/rrset/"
HEADERS=()
HEADERS+=("-H" "User-Agent: bash/${BASH_VERSION} $(uname -s)/$(uname -r) OpenSSL/$(openssl version | awk '{print $2}')")
HEADERS+=("-H" "X-Amz-Date: ${AWS_DATE}")
HEADERS+=("-H" "Authorization: AWS4-HMAC-SHA256 Credential=${AWS_CREDENTIAL}, SignedHeaders=${AWS_SIGNED_HEADERS}, Signature=${AWS_SIGNATURE}")
HEADERS+=("-H" "amz-sdk-invocation-id: $(uuidgen -r)")
HEADERS+=("-H" "amz-sdk-request: attempt=1")
logger "$(curl -Ss --data "${AWS_PAYLOAD}" "${HEADERS[@]}" -X POST "${AWS_URL}" 2>&1)"
}
#-------------------------------------------------------------------------------
check_system_utility awk cat cut curl date openssl uname uuidgen
logger "Querying OpenDNS name server to get the current IP for ${DNS_FQDN}."
OLD_IP="$(dig +short "${DNS_FQDN}" @resolver1.opendns.com)"
if [[ -n "${OLD_IP}" ]] && ! validate_ip "${OLD_IP}" ; then
logger "Invalid current IP returned: ${OLD_IP}"
exit 1
else
logger "Current IP is: ${OLD_IP}"
fi
logger "Using https://checkip.amazonaws.com/ service to check public IP."
NEW_IP="$(curl -sS --max-time 5 https://checkip.amazonaws.com/)"
if [[ -n "${NEW_IP}" ]] && ! validate_ip "${NEW_IP}" ; then
logger "Invalid response returned by service: ${NEW_IP}"
exit 1
else
logger "Service reports public IP as: ${NEW_IP}"
fi
if [[ "${OLD_IP}" == "${NEW_IP}" ]]; then
logger "The AWS Route 53 DNS record for ${DNS_FQDN} does not need to be updated."
else
logger "Updating the AWS Route 53 DNS record for: ${DNS_FQDN}; FROM: ${OLD_IP}; TO: ${NEW_IP}"
aws_via_curl
fi