diff options
author | Suren A. Chilingaryan <csa@suren.me> | 2025-09-08 18:46:38 +0400 |
---|---|---|
committer | Suren A. Chilingaryan <csa@suren.me> | 2025-09-08 18:46:38 +0400 |
commit | 062041ee31251e5e8f175f0e7baaee71a2f05709 (patch) | |
tree | a3fc72e26e7db17d1e1ebd03dc98c7c8b79ce08b /custom_components/patch_asyncssh | |
parent | adae7311eaec8589d65c52241c21be5625013b08 (diff) | |
download | hass-062041ee31251e5e8f175f0e7baaee71a2f05709.tar.gz hass-062041ee31251e5e8f175f0e7baaee71a2f05709.tar.bz2 hass-062041ee31251e5e8f175f0e7baaee71a2f05709.tar.xz hass-062041ee31251e5e8f175f0e7baaee71a2f05709.zip |
Recover connectivity to MerlinWRT: allow hostkeys beyond 'ssh-rsa'
Diffstat (limited to 'custom_components/patch_asyncssh')
-rw-r--r-- | custom_components/patch_asyncssh/__init__.py | 49 | ||||
-rw-r--r-- | custom_components/patch_asyncssh/__pycache__/__init__.cpython-312.pyc | bin | 0 -> 2002 bytes | |||
-rw-r--r-- | custom_components/patch_asyncssh/manifest.json | 9 |
3 files changed, 58 insertions, 0 deletions
diff --git a/custom_components/patch_asyncssh/__init__.py b/custom_components/patch_asyncssh/__init__.py new file mode 100644 index 0000000..cdcb095 --- /dev/null +++ b/custom_components/patch_asyncssh/__init__.py @@ -0,0 +1,49 @@ +# /config/custom_components/patch_asyncssh/__init__.py +from __future__ import annotations +import logging + +_LOGGER = logging.getLogger(__name__) + +async def async_setup(hass, config): + """Patch asyncssh at HA startup, then return True to finish setup.""" + try: + import asyncssh + + # Re-expose ReadHostKeysPolicy at top-level for libs that expect it + try: + from asyncssh.hostkeys import ReadHostKeysPolicy + asyncssh.ReadHostKeysPolicy = ReadHostKeysPolicy # type: ignore[attr-defined] + except Exception: + pass + + # Patch only once + if not getattr(asyncssh, "_ha_patched_connect", False): + _real_connect = asyncssh.connect + + def _patched_connect(*args, **kwargs): + # Expand allowed *server* host key algorithms to match Dropbear + algs = list( + kwargs.get("server_host_key_algs") + or kwargs.get("host_key_algs") + or [] + ) + wanted = ["ssh-ed25519", "ecdsa-sha2-nistp256", "rsa-sha2-256"] + for a in wanted: + if a not in algs: + algs.append(a) + kwargs["server_host_key_algs"] = algs or wanted + + return _real_connect(*args, **kwargs) + + asyncssh.connect = _patched_connect # type: ignore[assignment] + asyncssh._ha_patched_connect = True # type: ignore[attr-defined] + _LOGGER.info( + "patch_asyncssh: asyncssh.connect patched (added algs %s)", + ["ssh-ed25519", "ecdsa-sha2-nistp256", "rsa-sha2-256"], + ) + else: + _LOGGER.debug("patch_asyncssh: asyncssh.connect already patched") + except Exception: + _LOGGER.exception("patch_asyncssh: failed to patch asyncssh") + + return True diff --git a/custom_components/patch_asyncssh/__pycache__/__init__.cpython-312.pyc b/custom_components/patch_asyncssh/__pycache__/__init__.cpython-312.pyc Binary files differnew file mode 100644 index 0000000..1788a71 --- /dev/null +++ b/custom_components/patch_asyncssh/__pycache__/__init__.cpython-312.pyc diff --git a/custom_components/patch_asyncssh/manifest.json b/custom_components/patch_asyncssh/manifest.json new file mode 100644 index 0000000..7017f14 --- /dev/null +++ b/custom_components/patch_asyncssh/manifest.json @@ -0,0 +1,9 @@ +{ + "domain": "patch_asyncssh", + "name": "Patch AsyncSSH (host key algs)", + "version": "0.0.1", + "documentation": "", + "requirements": [], + "codeowners": [], + "iot_class": "local_push" +} |