summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatt Clay <matt@mystile.com>2022-12-13 17:46:33 -0800
committerMatt Clay <matt@mystile.com>2022-12-14 09:19:33 -0800
commit75b60b17ee1ff18ded04cf07b71e4ee32e673a0b (patch)
tree6d224069fddbacf1f81761e34adcbc9e3fbb771b
parentaa268b8db27193dd3f842ad72860ec3aefc3620d (diff)
downloadansible-75b60b17ee1ff18ded04cf07b71e4ee32e673a0b.tar.gz
ansible-test - Support RSA SHA-1 for SSH clients.
-rw-r--r--changelogs/fragments/ansible-test-container-management.yml5
-rw-r--r--test/lib/ansible_test/_internal/connections.py8
-rw-r--r--test/lib/ansible_test/_internal/host_profiles.py14
-rw-r--r--test/lib/ansible_test/_internal/inventory.py5
-rw-r--r--test/lib/ansible_test/_internal/ssh.py51
5 files changed, 75 insertions, 8 deletions
diff --git a/changelogs/fragments/ansible-test-container-management.yml b/changelogs/fragments/ansible-test-container-management.yml
index 04961b98ee..293d632713 100644
--- a/changelogs/fragments/ansible-test-container-management.yml
+++ b/changelogs/fragments/ansible-test-container-management.yml
@@ -39,6 +39,11 @@ minor_changes:
adding the ``retry/never`` alias. This is useful for tests that cannot pass on a retry or are too
slow to make retries useful.
- ansible-test - The ``ansible-test env`` command now detects and reports the container ID if running in a container.
+ - ansible-test - SSH connections from OpenSSH 8.8+ to CentOS 6 containers now work without additional configuration.
+ However, clients older than OpenSSH 7.0 can no longer connect to CentOS 6 containers as a result.
+ The container must have ``centos6`` in the image name for this work-around to be applied.
+ - ansible-test - SSH shell connections from OpenSSH 8.8+ to ansible-test provisioned network instances now work without additional configuration.
+ However, clients older than OpenSSH 7.0 can no longer open shell sessions for ansible-test provisioned network instances as a result.
bugfixes:
- ansible-test - Multiple containers now work under Podman without specifying the ``--docker-network`` option.
- ansible-test - Prevent concurrent / repeat pulls of the same container image.
diff --git a/test/lib/ansible_test/_internal/connections.py b/test/lib/ansible_test/_internal/connections.py
index 829d9d3285..4823b1a476 100644
--- a/test/lib/ansible_test/_internal/connections.py
+++ b/test/lib/ansible_test/_internal/connections.py
@@ -34,6 +34,7 @@ from .docker_util import (
from .ssh import (
SshConnectionDetail,
+ ssh_options_to_list,
)
from .become import (
@@ -123,7 +124,7 @@ class SshConnection(Connection):
self.options = ['-i', settings.identity_file]
- ssh_options = dict(
+ ssh_options: dict[str, t.Union[int, str]] = dict(
BatchMode='yes',
StrictHostKeyChecking='no',
UserKnownHostsFile='/dev/null',
@@ -131,8 +132,9 @@ class SshConnection(Connection):
ServerAliveCountMax=4,
)
- for ssh_option in sorted(ssh_options):
- self.options.extend(['-o', f'{ssh_option}={ssh_options[ssh_option]}'])
+ ssh_options.update(settings.options)
+
+ self.options.extend(ssh_options_to_list(ssh_options))
def run(self,
command: list[str],
diff --git a/test/lib/ansible_test/_internal/host_profiles.py b/test/lib/ansible_test/_internal/host_profiles.py
index 488cfcea80..b4742d88eb 100644
--- a/test/lib/ansible_test/_internal/host_profiles.py
+++ b/test/lib/ansible_test/_internal/host_profiles.py
@@ -998,6 +998,10 @@ class DockerProfile(ControllerHostProfile[DockerConfig], SshTargetHostProfile[Do
port=port,
identity_file=SshKey(self.args).key,
python_interpreter=self.python.path,
+ # CentOS 6 uses OpenSSH 5.3, making it incompatible with the default configuration of OpenSSH 8.8 and later clients.
+ # Since only CentOS 6 is affected, and it is only supported by ansible-core 2.12, support for RSA SHA-1 is simply hard-coded here.
+ # A substring is used to allow custom containers to work, not just the one provided with ansible-test.
+ enable_rsa_sha1='centos6' in self.config.image,
)
return [SshConnection(self.args, settings)]
@@ -1089,6 +1093,12 @@ class NetworkRemoteProfile(RemoteProfile[NetworkRemoteConfig]):
ansible_port=connection.port,
ansible_user=connection.username,
ansible_ssh_private_key_file=core_ci.ssh_key.key,
+ # VyOS 1.1.8 uses OpenSSH 5.5, making it incompatible with RSA SHA-256/512 used by Paramiko 2.9 and later.
+ # IOS CSR 1000V uses an ancient SSH server, making it incompatible with RSA SHA-256/512 used by Paramiko 2.9 and later.
+ # That means all network platforms currently offered by ansible-core-ci require support for RSA SHA-1, so it is simply hard-coded here.
+ # NOTE: This option only exists in ansible-core 2.14 and later. For older ansible-core versions, use of Paramiko 2.8.x or earlier is required.
+ # See: https://github.com/ansible/ansible/pull/78789
+ # See: https://github.com/ansible/ansible/pull/78842
ansible_paramiko_use_rsa_sha2_algorithms='no',
ansible_network_os=f'{self.config.collection}.{self.config.platform}' if self.config.collection else self.config.platform,
)
@@ -1132,6 +1142,10 @@ class NetworkRemoteProfile(RemoteProfile[NetworkRemoteConfig]):
port=core_ci.connection.port,
user=core_ci.connection.username,
identity_file=core_ci.ssh_key.key,
+ # VyOS 1.1.8 uses OpenSSH 5.5, making it incompatible with the default configuration of OpenSSH 8.8 and later clients.
+ # IOS CSR 1000V uses an ancient SSH server, making it incompatible with the default configuration of OpenSSH 8.8 and later clients.
+ # That means all network platforms currently offered by ansible-core-ci require support for RSA SHA-1, so it is simply hard-coded here.
+ enable_rsa_sha1=True,
)
return [SshConnection(self.args, settings)]
diff --git a/test/lib/ansible_test/_internal/inventory.py b/test/lib/ansible_test/_internal/inventory.py
index 9cfd439413..6abf9ede96 100644
--- a/test/lib/ansible_test/_internal/inventory.py
+++ b/test/lib/ansible_test/_internal/inventory.py
@@ -25,6 +25,10 @@ from .host_profiles import (
WindowsRemoteProfile,
)
+from .ssh import (
+ ssh_options_to_str,
+)
+
def create_controller_inventory(args: EnvironmentConfig, path: str, controller_host: ControllerHostProfile) -> None:
"""Create and return inventory for use in controller-only integration tests."""
@@ -149,6 +153,7 @@ def create_posix_inventory(args: EnvironmentConfig, path: str, target_hosts: lis
ansible_port=ssh.settings.port,
ansible_user=ssh.settings.user,
ansible_ssh_private_key_file=ssh.settings.identity_file,
+ ansible_ssh_extra_args=ssh_options_to_str(ssh.settings.options),
)
if ssh.become:
diff --git a/test/lib/ansible_test/_internal/ssh.py b/test/lib/ansible_test/_internal/ssh.py
index a5b40c8bbb..fd01ff253f 100644
--- a/test/lib/ansible_test/_internal/ssh.py
+++ b/test/lib/ansible_test/_internal/ssh.py
@@ -2,6 +2,7 @@
from __future__ import annotations
import dataclasses
+import itertools
import json
import os
import random
@@ -38,10 +39,40 @@ class SshConnectionDetail:
identity_file: str
python_interpreter: t.Optional[str] = None
shell_type: t.Optional[str] = None
+ enable_rsa_sha1: bool = False
def __post_init__(self):
self.name = sanitize_host_name(self.name)
+ @property
+ def options(self) -> dict[str, str]:
+ """OpenSSH config options, which can be passed to the `ssh` CLI with the `-o` argument."""
+ options: dict[str, str] = {}
+
+ if self.enable_rsa_sha1:
+ # Newer OpenSSH clients connecting to older SSH servers must explicitly enable ssh-rsa support.
+ # OpenSSH 8.8, released on 2021-09-26, deprecated using RSA with the SHA-1 hash algorithm (ssh-rsa).
+ # OpenSSH 7.2, released on 2016-02-29, added support for using RSA with SHA-256/512 hash algorithms.
+ # See: https://www.openssh.com/txt/release-8.8
+ algorithms = '+ssh-rsa' # append the algorithm to the default list, requires OpenSSH 7.0 or later
+
+ options.update(dict(
+ # Host key signature algorithms that the client wants to use.
+ # Available options can be found with `ssh -Q HostKeyAlgorithms` or `ssh -Q key` on older clients.
+ # This option was updated in OpenSSH 7.0, released on 2015-08-11, to support the "+" prefix.
+ # See: https://www.openssh.com/txt/release-7.0
+ HostKeyAlgorithms=algorithms,
+ # Signature algorithms that will be used for public key authentication.
+ # Available options can be found with `ssh -Q PubkeyAcceptedAlgorithms` or `ssh -Q key` on older clients.
+ # This option was added in OpenSSH 7.0, released on 2015-08-11.
+ # See: https://www.openssh.com/txt/release-7.0
+ # This option is an alias for PubkeyAcceptedAlgorithms, which was added in OpenSSH 8.5.
+ # See: https://www.openssh.com/txt/release-8.5
+ PubkeyAcceptedKeyTypes=algorithms,
+ ))
+
+ return options
+
class SshProcess:
"""Wrapper around an SSH process."""
@@ -141,7 +172,7 @@ def create_ssh_command(
if ssh.user:
cmd.extend(['-l', ssh.user]) # user to log in as on the remote machine
- ssh_options = dict(
+ ssh_options: dict[str, t.Union[int, str]] = dict(
BatchMode='yes',
ExitOnForwardFailure='yes',
LogLevel='ERROR',
@@ -153,9 +184,7 @@ def create_ssh_command(
ssh_options.update(options or {})
- for key, value in sorted(ssh_options.items()):
- cmd.extend(['-o', '='.join([key, str(value)])])
-
+ cmd.extend(ssh_options_to_list(ssh_options))
cmd.extend(cli_args or [])
cmd.append(ssh.host)
@@ -165,6 +194,18 @@ def create_ssh_command(
return cmd
+def ssh_options_to_list(options: t.Union[dict[str, t.Union[int, str]], dict[str, str]]) -> list[str]:
+ """Format a dictionary of SSH options as a list suitable for passing to the `ssh` command."""
+ return list(itertools.chain.from_iterable(
+ ('-o', f'{key}={value}') for key, value in sorted(options.items())
+ ))
+
+
+def ssh_options_to_str(options: t.Union[dict[str, t.Union[int, str]], dict[str, str]]) -> str:
+ """Format a dictionary of SSH options as a string suitable for passing as `ansible_ssh_extra_args` in inventory."""
+ return shlex.join(ssh_options_to_list(options))
+
+
def run_ssh_command(
args: EnvironmentConfig,
ssh: SshConnectionDetail,
@@ -245,7 +286,7 @@ def generate_ssh_inventory(ssh_connections: list[SshConnectionDetail]) -> str:
ansible_pipelining='yes',
ansible_python_interpreter=ssh.python_interpreter,
ansible_shell_type=ssh.shell_type,
- ansible_ssh_extra_args='-o UserKnownHostsFile=/dev/null', # avoid changing the test environment
+ ansible_ssh_extra_args=ssh_options_to_str(dict(UserKnownHostsFile='/dev/null', **ssh.options)), # avoid changing the test environment
ansible_ssh_host_key_checking='no',
))) for ssh in ssh_connections),
),