From 2a6b82ed77a321e386b56e6ad8bf678eac4be781 Mon Sep 17 00:00:00 2001 From: nicholasyang Date: Wed, 9 Oct 2024 15:09:57 +0800 Subject: [PATCH 1/2] Dev: bootstrap: more robust implementation for ssh_merge (bsc#1230530) * Do not use legacy `write_remote_file`, which does not work well with `_guess_user_for_ssh`. * Implement deduplication when merging known_hosts --- crmsh/bootstrap.py | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/crmsh/bootstrap.py b/crmsh/bootstrap.py index ce1c4579f..f45a94001 100644 --- a/crmsh/bootstrap.py +++ b/crmsh/bootstrap.py @@ -12,6 +12,7 @@ # TODO: Make csync2 usage optional # TODO: Configuration file for bootstrap? import codecs +import io import os import subprocess import sys @@ -854,8 +855,8 @@ def init_ssh_impl(local_user: str, user_node_list: typing.List[typing.Tuple[str, public_key_list.append(swap_public_ssh_key(node, local_user, remote_user, local_user, remote_user, add=True)) hacluster_public_key_list.append(swap_public_ssh_key(node, 'hacluster', 'hacluster', local_user, remote_user, add=True)) if len(user_node_list) > 1: - shell_script = _merge_authorized_keys(public_key_list) - hacluster_shell_script = _merge_authorized_keys(hacluster_public_key_list) + shell_script = _merge_line_into_file('~/.ssh/authorized_keys', public_key_list).encode('utf-8') + hacluster_shell_script = _merge_line_into_file('~/.ssh/authorized_keys', hacluster_public_key_list).encode('utf-8') for i, (remote_user, node) in enumerate(user_node_list): result = utils.su_subprocess_run( local_user, @@ -884,16 +885,18 @@ def init_ssh_impl(local_user: str, user_node_list: typing.List[typing.Tuple[str, change_user_shell('hacluster', node) -def _merge_authorized_keys(keys: typing.List[str]) -> bytes: - shell_script = '''for key in "${keys[@]}"; do - grep -F "$key" ~/.ssh/authorized_keys > /dev/null || sed -i "\\$a $key" ~/.ssh/authorized_keys - done''' - keys_definition = ("keys+=('{}')\n".format(key) for key in keys) - buf = bytearray() +def _merge_line_into_file(path: str, lines: typing.Iterable[str]) -> str: + shell_script = '''[ -e "$path" ] || echo '# created by crmsh' > "$path" +for key in "${keys[@]}"; do + grep -F "$key" "$path" > /dev/null || sed -i "\\$a $key" "$path" +done''' + keys_definition = ("keys+=('{}')\n".format(key) for key in lines) + buf = io.StringIO() + buf.write(f'path={path}\n') for item in keys_definition: - buf.extend(item.encode('utf-8')) - buf.extend(shell_script.encode('utf-8')) - return buf + buf.write(item) + buf.write(shell_script) + return buf.getvalue() def _fetch_core_hosts(local_user, remote_user, remote_host) -> typing.Tuple[typing.List[str], typing.List[str]]: @@ -1831,7 +1834,7 @@ def join_ssh_merge(cluster_node, remote_user): rc, _, _ = utils.get_stdout_stderr_as_local_sudoer("ssh {} {} true".format(SSH_OPTION, utils.this_node())) assert rc == 0 - known_hosts_new = set() + known_hosts_new: set[str] = set() cat_cmd = "[ -e ~/.ssh/known_hosts ] && cat ~/.ssh/known_hosts || true" #logger_utils.log_only_to_file("parallax.call {} : {}".format(hosts, cat_cmd)) @@ -1841,10 +1844,9 @@ def join_ssh_merge(cluster_node, remote_user): known_hosts_new.update((utils.to_ascii(known_hosts_content) or "").splitlines()) if known_hosts_new: - hoststxt = "\n".join(sorted(known_hosts_new)) - #results = parallax.parallax_copy(hosts, tmpf, known_hosts_path, strict=False) + script = _merge_line_into_file('~/.ssh/known_hosts', known_hosts_new) for host in hosts: - utils.write_remote_file(hoststxt, "~/.ssh/known_hosts", utils.user_of(host), remote=host) + utils.get_stdout_or_raise_error(script, remote=host) def update_expected_votes(): From 85ace09e398d642f5d1b4ec7c3871c53fd47f26d Mon Sep 17 00:00:00 2001 From: nicholasyang Date: Wed, 9 Oct 2024 15:15:27 +0800 Subject: [PATCH 2/2] unused code removal --- crmsh/bootstrap.py | 4 +--- crmsh/utils.py | 15 --------------- 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/crmsh/bootstrap.py b/crmsh/bootstrap.py index f45a94001..86c6b854a 100644 --- a/crmsh/bootstrap.py +++ b/crmsh/bootstrap.py @@ -26,15 +26,13 @@ import yaml import socket -from tempfile import mktemp from string import Template from lxml import etree -import crmsh.parallax from . import config, constants from . import utils from . import xmlutil -from .cibconfig import mkset_obj, cib_factory +from .cibconfig import cib_factory from . import corosync from . import tmpfiles from . import lock diff --git a/crmsh/utils.py b/crmsh/utils.py index 69cf213f4..7811388c2 100644 --- a/crmsh/utils.py +++ b/crmsh/utils.py @@ -775,21 +775,6 @@ def str2file(s, fname, mod=0o644): return False return True -def write_remote_file(text, tofile, user, remote): - shell_script = f'''cat >> {tofile} << EOF -{text} -EOF -''' - result = subprocess_run_auto_ssh_no_input( - shell_script, - remote, - user, - stdout=subprocess.DEVNULL, - stderr=subprocess.PIPE, - ) - if result.returncode != 0: - raise ValueError("Failed to write to {}@{}/{}: {}".format(user, remote, tofile, result.stdout.decode('utf-8'))) - def copy_remote_textfile(remote_user, remote_node, remote_text_file, local_path): """ scp might lack permissions to copy the file for a non-root user.