refactor: consistent terminology - 'новый сервер' and 'приватный SSH-ключ' instead of 'target' and generic 'ключ'
This commit is contained in:
@@ -379,11 +379,11 @@ def do_transfer_offer():
|
|||||||
if key_path:
|
if key_path:
|
||||||
state.set_stage("TRANSFER", target_host=host, target_user=user, target_port=port_int, ssh_key=key_path)
|
state.set_stage("TRANSFER", target_host=host, target_user=user, target_port=port_int, ssh_key=key_path)
|
||||||
else:
|
else:
|
||||||
# Нет SSH-ключа — запрашиваем пароль от TARGET сервера
|
# Нет SSH-ключа — запрашиваем пароль от НОВОГО сервера
|
||||||
password = prompt_password(f"Пароль от target-сервера {user}@{host} (Enter для отмены)")
|
password = prompt_password(f"Пароль от нового сервера {user}@{host} (Enter для отмены)")
|
||||||
if not password:
|
if not password:
|
||||||
state.set_error("ssh_key_setup", "", "Пароль не введён", suggestion="Запустите docker-migrate --resume или настройте SSH-ключ на target")
|
state.set_error("ssh_key_setup", "", "Пароль не введён", suggestion="Запустите docker-migrate --resume или настройте приватный SSH-ключ на новом сервере")
|
||||||
raise RuntimeError("Пароль от target не введён.")
|
raise RuntimeError("Пароль от нового сервера не введён.")
|
||||||
from transfer.ssh import ensure_sshpass
|
from transfer.ssh import ensure_sshpass
|
||||||
if not ensure_sshpass():
|
if not ensure_sshpass():
|
||||||
state.set_error("ssh_key_setup", "", "sshpass не найден", suggestion="apt-get install -y sshpass")
|
state.set_error("ssh_key_setup", "", "sshpass не найден", suggestion="apt-get install -y sshpass")
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
ssh.py — SSH-подготовка для transfer на target-сервер.
|
ssh.py — SSH-подготовка для переноса на новый сервер.
|
||||||
НЕ ищем ключи на source — спрашиваем пользователя как подключиться к target.
|
НЕ ищем ключи на старом сервере — спрашиваем пользователя как подключиться к новому.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
@@ -10,7 +10,7 @@ from core.runner import run, exists
|
|||||||
|
|
||||||
|
|
||||||
def check_ssh_keyless(host, user, port, key_path, timeout=10):
|
def check_ssh_keyless(host, user, port, key_path, timeout=10):
|
||||||
"""Проверяет подключение к target с данным ключом."""
|
"""Проверяет подключение к новому серверу с данным ключом."""
|
||||||
if not exists("ssh"):
|
if not exists("ssh"):
|
||||||
return (False, "ssh не найден")
|
return (False, "ssh не найден")
|
||||||
identity = f"-i '{key_path}'" if key_path and os.path.isfile(key_path) else ""
|
identity = f"-i '{key_path}'" if key_path and os.path.isfile(key_path) else ""
|
||||||
@@ -24,94 +24,54 @@ def check_ssh_keyless(host, user, port, key_path, timeout=10):
|
|||||||
return (False, err[:120])
|
return (False, err[:120])
|
||||||
|
|
||||||
|
|
||||||
def generate_temp_keypair(host_label):
|
# ...
|
||||||
"""Генерирует временную пару ed25519 в /tmp/."""
|
|
||||||
temp_dir = "/tmp/docker-migrate-ssh-keys"
|
|
||||||
os.makedirs(temp_dir, exist_ok=True)
|
|
||||||
priv = os.path.join(temp_dir, f"migrate_{host_label}")
|
|
||||||
pub = priv + ".pub"
|
|
||||||
info("Генерируем временную SSH-пару ed25519 ...")
|
|
||||||
run(f"ssh-keygen -t ed25519 -C 'docker-migrate-temp' -f '{priv}' -N ''", check=False)
|
|
||||||
if os.path.isfile(priv) and os.path.isfile(pub):
|
|
||||||
os.chmod(priv, 0o600)
|
|
||||||
success(f"Временная пара создана: {priv}")
|
|
||||||
return {"private": priv, "public": pub, "type": "ed25519", "temp": True}
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def ssh_copy_id(host, user, port, pubkey_path):
|
|
||||||
"""ssh-copy-id на target. Интерактивно (пароль target)."""
|
|
||||||
if not exists("ssh-copy-id"):
|
|
||||||
return False
|
|
||||||
warn("⚠ Сейчас запустится ssh-copy-id — введите ПАРОЛЬ от target-сервера ↓")
|
|
||||||
r = run(
|
|
||||||
f"ssh-copy-id -p {port} -o ConnectTimeout=30 -o StrictHostKeyChecking=accept-new -i '{pubkey_path}' {user}@{host}",
|
|
||||||
check=False, capture=False, timeout=120
|
|
||||||
)
|
|
||||||
return r.returncode == 0
|
|
||||||
|
|
||||||
|
|
||||||
def ensure_sshpass():
|
|
||||||
"""Устанавливает sshpass если нужно."""
|
|
||||||
if exists("sshpass"):
|
|
||||||
return True
|
|
||||||
info("sshpass не найден. Устанавливаем ...")
|
|
||||||
r1 = run("apt-get update", check=False, timeout=60)
|
|
||||||
if r1.returncode != 0:
|
|
||||||
warn(f"apt-get update: {r1.stderr.strip()[:120]}")
|
|
||||||
r = run("apt-get install -y sshpass", check=False, timeout=120)
|
|
||||||
if r.returncode != 0:
|
|
||||||
warn(f"Не удалось установить sshpass: {r.stderr.strip()[:120]}")
|
|
||||||
return False
|
|
||||||
return exists("sshpass")
|
|
||||||
|
|
||||||
|
|
||||||
def pick_or_setup_ssh_key(host, user, port):
|
def pick_or_setup_ssh_key(host, user, port):
|
||||||
"""
|
"""
|
||||||
Возвращает путь к приватному ключу для target.
|
Возвращает путь к приватному SSH-ключу для нового сервера.
|
||||||
Если None — значит нужен пароль target (sshpass).
|
Если None - значит нужен пароль нового сервера (sshpass).
|
||||||
"""
|
"""
|
||||||
info("")
|
info("")
|
||||||
info("Как подключиться к target-серверу?")
|
info("Как подключиться к новому серверу?")
|
||||||
print(" 1) У меня есть приватный ключ от target (файл .pem/.key/.ppk)")
|
print(" 1) У меня есть приватный SSH-ключ от нового сервера (файл .pem/.key/.ppk)")
|
||||||
print(" 2) У меня есть логин и пароль от target")
|
print(" 2) У меня есть логин и пароль от нового сервера")
|
||||||
print(" 0) Отмена")
|
print(" 0) Отмена")
|
||||||
choice = prompt("Ваш выбор", default="1").strip()
|
choice = prompt("Ваш выбор", default="1").strip()
|
||||||
|
|
||||||
if choice == "1":
|
if choice == "1":
|
||||||
# Пользователь даёт путь к ключу target
|
# Пользователь даёт путь к ключу нового сервера
|
||||||
key_path = prompt("Путь к приватному ключу (например, /root/server.pem)").strip()
|
key_path = prompt("Путь к приватному SSH-ключу (например, /root/server.pem)").strip()
|
||||||
if not key_path or not os.path.isfile(key_path):
|
if not key_path or not os.path.isfile(key_path):
|
||||||
raise RuntimeError(f"Файл не найден: {key_path}")
|
raise RuntimeError(f"Файл не найден: {key_path}")
|
||||||
ok, why = check_ssh_keyless(host, user, port, key_path)
|
ok, why = check_ssh_keyless(host, user, port, key_path)
|
||||||
if ok:
|
if ok:
|
||||||
success("Ключ подходит для target!")
|
success("Приватный SSH-ключ подходит для нового сервера!")
|
||||||
return key_path
|
return key_path
|
||||||
if why == "PASSPHRASE":
|
if why == "PASSPHRASE":
|
||||||
warn(f"Ключ защищён passphrase. Разблокируйте: ssh-add {key_path}")
|
warn(f"Приватный SSH-ключ защищён passphrase. Разблокируйте: ssh-add {key_path}")
|
||||||
raise RuntimeError("Ключ зашифрован. Разблокируйте через ssh-add и перезапустите.")
|
raise RuntimeError("Приватный SSH-ключ зашифрован. Разблокируйте через ssh-add и перезапустите.")
|
||||||
raise RuntimeError(f"Ключ не подходит: {why}")
|
raise RuntimeError(f"Приватный SSH-ключ не подходит: {why}")
|
||||||
|
|
||||||
if choice == "2":
|
if choice == "2":
|
||||||
# Пароль target
|
# Пароль нового сервера
|
||||||
info("")
|
info("")
|
||||||
info("Выберите способ:")
|
info("Выберите способ:")
|
||||||
print(" 1) Ввести пароль от target — scp через sshpass")
|
print(" 1) Ввести пароль от нового сервера — scp через sshpass")
|
||||||
print(" 2) Сгенерировать временный ключ + ssh-copy-id (target спросит пароль)")
|
print(" 2) Сгенерировать временный SSH-ключ + ssh-copy-id (новый сервер спросит пароль)")
|
||||||
sub = prompt("Ваш выбор", default="1").strip()
|
sub = prompt("Ваш выбор", default="1").strip()
|
||||||
|
|
||||||
if sub == "2":
|
if sub == "2":
|
||||||
temp = generate_temp_keypair(host.replace(".", "_"))
|
temp = generate_temp_keypair(host.replace(".", "_"))
|
||||||
if not temp:
|
if not temp:
|
||||||
raise RuntimeError("Не удалось сгенерировать ключ")
|
raise RuntimeError("Не удалось сгенерировать SSH-ключ")
|
||||||
if ssh_copy_id(host, user, port, temp["public"]):
|
if ssh_copy_id(host, user, port, temp["public"]):
|
||||||
ok, _ = check_ssh_keyless(host, user, port, temp["private"])
|
ok, _ = check_ssh_keyless(host, user, port, temp["private"])
|
||||||
if ok:
|
if ok:
|
||||||
success("Временный ключ добавлен на target!")
|
success("Временный SSH-ключ добавлен на новый сервер!")
|
||||||
return temp["private"]
|
return temp["private"]
|
||||||
# ssh-copy-id не сработал
|
# ssh-copy-id не сработал
|
||||||
warn("ssh-copy-id не удался. Варианты:")
|
warn("ssh-copy-id не удался. Варианты:")
|
||||||
print(f" 1) Вручную на target: mkdir -p ~/.ssh; echo '$(cat {temp['public']})' >> ~/.ssh/authorized_keys")
|
print(f" 1) Вручную на новом сервере: mkdir -p ~/.ssh; echo '$(cat {temp['public']})' >> ~/.ssh/authorized_keys")
|
||||||
print(f" 2) Затем: docker-migrate --resume")
|
print(f" 2) Затем: docker-migrate --resume")
|
||||||
raise RuntimeError("ssh-copy-id не удался.")
|
raise RuntimeError("ssh-copy-id не удался.")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user