mirror of
https://github.com/opnsense/plugins.git
synced 2026-05-28 04:34:15 -04:00
Merge 012c3e87a7 into cb9a5d6d69
This commit is contained in:
commit
500f2b92d6
7 changed files with 203 additions and 5 deletions
|
|
@ -38,6 +38,7 @@
|
|||
<label>resourceId</label>
|
||||
<type>text</type>
|
||||
<advanced>true</advanced>
|
||||
<style>optional_setting service_azure</style>
|
||||
</field>
|
||||
<field>
|
||||
<id>account.username</id>
|
||||
|
|
@ -51,6 +52,13 @@
|
|||
<type>password</type>
|
||||
<help>Password associated with this account</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>account.token_secret</id>
|
||||
<label>Token secret</label>
|
||||
<type>password</type>
|
||||
<style>optional_setting service_desec-v4 service_desec-v6</style>
|
||||
<help>Token secret for the domain. This is not your deSEC account password.</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>account.wildcard</id>
|
||||
<label>Wildcard</label>
|
||||
|
|
@ -58,6 +66,20 @@
|
|||
<style>optional_setting service_dyndns2 service_woima service_cloudflare service_easydns service_custom</style>
|
||||
<help>add a DNS wildcard CNAME record that points to the configured host.</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>account.prune_a</id>
|
||||
<label>Prune A</label>
|
||||
<type>checkbox</type>
|
||||
<style>optional_setting service_desec-v6</style>
|
||||
<help>Delete existing A (IPv4) records when this IPv6 domain updates. Leave unchecked to preserve them.</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>account.prune_aaaa</id>
|
||||
<label>Prune AAAA</label>
|
||||
<type>checkbox</type>
|
||||
<style>optional_setting service_desec-v4</style>
|
||||
<help>Delete existing AAAA (IPv6) records when this IPv4 domain updates. Leave unchecked to preserve them.</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>account.zone</id>
|
||||
<label>Zone</label>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<model>
|
||||
<mount>//OPNsense/DynDNS</mount>
|
||||
<version>1.5.1</version>
|
||||
<version>1.5.2</version>
|
||||
<description>Dynamic DNS client</description>
|
||||
<items>
|
||||
<general>
|
||||
|
|
@ -113,6 +113,16 @@
|
|||
<Required>N</Required>
|
||||
<Mask>/^[^\n]*$/</Mask>
|
||||
</password>
|
||||
<!--
|
||||
Keep this as TextField to support cloning and to avoid
|
||||
confusion about the state of migrated deSEC accounts. The
|
||||
dialog still renders it as a protected password input.
|
||||
Users who can access this GUI can trivially extract it anyway.
|
||||
-->
|
||||
<token_secret type="TextField">
|
||||
<Required>N</Required>
|
||||
<Mask>/^[^\n]*$/</Mask>
|
||||
</token_secret>
|
||||
<resourceId type="TextField">
|
||||
<Required>N</Required>
|
||||
<Mask>/^[^\n]*$/</Mask>
|
||||
|
|
@ -131,6 +141,14 @@
|
|||
<Default>0</Default>
|
||||
<Required>Y</Required>
|
||||
</wildcard>
|
||||
<prune_a type="BooleanField">
|
||||
<Default>0</Default>
|
||||
<Required>Y</Required>
|
||||
</prune_a>
|
||||
<prune_aaaa type="BooleanField">
|
||||
<Default>0</Default>
|
||||
<Required>Y</Required>
|
||||
</prune_aaaa>
|
||||
<zone type="HostnameField">
|
||||
<Required>N</Required>
|
||||
<IpAllowed>N</IpAllowed>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace OPNsense\DynDNS\Migrations;
|
||||
|
||||
use OPNsense\Base\BaseModelMigration;
|
||||
|
||||
class M1_5_2 extends BaseModelMigration
|
||||
{
|
||||
public function run($model)
|
||||
{
|
||||
foreach ($model->accounts->account->iterateItems() as $account) {
|
||||
$service = (string)$account->service;
|
||||
if ($service == 'desec-v4' || $service == 'desec-v6') {
|
||||
/*
|
||||
* Older deSEC entries used "password" to store the token secret;
|
||||
* the deSEC account password was never supported. Copy the value
|
||||
* to the explicit field, but leave the old value so unchanged
|
||||
* migrated entries can roll back. Entries created after this
|
||||
* migration only use token_secret.
|
||||
*/
|
||||
$legacy_token = $account->password->getValue();
|
||||
if ((string)$account->token_secret == '' && $legacy_token != '') {
|
||||
$account->token_secret = $legacy_token;
|
||||
}
|
||||
/*
|
||||
* deSEC never used "username" as an account login. If set at
|
||||
* all, it could only mirror Hostname(s), which is already the
|
||||
* authoritative update target.
|
||||
*/
|
||||
$account->username = '';
|
||||
/*
|
||||
* The original deSEC path deleted the opposite address family
|
||||
* when "preserve" was omitted. Existing accounts keep that
|
||||
* behavior; new accounts get the model defaults and preserve.
|
||||
*/
|
||||
if ($service == 'desec-v4') {
|
||||
$account->prune_aaaa = '1';
|
||||
} else {
|
||||
$account->prune_a = '1';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -57,8 +57,8 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
updateServiceControlUI('dyndns');
|
||||
}
|
||||
});
|
||||
$("#account\\.service").change(function(){
|
||||
let service = $(this).val();
|
||||
function updateAccountServiceControls() {
|
||||
let service = $("#account\\.service").val();
|
||||
$("#frm_DialogAccount .optional_setting").each(function(){
|
||||
let this_item = $(this);
|
||||
if (this_item.hasClass("service_"+service)) {
|
||||
|
|
@ -69,6 +69,22 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
this_item.prop( "disabled", true );
|
||||
}
|
||||
});
|
||||
let is_desec = ['desec-v4', 'desec-v6'].includes(service);
|
||||
// deSEC's dynDNS "username" was never an account login; the only
|
||||
// useful value was a duplicate of Hostname(s), so migration clears
|
||||
// it. Use Hostname(s) plus Token secret and keep legacy Password
|
||||
// hidden for rollback without exposing or rewriting it.
|
||||
$("#account\\.username, #account\\.password")
|
||||
.prop("disabled", is_desec)
|
||||
.closest("tr")
|
||||
.toggle(!is_desec);
|
||||
}
|
||||
|
||||
$("#account\\.service").change(updateAccountServiceControls);
|
||||
$(document).ajaxComplete(function(event, xhr, settings) {
|
||||
if (settings.url && settings.url.indexOf('/api/dyndns/accounts/get_item/') !== -1) {
|
||||
updateAccountServiceControls();
|
||||
}
|
||||
});
|
||||
$('#DialogAccount').on('shown.bs.modal', function (e) {
|
||||
$("#account\\.service").change();
|
||||
|
|
|
|||
97
dns/ddclient/src/opnsense/scripts/ddclient/lib/account/desec.py
Executable file
97
dns/ddclient/src/opnsense/scripts/ddclient/lib/account/desec.py
Executable file
|
|
@ -0,0 +1,97 @@
|
|||
import syslog
|
||||
import requests
|
||||
from . import BaseAccount
|
||||
|
||||
|
||||
class DeSEC(BaseAccount):
|
||||
_checked_values = {'1', 'true', 'yes', 'on'}
|
||||
_preserve_value = 'preserve'
|
||||
_user_agent = 'OPNsense-dyndns'
|
||||
|
||||
_services = {
|
||||
'desec-v4': {
|
||||
'label': 'deSEC (IPv4)',
|
||||
'server': 'update.dedyn.io',
|
||||
'address_param': 'myipv4',
|
||||
'other_param': 'myipv6',
|
||||
'prune_setting': 'prune_aaaa'
|
||||
},
|
||||
'desec-v6': {
|
||||
'label': 'deSEC (IPv6)',
|
||||
'server': 'update6.dedyn.io',
|
||||
'address_param': 'myipv6',
|
||||
'other_param': 'myipv4',
|
||||
'prune_setting': 'prune_a'
|
||||
}
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def known_services(cls):
|
||||
return {key: item['label'] for key, item in cls._services.items()}
|
||||
|
||||
@classmethod
|
||||
def match(cls, account):
|
||||
return account.get('service') in cls._services
|
||||
|
||||
@classmethod
|
||||
def _is_checked(cls, value):
|
||||
return value is True or str(value).lower() in cls._checked_values
|
||||
|
||||
def _token_secret(self):
|
||||
# Legacy deSEC accounts stored the token secret in "password"; a deSEC
|
||||
# account password was never accepted by this backend.
|
||||
return self.settings.get('token_secret') or self.settings.get('password') or ''
|
||||
|
||||
def _address_parameters(self, service_settings):
|
||||
# deSEC prunes the other address family when its parameter is empty.
|
||||
# New accounts preserve by default; migrated accounts may set prune_* to
|
||||
# keep the historic behavior.
|
||||
other_address = (
|
||||
''
|
||||
if self._is_checked(self.settings.get(service_settings['prune_setting'], False))
|
||||
else self._preserve_value
|
||||
)
|
||||
return {
|
||||
'hostname': self.settings.get('hostnames'),
|
||||
service_settings['address_param']: str(self.current_address),
|
||||
service_settings['other_param']: other_address
|
||||
}
|
||||
|
||||
def _request_options(self, service_settings):
|
||||
uri_proto = 'https' if self.settings.get('force_ssl', False) else 'http'
|
||||
# deSEC's dynDNS "username" is the domain being updated, not an
|
||||
# account login. We already send that via the hostname parameter, so use
|
||||
# token authentication and keep the generic Username field irrelevant.
|
||||
return {
|
||||
'url': f"{uri_proto}://{service_settings['server']}/nic/update",
|
||||
'params': self._address_parameters(service_settings),
|
||||
'headers': {
|
||||
'User-Agent': self._user_agent,
|
||||
'Authorization': f"Token {self._token_secret()}"
|
||||
}
|
||||
}
|
||||
|
||||
def execute(self):
|
||||
if not super().execute():
|
||||
return False
|
||||
|
||||
service_settings = self._services[self.settings.get('service')]
|
||||
req = requests.get(**self._request_options(service_settings))
|
||||
|
||||
if 200 <= req.status_code < 300:
|
||||
if self.is_verbose:
|
||||
syslog.syslog(
|
||||
syslog.LOG_NOTICE,
|
||||
"Account %s set new ip %s [%s]" % (self.description, self.current_address, req.text.strip())
|
||||
)
|
||||
|
||||
self.update_state(address=self.current_address, status=req.text.split()[0] if req.text else '')
|
||||
return True
|
||||
|
||||
syslog.syslog(
|
||||
syslog.LOG_ERR,
|
||||
"Account %s failed to set new ip %s [%d - %s]" % (
|
||||
self.description, self.current_address, req.status_code, req.text.replace('\n', '')
|
||||
)
|
||||
)
|
||||
return False
|
||||
|
|
@ -34,8 +34,6 @@ class DynDNS2(BaseAccount):
|
|||
|
||||
_services = {
|
||||
'dyndns2': 'members.dyndns.org',
|
||||
'desec-v4': 'update.dedyn.io',
|
||||
'desec-v6': 'update6.dedyn.io',
|
||||
'dns-o-matic': 'updates.dnsomatic.com',
|
||||
'dynu': 'api.dynu.com',
|
||||
'he-net': 'dyn.dns.he.net',
|
||||
|
|
|
|||
|
|
@ -17,8 +17,11 @@
|
|||
"resourceId": {{ account.resourceId | default('') | tojson }},
|
||||
"username": {{ account.username | default('') | tojson }},
|
||||
"password": {{ account.password | default('') | tojson }},
|
||||
"token_secret": {{ account.token_secret | default('') | tojson }},
|
||||
"hostnames": "{{ account.hostnames }}",
|
||||
"wildcard": {{ "true" if account.wildcard == '1' else "false"}},
|
||||
"prune_a": {{ "true" if account.prune_a|default('0') == '1' else "false"}},
|
||||
"prune_aaaa": {{ "true" if account.prune_aaaa|default('0') == '1' else "false"}},
|
||||
"zone": "{{ account.zone }}",
|
||||
"checkip": "{{ account.checkip }}",
|
||||
"interface": "{% if account.interface %}{{physical_interface(account.interface)}}{% endif %}",
|
||||
|
|
|
|||
Loading…
Reference in a new issue