diff mbox series

[09/24] devtool: ide-sdk: auto-disable ssh host key checking for loopback targets

Message ID 20260830142922.17241-10-adrian.freihofer@siemens.com
State New
Headers show
Series devtool: ide-sdk: NFS/slirp support, deploy filtering, and robustness fixes | expand

Commit Message

AdrianF Aug. 30, 2026, 2:28 p.m. UTC
From: Adrian Freihofer <adrian.freihofer@siemens.com>

QEMU instances reached via slirp/hostfwd (e.g. root@localhost) get a new
ephemeral ssh host key on every boot, so StrictHostKeyChecking would
fail on the second and later runs unless --no-host-check is passed
explicitly. Detect loopback targets (localhost, 127.0.0.1, ::1) and
disable host key checking automatically, both in TargetDevice and in
the generated do_install-through-bitbake script that re-parses the
target args on the build host.

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 scripts/lib/devtool/ide_sdk.py | 50 ++++++++++++++++++++++++++--------
 1 file changed, 38 insertions(+), 12 deletions(-)
diff mbox series

Patch

diff --git a/scripts/lib/devtool/ide_sdk.py b/scripts/lib/devtool/ide_sdk.py
index 719648a3eb..e45752aff1 100755
--- a/scripts/lib/devtool/ide_sdk.py
+++ b/scripts/lib/devtool/ide_sdk.py
@@ -45,22 +45,24 @@  class DevtoolIdeMode(Enum):
     shared = 'shared'
 
 
+# Hosts a ssh target is considered to loop back to the local machine, e.g. a
+# QEMU instance reached through slirp/hostfwd port forwarding (root@localhost)
+# which has an ephemeral ssh host key that changes on every boot.
+LOOPBACK_HOSTS = ('localhost', '127.0.0.1', '::1')
+
+
+def target_host(target):
+    return target.split('@')[-1]
+
+
+def is_loopback_target(target):
+    return target_host(target) in LOOPBACK_HOSTS
+
+
 class TargetDevice:
     """SSH remote login parameters"""
 
     def __init__(self, args):
-        self.extraoptions = []
-        if args.no_host_check:
-            self.extraoptions += ['-o', 'UserKnownHostsFile=/dev/null', '-o', 'StrictHostKeyChecking=no']
-        self.ssh_sshexec = 'ssh'
-        if args.ssh_exec:
-            self.ssh_sshexec = args.ssh_exec
-        self.ssh_port = []
-        if args.port:
-            self.ssh_port = ['-p', args.port]
-        if args.key:
-            self.extraoptions += ['-i', args.key]
-
         self.target = args.target
         target_sp = args.target.split('@')
         if len(target_sp) == 1:
@@ -72,6 +74,25 @@  class TargetDevice:
         else:
             logger.error("Invalid target argument: %s" % args.target)
 
+        no_host_check = args.no_host_check
+        if not no_host_check and is_loopback_target(args.target):
+            logger.debug(
+                "Target %s is a loopback address, disabling ssh host key checking "
+                "(assuming a QEMU instance with an ephemeral host key)." % args.target)
+            no_host_check = True
+
+        self.extraoptions = []
+        if no_host_check:
+            self.extraoptions += ['-o', 'UserKnownHostsFile=/dev/null', '-o', 'StrictHostKeyChecking=no']
+        self.ssh_sshexec = 'ssh'
+        if args.ssh_exec:
+            self.ssh_sshexec = args.ssh_exec
+        self.ssh_port = []
+        if args.port:
+            self.ssh_port = ['-p', args.port]
+        if args.key:
+            self.extraoptions += ['-i', args.key]
+
 
 class RecipeNative:
     """Base class for calling bitbake to provide a -native recipe"""
@@ -1324,6 +1345,8 @@  class RecipeModified:
                        'no_preserve', 'port', 'show_status', 'ssh_exec', 'strip', 'target']
         filtered_args_dict = {key: value for key, value in vars(
             args).items() if key in args_filter}
+        if is_loopback_target(filtered_args_dict['target']):
+            filtered_args_dict['no_host_check'] = True
         cmd_lines.append('filtered_args_dict = %s' % str(filtered_args_dict))
         cmd_lines.append('class Dict2Class(object):')
         cmd_lines.append('    def __init__(self, my_dict):')
@@ -1340,6 +1363,9 @@  class RecipeModified:
         cmd_lines.append('        i += 2')
         cmd_lines.append('    else:')
         cmd_lines.append('        i += 1')
+        cmd_lines.append(
+            "if filtered_args.target.split('@')[-1] in %s:" % str(LOOPBACK_HOSTS))
+        cmd_lines.append('    filtered_args.no_host_check = True')
         cmd_lines.append(
             'setattr(filtered_args, "recipename", "%s")' % self.bpn)
         cmd_lines.append('deploy_no_d("%s", "%s", "%s", "%s", "%s", "%s", %d, "%s", "%s", filtered_args)' %