Drop some dyndns yagni: we don't need to be able to specify a custom dyndns host...

This commit is contained in:
Alexandre Aubin 2021-10-24 22:16:07 +02:00
parent a8c6b5e637
commit c6dfe08973
4 changed files with 63 additions and 107 deletions

View file

@ -1483,9 +1483,6 @@ dyndns:
subscribe: subscribe:
action_help: Subscribe to a DynDNS service action_help: Subscribe to a DynDNS service
arguments: arguments:
--subscribe-host:
help: Dynette HTTP API to subscribe to
default: "dyndns.yunohost.org"
-d: -d:
full: --domain full: --domain
help: Full domain to subscribe with help: Full domain to subscribe with
@ -1499,9 +1496,6 @@ dyndns:
update: update:
action_help: Update IP on DynDNS platform action_help: Update IP on DynDNS platform
arguments: arguments:
--dyn-host:
help: Dynette DNS server to inform
default: "dyndns.yunohost.org"
-d: -d:
full: --domain full: --domain
help: Full domain to update help: Full domain to update

View file

@ -167,15 +167,16 @@ def domain_add(operation_logger, domain, dyndns=False):
# DynDNS domain # DynDNS domain
if dyndns: if dyndns:
from yunohost.dyndns import _dyndns_provides, _guess_current_dyndns_domain from yunohost.utils.dns import is_yunohost_dyndns_domain
from yunohost.dyndns import _guess_current_dyndns_domain
# Do not allow to subscribe to multiple dyndns domains... # Do not allow to subscribe to multiple dyndns domains...
if _guess_current_dyndns_domain("dyndns.yunohost.org") != (None, None): if _guess_current_dyndns_domain() != (None, None):
raise YunohostValidationError("domain_dyndns_already_subscribed") raise YunohostValidationError("domain_dyndns_already_subscribed")
# Check that this domain can effectively be provided by # Check that this domain can effectively be provided by
# dyndns.yunohost.org. (i.e. is it a nohost.me / noho.st) # dyndns.yunohost.org. (i.e. is it a nohost.me / noho.st)
if not _dyndns_provides("dyndns.yunohost.org", domain): if not is_yunohost_dyndns_domain(domain):
raise YunohostValidationError("domain_dyndns_root_unknown") raise YunohostValidationError("domain_dyndns_root_unknown")
operation_logger.start() operation_logger.start()

View file

@ -39,7 +39,7 @@ from moulinette.utils.network import download_json
from yunohost.utils.error import YunohostError, YunohostValidationError from yunohost.utils.error import YunohostError, YunohostValidationError
from yunohost.domain import _get_maindomain from yunohost.domain import _get_maindomain
from yunohost.utils.network import get_public_ip from yunohost.utils.network import get_public_ip
from yunohost.utils.dns import dig from yunohost.utils.dns import dig, is_yunohost_dyndns_domain
from yunohost.log import is_unit_operation from yunohost.log import is_unit_operation
from yunohost.regenconf import regen_conf from yunohost.regenconf import regen_conf
@ -53,66 +53,36 @@ RE_DYNDNS_PRIVATE_KEY_SHA512 = re.compile(
r".*/K(?P<domain>[^\s\+]+)\.\+165.+\.private$" r".*/K(?P<domain>[^\s\+]+)\.\+165.+\.private$"
) )
DYNDNS_PROVIDER = "dyndns.yunohost.org"
DYNDNS_DNS_AUTH = ["ns0.yunohost.org", "ns1.yunohost.org"]
def _dyndns_provides(provider, domain):
def _dyndns_available(domain):
""" """
Checks if a provider provide/manage a given domain. Checks if a domain is available on dyndns.yunohost.org
Keyword arguments: Keyword arguments:
provider -- The url of the provider, e.g. "dyndns.yunohost.org"
domain -- The full domain that you'd like.. e.g. "foo.nohost.me"
Returns:
True if the provider provide/manages the domain. False otherwise.
"""
logger.debug("Checking if %s is managed by %s ..." % (domain, provider))
try:
# Dyndomains will be a list of domains supported by the provider
# e.g. [ "nohost.me", "noho.st" ]
dyndomains = download_json("https://%s/domains" % provider, timeout=30)
except MoulinetteError as e:
logger.error(str(e))
raise YunohostError(
"dyndns_could_not_check_provide", domain=domain, provider=provider
)
# Extract 'dyndomain' from 'domain', e.g. 'nohost.me' from 'foo.nohost.me'
dyndomain = ".".join(domain.split(".")[1:])
return dyndomain in dyndomains
def _dyndns_available(provider, domain):
"""
Checks if a domain is available from a given provider.
Keyword arguments:
provider -- The url of the provider, e.g. "dyndns.yunohost.org"
domain -- The full domain that you'd like.. e.g. "foo.nohost.me" domain -- The full domain that you'd like.. e.g. "foo.nohost.me"
Returns: Returns:
True if the domain is available, False otherwise. True if the domain is available, False otherwise.
""" """
logger.debug("Checking if domain %s is available on %s ..." % (domain, provider)) logger.debug(f"Checking if domain {domain} is available on {DYNDNS_PROVIDER} ...")
try: try:
r = download_json( r = download_json(f"https://{DYNDNS_PROVIDER}/test/{domain}", expected_status_code=None)
"https://%s/test/%s" % (provider, domain), expected_status_code=None
)
except MoulinetteError as e: except MoulinetteError as e:
logger.error(str(e)) logger.error(str(e))
raise YunohostError( raise YunohostError(
"dyndns_could_not_check_available", domain=domain, provider=provider "dyndns_could_not_check_available", domain=domain, provider=DYNDNS_PROVIDER
) )
return r == "Domain %s is available" % domain return r == f"Domain {domain} is available"
@is_unit_operation() @is_unit_operation()
def dyndns_subscribe( def dyndns_subscribe(
operation_logger, subscribe_host="dyndns.yunohost.org", domain=None, key=None operation_logger, domain=None, key=None
): ):
""" """
Subscribe to a DynDNS service Subscribe to a DynDNS service
@ -120,11 +90,9 @@ def dyndns_subscribe(
Keyword argument: Keyword argument:
domain -- Full domain to subscribe with domain -- Full domain to subscribe with
key -- Public DNS key key -- Public DNS key
subscribe_host -- Dynette HTTP API to subscribe to
""" """
if _guess_current_dyndns_domain(subscribe_host) != (None, None): if _guess_current_dyndns_domain() != (None, None):
raise YunohostValidationError("domain_dyndns_already_subscribed") raise YunohostValidationError("domain_dyndns_already_subscribed")
if domain is None: if domain is None:
@ -132,13 +100,13 @@ def dyndns_subscribe(
operation_logger.related_to.append(("domain", domain)) operation_logger.related_to.append(("domain", domain))
# Verify if domain is provided by subscribe_host # Verify if domain is provided by subscribe_host
if not _dyndns_provides(subscribe_host, domain): if not is_yunohost_dyndns_domain(domain):
raise YunohostValidationError( raise YunohostValidationError(
"dyndns_domain_not_provided", domain=domain, provider=subscribe_host "dyndns_domain_not_provided", domain=domain, provider=DYNDNS_PROVIDER
) )
# Verify if domain is available # Verify if domain is available
if not _dyndns_available(subscribe_host, domain): if not _dyndns_available(domain):
raise YunohostValidationError("dyndns_unavailable", domain=domain) raise YunohostValidationError("dyndns_unavailable", domain=domain)
operation_logger.start() operation_logger.start()
@ -167,9 +135,9 @@ def dyndns_subscribe(
# Send subscription # Send subscription
try: try:
b64encoded_key = base64.b64encode(key.encode()).decode()
r = requests.post( r = requests.post(
"https://%s/key/%s?key_algo=hmac-sha512" f"https://{DYNDNS_PROVIDER}/key/{b64encoded_key}?key_algo=hmac-sha512",
% (subscribe_host, base64.b64encode(key.encode()).decode()),
data={"subdomain": domain}, data={"subdomain": domain},
timeout=30, timeout=30,
) )
@ -205,7 +173,6 @@ def dyndns_subscribe(
@is_unit_operation() @is_unit_operation()
def dyndns_update( def dyndns_update(
operation_logger, operation_logger,
dyn_host="dyndns.yunohost.org",
domain=None, domain=None,
key=None, key=None,
ipv4=None, ipv4=None,
@ -218,7 +185,6 @@ def dyndns_update(
Keyword argument: Keyword argument:
domain -- Full domain to update domain -- Full domain to update
dyn_host -- Dynette DNS server to inform
key -- Public DNS key key -- Public DNS key
ipv4 -- IP address to send ipv4 -- IP address to send
ipv6 -- IPv6 address to send ipv6 -- IPv6 address to send
@ -229,7 +195,7 @@ def dyndns_update(
# If domain is not given, try to guess it from keys available... # If domain is not given, try to guess it from keys available...
if domain is None: if domain is None:
(domain, key) = _guess_current_dyndns_domain(dyn_host) (domain, key) = _guess_current_dyndns_domain()
if domain is None: if domain is None:
raise YunohostValidationError("dyndns_no_domain_registered") raise YunohostValidationError("dyndns_no_domain_registered")
@ -251,33 +217,32 @@ def dyndns_update(
logger.debug("Building zone update file ...") logger.debug("Building zone update file ...")
lines = [ lines = [
"server %s" % dyn_host, f"server {DYNDNS_PROVIDER}",
"zone %s" % host, f"zone {host}",
] ]
auth_resolvers = []
for dns_auth in DYNDNS_DNS_AUTH:
for type_ in ["A", "AAAA"]:
ok, result = dig(dns_auth, type_)
if ok == "ok" and len(result) and result[0]:
auth_resolvers.append(result[0])
if not auth_resolvers:
raise YunohostError(
f"Failed to resolve IPv4/IPv6 for {DYNDNS_DNS_AUTH} ?", raw_msg=True
)
def resolve_domain(domain, rdtype): def resolve_domain(domain, rdtype):
ok, result = dig(dyn_host, "A") ok, result = dig(domain, rdtype, resolvers=auth_resolvers)
dyn_host_ipv4 = result[0] if ok == "ok" and len(result) else None
if not dyn_host_ipv4:
raise YunohostError(
"Failed to resolve IPv4 for %s ?" % dyn_host, raw_msg=True
)
ok, result = dig(dyn_host, "AAAA")
dyn_host_ipv6 = result[0] if ok == "ok" and len(result) else None
if not dyn_host_ipv6:
raise YunohostError(
"Failed to resolve IPv6 for %s ?" % dyn_host, raw_msg=True
)
ok, result = dig(domain, rdtype, resolvers=[dyn_host_ipv4, dyn_host_ipv6])
if ok == "ok": if ok == "ok":
return result[0] if len(result) else None return result[0] if len(result) else None
elif result[0] == "Timeout": elif result[0] == "Timeout":
logger.debug( logger.debug(
"Timed-out while trying to resolve %s record for %s using %s" f"Timed-out while trying to resolve {rdtype} record for {domain}"
% (rdtype, domain, dyn_host)
) )
else: else:
return None return None
@ -388,19 +353,21 @@ def dyndns_update(
) )
# Legacy
def dyndns_installcron(): def dyndns_installcron():
logger.warning( logger.warning(
"This command is deprecated. The dyndns cron job should automatically be added/removed by the regenconf depending if there's a private key in /etc/yunohost/dyndns. You can run the regenconf yourself with 'yunohost tools regen-conf yunohost'." "This command is deprecated. The dyndns cron job should automatically be added/removed by the regenconf depending if there's a private key in /etc/yunohost/dyndns. You can run the regenconf yourself with 'yunohost tools regen-conf yunohost'."
) )
# Legacy
def dyndns_removecron(): def dyndns_removecron():
logger.warning( logger.warning(
"This command is deprecated. The dyndns cron job should automatically be added/removed by the regenconf depending if there's a private key in /etc/yunohost/dyndns. You can run the regenconf yourself with 'yunohost tools regen-conf yunohost'." "This command is deprecated. The dyndns cron job should automatically be added/removed by the regenconf depending if there's a private key in /etc/yunohost/dyndns. You can run the regenconf yourself with 'yunohost tools regen-conf yunohost'."
) )
def _guess_current_dyndns_domain(dyn_host): def _guess_current_dyndns_domain():
""" """
This function tries to guess which domain should be updated by This function tries to guess which domain should be updated by
"dyndns_update()" because there's not proper management of the current "dyndns_update()" because there's not proper management of the current
@ -423,7 +390,7 @@ def _guess_current_dyndns_domain(dyn_host):
# current domain beause that's not the one we want to update..) # current domain beause that's not the one we want to update..)
# If there's only 1 such key found, then avoid doing the request # If there's only 1 such key found, then avoid doing the request
# for nothing (that's very probably the one we want to find ...) # for nothing (that's very probably the one we want to find ...)
if len(paths) > 1 and _dyndns_available(dyn_host, _domain): if len(paths) > 1 and _dyndns_available(_domain):
continue continue
else: else:
return (_domain, path) return (_domain, path)

View file

@ -45,7 +45,6 @@ from yunohost.app_catalog import (
_update_apps_catalog, _update_apps_catalog,
) )
from yunohost.domain import domain_add from yunohost.domain import domain_add
from yunohost.dyndns import _dyndns_available, _dyndns_provides
from yunohost.firewall import firewall_upnp from yunohost.firewall import firewall_upnp
from yunohost.service import service_start, service_enable from yunohost.service import service_start, service_enable
from yunohost.regenconf import regen_conf from yunohost.regenconf import regen_conf
@ -205,12 +204,12 @@ def tools_postinstall(
password -- YunoHost admin password password -- YunoHost admin password
""" """
from yunohost.dyndns import _dyndns_available
from yunohost.utils.dns import is_yunohost_dyndns_domain
from yunohost.utils.password import assert_password_is_strong_enough from yunohost.utils.password import assert_password_is_strong_enough
from yunohost.domain import domain_main_domain from yunohost.domain import domain_main_domain
import psutil import psutil
dyndns_provider = "dyndns.yunohost.org"
# Do some checks at first # Do some checks at first
if os.path.isfile("/etc/yunohost/installed"): if os.path.isfile("/etc/yunohost/installed"):
raise YunohostValidationError("yunohost_already_installed") raise YunohostValidationError("yunohost_already_installed")
@ -235,33 +234,28 @@ def tools_postinstall(
if not force_password: if not force_password:
assert_password_is_strong_enough("admin", password) assert_password_is_strong_enough("admin", password)
if not ignore_dyndns: # If this is a nohost.me/noho.st, actually check for availability
# Check if yunohost dyndns can handle the given domain if not ignore_dyndns and is_yunohost_dyndns_domain(domain):
# (i.e. is it a .nohost.me ? a .noho.st ?) # (Except if the user explicitly said he/she doesn't care about dyndns)
try: if ignore_dyndns:
is_nohostme_or_nohost = _dyndns_provides(dyndns_provider, domain) dyndns = False
# If an exception is thrown, most likely we don't have internet # Check if the domain is available...
# connectivity or something. Assume that this domain isn't manageable else:
# and inform the user that we could not contact the dyndns host server. try:
except Exception: available = _dyndns_available(domain)
logger.warning( # If an exception is thrown, most likely we don't have internet
m18n.n("dyndns_provider_unreachable", provider=dyndns_provider) # connectivity or something. Assume that this domain isn't manageable
) # and inform the user that we could not contact the dyndns host server.
is_nohostme_or_nohost = False except Exception:
logger.warning(
m18n.n("dyndns_provider_unreachable", provider="dyndns.yunohost.org")
)
# If this is a nohost.me/noho.st, actually check for availability if available:
if is_nohostme_or_nohost:
# (Except if the user explicitly said he/she doesn't care about dyndns)
if ignore_dyndns:
dyndns = False
# Check if the domain is available...
elif _dyndns_available(dyndns_provider, domain):
dyndns = True dyndns = True
# If not, abort the postinstall # If not, abort the postinstall
else: else:
raise YunohostValidationError("dyndns_unavailable", domain=domain) raise YunohostValidationError("dyndns_unavailable", domain=domain)
else:
dyndns = False
else: else:
dyndns = False dyndns = False