From fbfcec4873b801ef9d252383a911e625e9bda280 Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Tue, 5 Jul 2022 09:06:52 +0200 Subject: [PATCH] `yunohost domain dns push` works with Dynette domains --- hooks/conf_regen/01-yunohost | 2 +- locales/en.json | 1 + share/actionsmap.yml | 71 ++++++++++++++++++++++++++++++++++-- src/dns.py | 34 ++++++++++++++--- src/domain.py | 37 +++++++++++++++---- src/dyndns.py | 4 +- 6 files changed, 128 insertions(+), 21 deletions(-) diff --git a/hooks/conf_regen/01-yunohost b/hooks/conf_regen/01-yunohost index dc0bfc689..29da2b183 100755 --- a/hooks/conf_regen/01-yunohost +++ b/hooks/conf_regen/01-yunohost @@ -117,7 +117,7 @@ SHELL=/bin/bash # - if ip.yunohost.org answers ping (basic check to validate that we're connected to the internet and yunohost infra aint down) # - and if lock ain't already taken by another command # - trigger yunohost dyndns update -*/10 * * * * root : YunoHost DynDNS update; sleep \$((RANDOM\\%60)); ! ping -q -W5 -c1 ip.yunohost.org >/dev/null 2>&1 || test -e /var/run/moulinette_yunohost.lock || yunohost dyndns update >> /dev/null +*/10 * * * * root : YunoHost DynDNS update ; sleep \$((RANDOM\\%60)); ! ping -q -W5 -c1 ip.yunohost.org >/dev/null 2>&1 || test -e /var/run/moulinette_yunohost.lock || yunohost domain list --exclude-subdomains --output json | jq --raw-output '.domains[]' | grep -E "\.(noho\.st|nohost\.me|ynh\.fr)$" | xargs -I {} yunohost domain dns push "{}" >> /dev/null EOF else # (Delete cron if no dyndns domain found) diff --git a/locales/en.json b/locales/en.json index 96946acd3..a1a90d686 100644 --- a/locales/en.json +++ b/locales/en.json @@ -323,6 +323,7 @@ "domain_dns_conf_special_use_tld": "This domain is based on a special-use top-level domain (TLD) such as .local or .test and is therefore not expected to have actual DNS records.", "domain_dns_push_already_up_to_date": "Records already up to date, nothing to do.", "domain_dns_push_failed": "Updating the DNS records failed miserably.", + "domain_dns_push_failed_domain": "Updating the DNS records for {domain} failed : {error}", "domain_dns_push_failed_to_authenticate": "Failed to authenticate on registrar's API for domain '{domain}'. Most probably the credentials are incorrect? (Error: {error})", "domain_dns_push_failed_to_list": "Failed to list current records using the registrar's API: {error}", "domain_dns_push_managed_in_parent_domain": "The automatic DNS configuration feature is managed in the parent domain {parent_domain}.", diff --git a/share/actionsmap.yml b/share/actionsmap.yml index fae7ab8f8..85b240aa3 100644 --- a/share/actionsmap.yml +++ b/share/actionsmap.yml @@ -578,10 +578,69 @@ domain: help: The path to check (e.g. /coffee) subcategories: + dyndns: + subcategory_help: Subscribe and Update DynDNS Hosts + actions: + ### domain_dyndns_subscribe() + subscribe: + action_help: Subscribe to a DynDNS service + arguments: + -d: + full: --domain + help: Full domain to subscribe with + extra: + pattern: *pattern_domain + -k: + full: --key + help: Public DNS key + -p: + full: --password + nargs: "?" + const: 0 + help: Password used to later delete the domain + extra: + pattern: *pattern_password + comment: dyndns_added_password + + ### domain_dyndns_unsubscribe() + unsubscribe: + action_help: Unsubscribe from a DynDNS service + arguments: + -d: + full: --domain + help: Full domain to unsubscribe with + extra: + pattern: *pattern_domain + required: True + -p: + full: --password + nargs: "?" + const: 0 + help: Password used to delete the domain + extra: + pattern: *pattern_password config: subcategory_help: Domain settings actions: + ### domain_config_get() + get: + action_help: Display a domain configuration + api: GET /domains//config + arguments: + domain: + help: Domain name + key: + help: A specific panel, section or a question identifier + nargs: '?' + -f: + full: --full + help: Display all details (meant to be used by the API) + action: store_true + -e: + full: --export + help: Only export key/values, meant to be reimported using "config set --args-file" + action: store_true ### domain_config_get() get: @@ -641,6 +700,7 @@ domain: arguments: domain: help: Domain name to push DNS conf for + nargs: "*" extra: pattern: *pattern_domain -d: @@ -1406,16 +1466,17 @@ firewall: # DynDNS # ############################# dyndns: - category_help: Subscribe and Update DynDNS Hosts + category_help: Subscribe and Update DynDNS Hosts ( deprecated, use 'yunohost domain dyndns' instead ) actions: ### dyndns_subscribe() subscribe: action_help: Subscribe to a DynDNS service + deprecated: true arguments: -d: full: --domain - help: Full domain to subscribe with + help: Full domain to subscribe with ( deprecated, use 'yunohost domain dyndns subscribe' instead ) extra: pattern: *pattern_domain -k: @@ -1432,7 +1493,8 @@ dyndns: ### dyndns_unsubscribe() unsubscribe: - action_help: Unsubscribe to a DynDNS service + action_help: Unsubscribe to a DynDNS service ( deprecated, use 'yunohost domain dyndns unsubscribe' instead ) + deprecated: true arguments: -d: full: --domain @@ -1450,7 +1512,8 @@ dyndns: ### dyndns_update() update: - action_help: Update IP on DynDNS platform + action_help: Update IP on DynDNS platform ( deprecated, use 'yunohost domain dns push DOMAIN' instead ) + deprecated: true arguments: -d: full: --domain diff --git a/src/dns.py b/src/dns.py index c8bebed41..144f8a3a2 100644 --- a/src/dns.py +++ b/src/dns.py @@ -47,6 +47,7 @@ from yunohost.utils.error import YunohostValidationError, YunohostError from yunohost.utils.network import get_public_ip from yunohost.log import is_unit_operation from yunohost.hook import hook_callback +from yunohost.dyndns import dyndns_update logger = getActionLogger("yunohost.domain") @@ -622,7 +623,17 @@ def _get_registar_settings(domain): @is_unit_operation() -def domain_dns_push(operation_logger, domain, dry_run=False, force=False, purge=False): +def domain_dns_push(operation_logger, domains, dry_run=False, force=False, purge=False): + if type(domains).__name__!="list": # If we provide only a domain as an argument + domains = [domains] + for domain in domains: + try: + domain_dns_push_unique(domain,dry_run=dry_run,force=force,purge=purge) + except YunohostError as e: + logger.error(m18n.n("domain_dns_push_failed_domain",domain=domain,error=str(e))) + +@is_unit_operation() +def domain_dns_push_unique(operation_logger, domain, dry_run=False, force=False, purge=False): """ Send DNS records to the previously-configured registrar of the domain. """ @@ -643,12 +654,14 @@ def domain_dns_push(operation_logger, domain, dry_run=False, force=False, purge= # FIXME: in the future, properly unify this with yunohost dyndns update if registrar == "yunohost": - logger.info(m18n.n("domain_dns_registrar_yunohost")) + #logger.info(m18n.n("domain_dns_registrar_yunohost")) + from yunohost.dyndns import dyndns_update + dyndns_update(domain=domain) return {} if registrar == "parent_domain": parent_domain = domain.split(".", 1)[1] - registar, registrar_credentials = _get_registar_settings(parent_domain) + registrar, registrar_credentials = _get_registar_settings(parent_domain) if any(registrar_credentials.values()): raise YunohostValidationError( "domain_dns_push_managed_in_parent_domain", @@ -656,9 +669,18 @@ def domain_dns_push(operation_logger, domain, dry_run=False, force=False, purge= parent_domain=parent_domain, ) else: - raise YunohostValidationError( - "domain_registrar_is_not_configured", domain=parent_domain - ) + new_parent_domain = ".".join(parent_domain.split(".")[-3:]) + registrar, registrar_credentials = _get_registar_settings(new_parent_domain) + if registrar == "yunohost": + raise YunohostValidationError( + "domain_dns_push_managed_in_parent_domain", + domain=domain, + parent_domain=new_parent_domain, + ) + else: + raise YunohostValidationError( + "domain_registrar_is_not_configured", domain=parent_domain + ) if not all(registrar_credentials.values()): raise YunohostValidationError( diff --git a/src/domain.py b/src/domain.py index 5bdecf651..770c2931b 100644 --- a/src/domain.py +++ b/src/domain.py @@ -181,10 +181,8 @@ def domain_add(operation_logger, domain, dyndns=False,password=None): operation_logger.start() if dyndns: - from yunohost.dyndns import dyndns_subscribe - # Actually subscribe - dyndns_subscribe(domain=domain,password=password) + domain_dyndns_subscribe(domain=domain,password=password) _certificate_install_selfsigned([domain], True) @@ -357,14 +355,37 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False, pass # If a password is provided, delete the DynDNS record if password!=None: - from yunohost.dyndns import dyndns_unsubscribe - # Actually unsubscribe - dyndns_unsubscribe(domain=domain,password=password) + domain_dyndns_unsubscribe(domain=domain,password=password) logger.success(m18n.n("domain_deleted")) +def domain_dyndns_subscribe(**kwargs): + """ + Subscribe to a DynDNS domain + """ + from yunohost.dyndns import dyndns_subscribe + + dyndns_subscribe(**kwargs) + +def domain_dyndns_unsubscribe(**kwargs): + """ + Unsubscribe from a DynDNS domain + """ + from yunohost.dyndns import dyndns_unsubscribe + + dyndns_unsubscribe(**kwargs) + +def domain_dyndns_update(**kwargs): + """ + Update a DynDNS domain + """ + from yunohost.dyndns import dyndns_update + + dyndns_update(**kwargs) + + @is_unit_operation() def domain_main_domain(operation_logger, new_main_domain=None): """ @@ -572,7 +593,7 @@ def domain_dns_suggest(domain): return domain_dns_suggest(domain) -def domain_dns_push(domain, dry_run, force, purge): +def domain_dns_push(domain, dry_run=None, force=None, purge=None): from yunohost.dns import domain_dns_push - return domain_dns_push(domain, dry_run, force, purge) + return domain_dns_push(domain, dry_run=dry_run, force=force, purge=purge) diff --git a/src/dyndns.py b/src/dyndns.py index a5532f101..741bb81dc 100644 --- a/src/dyndns.py +++ b/src/dyndns.py @@ -176,7 +176,7 @@ def dyndns_subscribe(operation_logger, domain=None, key=None, password=None): # Add some dyndns update in 2 and 4 minutes from now such that user should # not have to wait 10ish minutes for the conf to propagate cmd = ( - "at -M now + {t} >/dev/null 2>&1 <<< \"/bin/bash -c 'yunohost dyndns update'\"" + f"at -M now + {{t}} >/dev/null 2>&1 <<< \"/bin/bash -c 'yunohost domain dns push {domain}'\"" ) # For some reason subprocess doesn't like the redirections so we have to use bash -c explicity... subprocess.check_call(["bash", "-c", cmd.format(t="2 min")]) @@ -231,7 +231,7 @@ def dyndns_unsubscribe(operation_logger, domain, password=None): # Add some dyndns update in 2 and 4 minutes from now such that user should # not have to wait 10ish minutes for the conf to propagate cmd = ( - "at -M now + {t} >/dev/null 2>&1 <<< \"/bin/bash -c 'yunohost dyndns update'\"" + f"at -M now + {{t}} >/dev/null 2>&1 <<< \"/bin/bash -c 'yunohost domain dns push {domain}'\"" ) # For some reason subprocess doesn't like the redirections so we have to use bash -c explicity... subprocess.check_call(["bash", "-c", cmd.format(t="2 min")])