Как я могу переподключиться к сеансу SSH после разрыва соединения?

Вопрос или проблема

Я запускал apt-get upgrade на сервере, когда маршрутизатор решил, что прошло слишком много времени с тех пор, как он меня в последний раз разозлил: он сбросил все соединения. С моралью истории следует использовать screen чаще, когда вы на некачественном маршрутизаторе.

В любом случае, я снова зашел в систему и обнаружил в htop, что процесс все еще завис, все еще ожидая моего ответа Y/n на обновление (пока еще не нажал, к счастью). Есть ли способ повторно подключиться к сессии, которая была прервана? Я в итоге просто убил его, так как он не был в процессе управления пакетами, но было бы здорово знать это на будущее.

Хотя вы не можете подключиться к прерванной SSH-сессии, вы можете переназначить процесс, выполняющийся внутри SSH – функционально это эквивалентно тому, что вы хотите.

Инструкции

В вашем случае вы возьмете на себя процесс apt-get, с целью управлять им из новой SSH-сессии, сессии screen или подобного. Мой любимый вариант для этого – команда reptyr:

$ sudo apt-get install reptyr
$ ps ax | grep apt-get
10626 pts/8   R+     0:32 apt-get upgrade

Затем, с pid, который вы нашли для вашего процесса:

$ sudo reptyr -T 10626

Или если это не сработает, попробуйте:

$ reptyr 10626

После этого все ваши ввода с клавиатуры будут направлены на программу, которую вы взяли под контроль. К сожалению, вы не увидите старый вывод SSH-сессии, такой как вывод apt-get, запрашивающий ваше подтверждение.

Объяснения

Существуют и множество других инструментов, которые в основном работают так же, как reptyr (т.е. через прикрепление отладки ptrace). Смотрите следующие вопросы и ответы, где они обсуждаются:

В приведенных выше инструкциях команда reptyr 10626 использует прикрепление отладки ptrace, в то время как команда sudo reptyr -T 10626 использует присвоение TTY и является предпочтительной (подробности).

Наконец, причина, по которой вы не можете взять на себя SSH-сессию таким образом, заключается в том, что процесс sshd не контролируется терминалом хоста, вместо этого он предоставляет подчиненную часть терминала – устройство pts – в то время как управляющая часть resides на клиентском компьютере, здесь с прерванной SSH-сессией между ними. Когда вы принудительно берете под контроль такой процесс sshd с помощью reptyr -s <pid>, ввод с клавиатуры идет в этот процесс, а не в его активный дочерний процесс. Поэтому “Ctrl+Z” просто завершит работу этого sshd.

Ответ на ваш правильный вопрос: вы не можете. Я думаю, главная проблема в том, что процедуры аутентификации будут не синхронизированы. Это просто не работает так.

Как вы сами заметили, решение – использовать screen, когда это возможно (кстати, tmux является альтернативой screen).

Для запуска долгих процессов я использую screen, или byobu, если хотите более дружелюбный интерфейс.

Для screen вы можете использовать:

screen [программа] [аргументы]

Это запустит [программу] и ее [аргументы] внутри сессии screen. Как только программа завершится, сессия автоматически закроется. Если вы хотите сохранить сессию после выполнения программы, просто запустите screen без аргументов, и новый приглашение появится внутри сессии. CTRL+A+D отсоединяет терминал от текущей сессии.

Чтобы повторно присоединиться к предыдущей сессии:

screen -r

Если открыта только одна сессия, она сразу же повторно присоединится. Если идет несколько сессий, она спросит, к какой из них вы хотите присоединиться. Если вы знаете имя сессии, вы можете просто добавить его в качестве аргумента к этой командной строке.

Byobu – это приятное улучшение. Он основан на screen, но предоставляет панель внизу, которая показывает все текущие сессии в виде вкладок и предлагает более легкие сочетания клавиш для передвижения между ними. Вы можете:

  • F2 начать новую сессию
  • F3 перейти к следующей вкладке сессии слева
  • F4 перейти к следующей вкладке сессии справа
  • F8 дать дружелюбное имя текущей вкладке сессии
  • F9 открыть меню опций
  • CTRL+A+D отсоединить все сессии от терминала.

СОВЕТ: избегайте оставлять сессию открытой с пользователем root. Если кто-то получит доступ к вашему терминалу (локально или удаленно), он может легко повторно подключиться к текущей сессии и использовать вашу систему как root. Если это необходимо, лучше начинать сессию с помощью обычного пользователя и использовать sudo для отдельных команд по мере необходимости.

Я делал do-dist-upgrade через ssh с ноутбука, который ушел в режим ожидания, в результате чего появилось сообщение Broken pipe. Когда я вернулся к машине, я увидел, что процессы, связанные с обновлением, все еще работают, среди которых был whiptail, запрашивающий ввод (какой дисплейный менеджер выбрать), и, что важно, SCREEN, принадлежащий root. Мне удалось выполнить sudo su - и затем screen -r, чтобы подключиться к сессии, и, во внимание, у меня был диалоговое окно whiptail передо мной, готовое принять ввод. Я смог бесшовно продолжить обновление.

Примечание: это было обновление с Ubuntu 14.04 на 16.04.

Я хотел использовать reptyr, но мне не было известно, какой PID мне нужно переназначить. Что мне нужно было знать, так это то, что команда w может показать PID ваших работающих sshd процессов, а затем pstree сможет дать вам PID оболочки, созданной этим процессом sshd. Вам нужно использовать reptyr -T pid-of-shell, а не PID sshd. Я написал небольшой инструмент, чтобы вывести информацию о каждом из ваших работающих процессов sshd, чтобы помочь вам определить, какой из них связан с прерванным соединением:

https://gist.github.com/simonLeary42/6a89a800c832c5d37da9e7cd03cdd8be

он требует jc, jq, w, ps и pstree.

#!/bin/bash
set -euo pipefail

ssh_client_ip=$(echo $SSH_CLIENT | awk '{print $1}')
w_json="$(w --pids $USER | jc --w)"
if [ -z "$w_json" ]; then
    echo "ошибка: 'w --pids | jc --w' ничего не вывело!"
    exit 1
fi

echo "$w_json"
# Консолидируем логику фильтрации в одном запросе jq
relevant_entries="$(echo "$w_json" | jq -c --arg ip "$ssh_client_ip" '
    .[] |
    select(.user != null and .tty != null and .what != null) |
    select(.tty == $ip) |
    select(.what | startswith("sshd"))
')"

if [ -z "$relevant_entries" ]; then
    echo "Используя команду 'w --pids', я не нашел процессов sshd для вашего пользователя с IP-адреса '$ssh_client_ip'!"
    exit 1
fi

# Обрабатываем соответствующие записи
while IFS= read -r entry; do
    pid=$(echo "$entry" | jq -r '.pcpu | split("/") | .[0]')  # Извлекаем левый PID
    proc_cmd=$(ps -o cmd= -p "$pid" | xargs)
    proc_age_seconds=$(ps -o etimes= -p "$pid" | xargs)
    proc_age_human_readable=$(date -u -d "@$proc_age_seconds" +"%H:%M:%S")
    proc_pct_cpu=$(ps -o pcpu= -p "$pid" | xargs)
    pstree_output=$(pstree -p "$pid")

    printf '%b\n' "\e[32m\"$proc_cmd\"\e[0m запущен \e[34m$proc_age_human_readable\e[0m назад, используя \e[31m$proc_pct_cpu%\e[0m CPU"
    echo "$pstree_output"
    echo ""
done <<< "$relevant_entries"

echo "Надеюсь, вы сможете найти PID вашего потерянного SSH-соединения выше."
echo "Вам нужен PID оболочки, а не любого процесса 'sshd'."
echo 'Затем вы можете выполнить `sudo reptyr -T "$PID"` для восстановления потерянной оболочки.'
sshd(1247535)---sshd(1247540)---bash(1247541)---emacs(1315001)

"sshd: simonleary_umass_edu [priv]" запущен 02:28:57 назад, используя 0.0% CPU
sshd(1286868)---sshd(1286895)---bash(1286897)---sh(1286957)-+-code-f1a4fb1014(1286981)-+-{code-f1a4fb1014}(1286989)
                                                            |                          |-{code-f1a4fb1014}(1286990)
                                                            |                          |-{code-f1a4fb1014}(1286991)
                                                            |                          |-{code-f1a4fb1014}(1286992)
                                                            |                          |-{code-f1a4fb1014}(1286994)
                                                            |                          |-{code-f1a4fb1014}(1286995)
                                                            |                          |-{code-f1a4fb1014}(1286996)
                                                            |                          `-{code-f1a4fb1014}(1286997)
                                                            `-sleep(1485342)

"sshd: simonleary_umass_edu [priv]" запущен 01:31:39 назад, используя 0.0% CPU
sshd(1363409)---sshd(1363414)---bash(1363415)---python(1371715)

"sshd: simonleary_umass_edu [priv]" запущен 01:24:24 назад, используя 0.0% CPU
sshd(1372865)---sshd(1372870)---bash(1372871)---bash(1487749)---pstree(1487837)

Надеюсь, вы сможете найти PID вашего потерянного SSH-соединения выше.
Вам нужен PID оболочки, а не любого процесса 'sshd'.
Затем вы можете выполнить `sudo reptyr -T "$PID"` для восстановления потерянной оболочки.

Ответ или решение

Переподключение к SSH-сессии после разрыва соединения может быть затруднительным, особенно если выполняются длительные процессы, такие как обновление пакетов. Учитывая данный сценарий, важно понимать, как можно восстановить контроль над запущенными процессами, даже если первоначальная SSH-сессия была прервана. В этом ответе рассмотрим подходящие методы, включая использование утилит reptyr и screen, которые помогают избежать подобных проблем в будущем.

Основные шаги для восстановления контроля над процессом

  1. Установка утилиты reptyr:
    reptyr позволяет «переносить» управление уже запущенным процессом на новый терминал. Если в процессе изменения соединения через SSH произошло прерывание, вы можете использовать reptyr, чтобы взять под контроль зависший процесс. Установите reptyr, если он не установлен:

    sudo apt-get install reptyr
  2. Поиск процесса:
    Установите, какой процесс вы хотите вернуть под контроль. Используйте команду ps для поиска PID (идентификатора процесса):

    ps ax | grep apt-get

    Это вернет вам список процессов, идентифицируя ваш apt-get upgrade с его PID, например, 10626.

  3. Перенос управления:
    После нахождения PID, используйте reptyr для того, чтобы забрать управление процессом:

    sudo reptyr -T 10626

    Либо, если это не сработает:

    reptyr 10626

    Теперь все вводимые вами команды будут действовать на запущенный процесс apt-get.

Важно отметить, что вы не увидите вывод, который происходил до разрыва сессии, но сможете продолжить работу с ним.

Использование screen для предотвращения проблем в будущем

Чтобы предотвратить подобные ситуации с разрывами соединения в будущем, используется screen. Это утилита для управления несколькими терминальными сессиями.

Основные команды screen:

  • Запуск новой сессии:

    screen
  • Запуск программы внутри screen:

    screen [program] [args]
  • Отключение сессии (detaching):

    Для отключения текущей сессии удерживайте CTRL и нажмите A, затем D.

  • Присоединение к существующей сессии:

    screen -r

    Если у вас несколько сессий, вам предложат выбрать ту, к которой вы хотите подключиться.

  • Создание новых именованных сессий:

    Вы можете быстро создавать именованные сессии с помощью:

    screen -S session_name

Использование byobu для удобства

Byobu – это расширенная оболочка для screen, которая предоставляет удобный интерфейс и множество дополнительных функций, таких как вкладки и различные панели состояния.

Основные функции byobu:

  • Легкая навигация между сессиями.
  • Удобное отображение информации о текущей сессии.
  • Можно безопасно работать с системными процессами, используя краткие команды.

Заключение

При разрыве SSH-сессии возможность восстановления управления зависшими процессами представляет собой важный навык для системных администраторов и технических специалистов. Используя reptyr и screen, вы сможете избежать потери прогресса в долгих задачах и обеспечить себе комфортное рабочее окружение, даже при непредвиденных обстоятельствах, таких как потеря соединения. Рекомендуется использовать screen или byobu для работы с длительными процессами, чтобы максимально снизить риск повреждения выполнения ваших задач в будущем.

Оцените материал
Добавить комментарий

Капча загружается...