From 1856e3a79d1e6cad2db40b67dce32b413c154660 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Fri, 16 Jan 2026 10:58:39 +1100 Subject: [PATCH] Add support for AddressFamily and ConnectTimeout in the .ssh/config when using `--remote-ssh-config`. --- CHANGELOG.md | 4 ++++ debian/changelog | 6 ++++++ enroll/remote.py | 53 +++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 2 +- rpm/enroll.spec | 4 +++- 5 files changed, 66 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0772cc4..99fd7e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.4.3 + + * Add support for AddressFamily and ConnectTimeout in the .ssh/config when using `--remote-ssh-config`. + # 0.4.2 * Support `--remote-ssh-config [path-to-ssh-config]` as an argument in case extra params are required beyond `--remote-port` or `--remote-user`. Note: `--remote-host` must still be set, but it can be an 'alias' represented by the 'Host' value in the ssh config. diff --git a/debian/changelog b/debian/changelog index 58e80e3..bd5049d 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +enroll (0.4.3) unstable; urgency=medium + + * Add support for AddressFamily and ConnectTimeout in the .ssh/config when using `--remote-ssh-config`. + + -- Miguel Jacq Fri, 16 Jan 2026 11:00 +1100 + enroll (0.4.2) unstable; urgency=medium * Support `--remote-ssh-config [path-to-ssh-config]` as an argument in case extra params are required beyond `--remote-port` or `--remote-user`. Note: `--remote-host` must still be set, but it can be an 'alias' represented by the 'Host' value in the ssh config. diff --git a/enroll/remote.py b/enroll/remote.py index c7b54a6..53e47b5 100644 --- a/enroll/remote.py +++ b/enroll/remote.py @@ -379,6 +379,10 @@ def _remote_harvest( sock = None hostkey_name = connect_host + # Timeouts derived from ssh_config if set (ConnectTimeout). + # Used both for socket connect (when we create one) and Paramiko handshake/auth. + connect_timeout: Optional[float] = None + if remote_ssh_config: from paramiko.config import SSHConfig # type: ignore from paramiko.proxy import ProxyCommand # type: ignore @@ -411,14 +415,58 @@ def _remote_harvest( else: key_filename = str(Path(str(ident)).expanduser()) + # Honour OpenSSH ConnectTimeout (seconds) if present. + if hcfg.get("connecttimeout"): + try: + connect_timeout = float(str(hcfg.get("connecttimeout"))) + except (TypeError, ValueError): + connect_timeout = None + proxycmd = hcfg.get("proxycommand") + + # AddressFamily support: inet (IPv4 only), inet6 (IPv6 only), any (default). + addrfam = str(hcfg.get("addressfamily") or "any").strip().lower() + family: Optional[int] = None + if addrfam == "inet": + family = _socket.AF_INET + elif addrfam == "inet6": + family = _socket.AF_INET6 + if proxycmd: + # ProxyCommand provides the transport; AddressFamily doesn't apply here. sock = ProxyCommand(str(proxycmd)) + elif family is not None: + # Enforce the requested address family by pre-connecting the socket and + # passing it into Paramiko via sock=. + last_err: Optional[OSError] = None + infos = _socket.getaddrinfo( + connect_host, connect_port, family, _socket.SOCK_STREAM + ) + for af, socktype, proto, _, sa in infos: + s = _socket.socket(af, socktype, proto) + if connect_timeout is not None: + s.settimeout(connect_timeout) + try: + s.connect(sa) + sock = s + break + except OSError as e: + last_err = e + try: + s.close() + except Exception: + pass # nosec + if sock is None and last_err is not None: + raise last_err elif hostkey_name != connect_host: # If HostKeyAlias is used, connect to HostName via a socket but # use HostKeyAlias for known_hosts lookups. - sock = _socket.create_connection((connect_host, connect_port)) + sock = _socket.create_connection( + (connect_host, connect_port), timeout=connect_timeout + ) + # If we created a socket (sock!=None), pass hostkey_name as hostname so + # known_hosts lookup uses HostKeyAlias (or whatever hostkey_name resolved to). ssh.connect( hostname=hostkey_name if sock is not None else connect_host, port=connect_port, @@ -427,6 +475,9 @@ def _remote_harvest( sock=sock, allow_agent=True, look_for_keys=True, + timeout=connect_timeout, + banner_timeout=connect_timeout, + auth_timeout=connect_timeout, ) # If no username was explicitly provided, SSH may have selected a default. diff --git a/pyproject.toml b/pyproject.toml index 92756d1..b4e33dc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "enroll" -version = "0.4.2" +version = "0.4.3" description = "Enroll a server's running state retrospectively into Ansible" authors = ["Miguel Jacq "] license = "GPL-3.0-or-later" diff --git a/rpm/enroll.spec b/rpm/enroll.spec index 98f3f8f..1217762 100644 --- a/rpm/enroll.spec +++ b/rpm/enroll.spec @@ -1,4 +1,4 @@ -%global upstream_version 0.4.2 +%global upstream_version 0.4.3 Name: enroll Version: %{upstream_version} @@ -43,6 +43,8 @@ Enroll a server's running state retrospectively into Ansible. %{_bindir}/enroll %changelog +* Fri Jan 16 2026 Miguel Jacq - %{version}-%{release} +- Add support for AddressFamily and ConnectTimeout in the .ssh/config when using `--remote-ssh-config`. * Tue Jan 13 2026 Miguel Jacq - %{version}-%{release} - Support `--remote-ssh-config [path-to-ssh-config]` as an argument in case extra params are required beyond `--remote-port` or `--remote-user`. Note: `--remote-host` must still be s et, but it can be an 'alias' represented by the 'Host' value in the ssh config.