fix: ssh-copy-id interactive (capture=False) so password prompt visible; encrypted keys just warned, not source-server passphrase confusion; clear auth choice menu for target

This commit is contained in:
2026-05-22 23:48:58 +04:00
parent 6d6418c059
commit bd16e339c0

View File

@@ -105,20 +105,21 @@ def generate_temp_keypair(host_label):
def ssh_copy_id(host, user, port, pubkey_path): def ssh_copy_id(host, user, port, pubkey_path):
"""Копирует публичный ключ на target через ssh-copy-id или вручную.""" """Копирует публичный ключ на target через ssh-copy-id. Интерактивно, т.к. запрашивает пароль target."""
if exists("ssh-copy-id"): if exists("ssh-copy-id"):
info("Копируем публичный ключ через ssh-copy-id ...") warn("⚠ Сейчас запустится ssh-copy-id — на target запросит ПАРОЛЬ. Введите его ниже ↓")
# Добавляем SSH опции чтобы избежать зависания # Запускаем без capture_output, чтобы prompt пароля был виден пользователю
r = run( r = run(
f"ssh-copy-id -p {port} -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new -i '{pubkey_path}' {user}@{host}", f"ssh-copy-id -p {port} -o ConnectTimeout=30 -o StrictHostKeyChecking=accept-new -i '{pubkey_path}' {user}@{host}",
check=False, check=False,
timeout=60 capture=False,
timeout=120
) )
if r.returncode == 0: if r.returncode == 0:
success("Ключ добавлен на target через ssh-copy-id") success("Ключ добавлен на target через ssh-copy-id")
return True return True
else: else:
warn(f"ssh-copy-id не удался: {r.stderr.strip()[:120]}") warn(f"ssh-copy-id не удался (код: {r.returncode})")
return False return False
@@ -218,28 +219,21 @@ def pick_or_setup_ssh_key(host, user, port):
if working_key: if working_key:
return working_key["private"] return working_key["private"]
# 3. Проверить ключи С passphrase (если пользователь знает) # 3. Проверить ключи — если зашифрован и не подходит, просто сообщаем и идём дальше
encrypted_count = 0
for k in keys: for k in keys:
label = os.path.basename(k["private"]) label = os.path.basename(k["private"])
# Проверим, не passphrase ли мешает
ok, _, err = check_ssh_connectivity(host, user, port, key_path=k["private"]) ok, _, err = check_ssh_connectivity(host, user, port, key_path=k["private"])
if not ok and ("passphrase" in err.lower() or "password" in err.lower()): if not ok and ("passphrase" in err.lower() or "password" in err.lower()):
if confirm(f"Ключ '{label}' зашифрован. Ввести passphrase и попробовать", default="y"): warn(f" ! Ключ '{label}' зашифрован (passphrase). Пропускаем.")
from core.color import prompt_password encrypted_count += 1
pw = prompt_password(f"Passphrase для {label}") if encrypted_count and not working_key:
if pw and try_key_with_passphrase(k["private"], pw): info(f"Все найденные ключи зашифрованы или не подходят для {host}.")
# После ssh-add ключ должен работать
ok2, _, _ = check_ssh_connectivity(host, user, port, key_path=k["private"])
if ok2:
success(f"Ключ '{label}' разблокирован и работает!")
return k["private"]
else:
warn(f"Passphrase введён, но ключ всё равно не подходит для {host}")
# 4. Ничего не работает — выбор метода # 4. Ничего не работает — выбор метода
print() print()
info("Выберите способ подключения к target:") info("Выберите способ подключения к target:")
print(" 1 Пароль (sshpass) — ввести пароль прямо сейчас") print(" 1 Пароль (sshpass) — ввести пароль от target прямо сейчас")
print(" 2 Сгенерировать временный ed25519 ключ — скрипт сам сделает ssh-copy-id") print(" 2 Сгенерировать временный ed25519 ключ — скрипт сам сделает ssh-copy-id")
print(" 0 Отмена — передать архив вручную") print(" 0 Отмена — передать архив вручную")
choice = prompt("Ваш выбор", default="1").strip() choice = prompt("Ваш выбор", default="1").strip()