diff --git a/dns/ddclient/Makefile b/dns/ddclient/Makefile
index bd849f880..4b731c124 100644
--- a/dns/ddclient/Makefile
+++ b/dns/ddclient/Makefile
@@ -1,7 +1,6 @@
PLUGIN_NAME= ddclient
-PLUGIN_VERSION= 1.14
-PLUGIN_REVISION= 1
-PLUGIN_DEPENDS= ddclient-devel
+PLUGIN_VERSION= 1.15
+PLUGIN_DEPENDS= ddclient-devel py${PLUGIN_PYTHON}-boto3
PLUGIN_COMMENT= Dynamic DNS client
PLUGIN_MAINTAINER= ad@opnsense.org
diff --git a/dns/ddclient/src/opnsense/mvc/app/controllers/OPNsense/DynDNS/forms/dialogAccount.xml b/dns/ddclient/src/opnsense/mvc/app/controllers/OPNsense/DynDNS/forms/dialogAccount.xml
index 7afd4b9c4..12aedb288 100644
--- a/dns/ddclient/src/opnsense/mvc/app/controllers/OPNsense/DynDNS/forms/dialogAccount.xml
+++ b/dns/ddclient/src/opnsense/mvc/app/controllers/OPNsense/DynDNS/forms/dialogAccount.xml
@@ -62,7 +62,7 @@
account.zone
text
-
+
Zone containing the host entry.
@@ -73,6 +73,13 @@
true
Hostname to update
+
+ account.ttl
+
+ text
+
+ Time to Live for the DNS entry
+
account.checkip
diff --git a/dns/ddclient/src/opnsense/mvc/app/models/OPNsense/DynDNS/DynDNS.xml b/dns/ddclient/src/opnsense/mvc/app/models/OPNsense/DynDNS/DynDNS.xml
index 7557c5fe4..5a3b4dd37 100644
--- a/dns/ddclient/src/opnsense/mvc/app/models/OPNsense/DynDNS/DynDNS.xml
+++ b/dns/ddclient/src/opnsense/mvc/app/models/OPNsense/DynDNS/DynDNS.xml
@@ -171,6 +171,12 @@
1
Y
+
+ 300
+ Y
+ 1
+ 604800
+
N
N
diff --git a/dns/ddclient/src/opnsense/scripts/ddclient/lib/account/aws.py b/dns/ddclient/src/opnsense/scripts/ddclient/lib/account/aws.py
new file mode 100644
index 000000000..7d66b0d19
--- /dev/null
+++ b/dns/ddclient/src/opnsense/scripts/ddclient/lib/account/aws.py
@@ -0,0 +1,71 @@
+"""
+ AWS Route53 DNS provider
+ Usage:
+ AWS access key: username
+ AWS secret key: password
+ Route53 Hosted Zone ID: resource ID (use advanced settings)
+"""
+import syslog
+import boto3
+from . import BaseAccount
+
+
+class AWS(BaseAccount):
+ _services = ['aws']
+
+ def __init__(self, account: dict):
+ super().__init__(account)
+
+ @staticmethod
+ def known_services():
+ return AWS._services
+
+ @staticmethod
+ def match(account):
+ return account.get('service') in AWS._services
+
+ def execute(self):
+ """ AWS DNS update
+ """
+
+ if super().execute():
+ if not self.current_address:
+ syslog.syslog(
+ syslog.LOG_WARNING,
+ f"No address found for {self.description}"
+ )
+ return False
+ client = boto3.client('route53',
+ aws_access_key_id = self.settings.get('username'),
+ aws_secret_access_key = self.settings.get('password'))
+ ip = str(self.current_address)
+ if ':' in ip:
+ addrType = 'AAAA'
+ else:
+ addrType = 'A'
+ TTL = self.settings.get('ttl')
+ changeBatch = {
+ 'Changes': [{
+ 'Action': 'UPSERT',
+ 'ResourceRecordSet': {
+ 'Name': host,
+ 'Type': addrType,
+ 'TTL': int(TTL),
+ 'ResourceRecords': [{'Value': ip}]
+ }
+ } for host in self.settings.get('hostnames').split(",")]
+ }
+ try:
+ response = client.change_resource_record_sets(
+ HostedZoneId = self.settings.get('zone'),
+ ChangeBatch = changeBatch)
+ except Exception as e:
+ syslog.syslog(syslog.LOG_ERR, str(e))
+ return False
+
+ syslog.syslog(
+ syslog.LOG_NOTICE,
+ f"Account {self.description} set new ip {self.current_address}, ID {response['ChangeInfo']['Id']}")
+
+ self.update_state(address=self.current_address)
+ return True
diff --git a/dns/ddclient/src/opnsense/service/templates/OPNsense/ddclient/ddclient.json b/dns/ddclient/src/opnsense/service/templates/OPNsense/ddclient/ddclient.json
index 3e2b8cfed..97895c809 100644
--- a/dns/ddclient/src/opnsense/service/templates/OPNsense/ddclient/ddclient.json
+++ b/dns/ddclient/src/opnsense/service/templates/OPNsense/ddclient/ddclient.json
@@ -24,6 +24,7 @@
"checkip": "{{ account.checkip }}",
"checkip_timeout": {{ account.checkip_timeout }},
"force_ssl": {{ "true" if account.force_ssl == '1' else "false"}},
+ "ttl": "{{ account.ttl }}",
"interface": "{%if account.interface %}{{physical_interface(account.interface)}}{% endif%}",
"description": "{{ account.description }}"
}{{ "," if not loop.last else ""}}