From e8602b158680a92903b930a9d9a25e9c2c458685 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Fri, 24 Jan 2020 14:03:00 +0100 Subject: [PATCH] Add pytest OpenSSH transport with password The pytest_multihost transport does not provide password-based authentication for OpenSSH transport. The OpenSSH command line tool has no API to pass in a password securely. The patch implements a custom transport that uses sshpass hack. It is not recommended for production but good enough for testing. Reviewed-By: Alexander Bokovoy --- freeipa.spec.in | 2 + ipatests/pytest_ipa/integration/host.py | 4 ++ ipatests/pytest_ipa/integration/transport.py | 48 ++++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 ipatests/pytest_ipa/integration/transport.py diff --git a/freeipa.spec.in b/freeipa.spec.in index dc1a13b6a..7beed1ebc 100755 --- a/freeipa.spec.in +++ b/freeipa.spec.in @@ -750,6 +750,8 @@ Requires: python3-pytest-sourceorder Requires: python3-sssdconfig >= %{sssd_version} Requires: tar Requires: xz +Requires: openssh-clients +Requires: sshpass %description -n python3-ipatests IPA is an integrated solution to provide centrally managed Identity (users, diff --git a/ipatests/pytest_ipa/integration/host.py b/ipatests/pytest_ipa/integration/host.py index dca28e584..c4edbd9fc 100644 --- a/ipatests/pytest_ipa/integration/host.py +++ b/ipatests/pytest_ipa/integration/host.py @@ -31,6 +31,7 @@ from ipapython import ipaldap from .fips import ( is_fips_enabled, enable_userspace_fips, disable_userspace_fips ) +from .transport import IPAOpenSSHTransport FIPS_NOISE_RE = re.compile(br"FIPS mode initialized\r?\n?") @@ -65,6 +66,8 @@ class LDAPClientWithoutCertCheck(ipaldap.LDAPClient): class Host(pytest_multihost.host.Host): """Representation of a remote IPA host""" + transport_class = IPAOpenSSHTransport + def __init__(self, domain, hostname, role, ip=None, external_hostname=None, username=None, password=None, test_dir=None, host_type=None): @@ -203,3 +206,4 @@ class WinHost(pytest_multihost.host.WinHost): This serves as a sketch class once we move from manual preparation of Active Directory to the automated setup. """ + transport_class = IPAOpenSSHTransport diff --git a/ipatests/pytest_ipa/integration/transport.py b/ipatests/pytest_ipa/integration/transport.py new file mode 100644 index 000000000..0b9f7eec4 --- /dev/null +++ b/ipatests/pytest_ipa/integration/transport.py @@ -0,0 +1,48 @@ +# +# Copyright (C) 2020 FreeIPA Contributors see COPYING for license +# +"""Enhanced SSH transport for pytest multihost + +Provides SSH password login for OpenSSH transport +""" +import os + +from pytest_multihost.transport import OpenSSHTransport + + +class IPAOpenSSHTransport(OpenSSHTransport): + def _get_ssh_argv(self): + """Return the path to SSH and options needed for every call""" + control_file = os.path.join(self.control_dir.path, "control") + known_hosts_file = os.path.join(self.control_dir.path, "known_hosts") + + argv = [ + "ssh", + "-l", + self.host.ssh_username, + "-o", + "ControlPath=%s" % control_file, + "-o", + "StrictHostKeyChecking=no", + "-o", + "UserKnownHostsFile=%s" % known_hosts_file, + ] + + if self.host.ssh_key_filename: + key_filename = os.path.expanduser(self.host.ssh_key_filename) + argv.extend(["-i", key_filename]) + elif self.host.ssh_password: + password_file = os.path.join(self.control_dir.path, "password") + with open(password_file, "w") as f: + os.fchmod(f.fileno(), 600) + f.write(self.host.ssh_password) + f.write("\n") + argv = ["sshpass", f"-f{password_file}"] + argv + else: + self.log.critical("No SSH credentials configured") + raise RuntimeError("No SSH credentials configured") + + argv.append(self.host.external_hostname) + self.log.debug("SSH invocation: %s", argv) + + return argv