Закройте ssh-туннель без рута и не уничтожая все остальные ssh-соединения

928
martin.macko.47

У меня есть SSH-туннель, прослушивающий локальный порт, скажем, 8888, открытый с удаленной машины с ssh -R 8888:localhost:80 myuser@myhost. Мне нужно написать скрипт для "myuser" на "myhost", который закроет этот туннель.

Сценарий будет выполнен "myuser" на "myhost". Это не будет выполнено рутом, и это не будет в состоянии sudo.

Один из подходов состоит в том, чтобы найти PID процесса, прослушивающего 8888 с помощью lsof или netstat, а затем завершить процесс. Тем не менее, как lsof, так и netstat отказываются давать мне PID без использования sudo. С netstat я просто получаю -вместо "PID / Program name" без sudo.

Есть ли способ узнать PID процесса, прослушивающего данный порт без привилегий root? Процесс прослушивания на этом порту выполняется под тем же пользователем, что и мы. Или есть какой-то другой способ закрыть ssh-туннель снаружи?

Обратите внимание, что использование escape-последовательности ssh для сброса соединения не является решением, поскольку мы не находимся в туннеле. Наш скрипт должен запускаться отдельно от туннеля на одном хосте одним и тем же пользователем.

Также обратите внимание, что просто запуск killall sshdне является решением, так как он уничтожит все другие ssh-соединения, не только это.

Этот вопрос не является дубликатом многих похожих вопросов, потому что все их принятые ответы, которые я нашел, требуют привилегий root или просто убивают все ssh-соединения.

На хосте "myhost" работает Ubuntu 12.04.5.


Изменить: резюме обсуждения по запросу @Jakuje:

  • @Patrick предложил подход, который можно использовать setuidили изменить, /etc/sudoersчтобы позволить пользователю запускать скрипт с привилегиями root без полного sudo. Хотя setuidнам нужно было бы написать двоичный файл вместо сценария. Однако разрешение пользователю запускать сценарий с привилегиями root может представлять потенциальную угрозу безопасности. И все же это решение не распространяется на случай, когда пользователь вообще не имеет корневого доступа, поэтому он не может изменить /etc/sudoers. Если @Patrick напишет это в качестве ответа, я определенно проголосую, но пока не буду отмечать, что он принят.

  • @EricRenouf обнаружил, что sshd делает сокет сокетом для пользователя, поэтому пользователь не может /proc/*/fdнайти процесс, имеющий сокет. Наверное, поэтому ни lsof, ни netstat не показывают этого.

Больше идей:

Как обнаружил @EricRenouf, вероятно, нет способа получить PID sshd по номеру открытого порта сокета или по индексу. Но мне любопытно, если нет другого трюка, как найти этот sshd PID или как сказать ему, чтобы закрыть соединение. Может быть, как-то напрямую с sshd.

Например, если бы я включил режим отладки sshd, sshd -dон бы регистрировал, какой процесс sshd открыл туннель, к какому порту, в /var/log/auth.logкотором пользователь может прочитать. Таким образом, скрипт может проанализировать журнал и найти там PID. Но запуск режима отладки sshd не очень хорошая идея. На самом деле существует простой патч для sshd для регистрации открытых туннелей даже без режима отладки. Но я не уверен, что запуск пропатченного sshd также будет хорошей идеей.

Есть ли другой трюк?

5
Хорошая проблема, я с нетерпением жду данных решений, так как иногда я страдаю от одной и той же проблемы. djsmiley2k 7 лет назад 0
Вы можете добавить команды lsof / netstat и kill в скрипт, а затем установить setuid на скрипт, чтобы он мог перейти в root при выполнении. Вам нужно будет добавить соответствующие команды в скрипт, убедиться, что скрипт принадлежит root, а затем использовать `chmod u + s / path / to / script`, чтобы сделать его SUID root. Patrick 7 лет назад 0
@ Патрик Не игнорируется ли SUID в скриптах? martin.macko.47 7 лет назад 1
Ты прав. Я не знал об этом. Также смотрите эту ветку: https://superuser.com/questions/440363/can-i-make-a-script-always-execute-as-root Я думаю, что изменение файла sudoers может быть лучшим вариантом. Patrick 7 лет назад 2
@ Патрик Да, это может работать как обходной путь. Но я чувствую, что открываю им дыру в безопасности. Я бы предпочел вообще не использовать привилегии root. Поскольку туннель запускается пользователем, пользователь может его убить. Поэтому я не вижу причины, по которой пользователь не может найти, какой процесс убить. Я просто не могу найти способ найти это. martin.macko.47 7 лет назад 0
Он запрашивается пользователем, но на самом деле он создается `sshd`, который почти наверняка работает как` root`. Я думаю, что это подтверждается поиском в `/ proc / net / tcp` и поиском записи для` 22B8` (hex для 8888) и проверкой (по крайней мере для меня), что столбец `uid` имеет` 0` Eric Renouf 7 лет назад 1
@EricRenouf для меня `uid` это` 1000` uid пользователя: `cat / proc / net / tcp | grep 22B8`: `10: 00000000: 22B8 00000000: 0000 0A 00000000: 00000000 00: 00000000 00000000 1000 0 758580 1 0000000000000000 100 0 0 10 0`. Порядок столбцов: `sl local_address rem_address st tx_queue rx_queue tr tm-> когда задан тайм-аут повторного запуска uid` martin.macko.47 7 лет назад 0
UID 0 в этом выводе, не так ли? Eric Renouf 7 лет назад 0
@EricRenouf Сложно отформатировать его здесь, поэтому смотрите [скриншот] (https://imgur.com/a/ytRcq). Если я не ошибаюсь, UID 1000. Не так ли? martin.macko.47 7 лет назад 0
Да, похоже, UID 0 для меня Eric Renouf 7 лет назад 0
Тогда я озадачен. Какой столбец является UID? Не является индексом 758580 и идентифицирует второй столбец слева от индекса, как написано [в этом ответе] (https://askubuntu.com/questions/243435/not-able-to-understand-and-map-output-of -proc-нетто-ТКФ)? martin.macko.47 7 лет назад 0
@ martin.macko.47, пожалуйста, отредактируйте вопрос с дополнительной информацией, собранной в комментариях. Очень трудно прочитать плохо отформатированные комментарии, чтобы понять, в чем проблема. Jakuje 7 лет назад 1
Я согласен с вами относительно UID сейчас, я смотрел на него неправильно, пытаясь отсчитать с конца, чтобы добраться до UID, но, похоже, это не сработает, мой плохой! Eric Renouf 7 лет назад 0
Плохая новость при дальнейшем расследовании, хотя sshd действительно сократит сокет для вашего пользователя, пытаясь найти PID, у которого он есть, все еще принадлежит root, что заставляет меня думать, что для этого вам понадобится sudo. Используя inode из / proc / net / tcp, я просмотрел / proc / * / fd, чтобы найти процесс, в котором был этот сокет, и его sshd работает от имени пользователя root, так что вам может быть трудно найти его непривилегированным пользователь Eric Renouf 7 лет назад 0
Можете ли вы запустить специальную копию sshd, возможно, указав конкретный файл конфигурации (в котором может быть указан другой порт)? Затем `pkill -f` с этим именем файла конфигурации (или любой другой уникальной частью командной строки, например, номером порта TCP для прослушивания)? TOOGAM 7 лет назад 0

1 ответ на вопрос

2
Google Fail

Начните с настройки собственного ключа SSH для подключения к «myhost». Затем отредактируйте файл .ssh / authorized_keys на «myhost», чтобы родительский PID оболочки записывался в файл:

command="echo $PPID > tunnel.pid; bash" ssh-rsa AAA... 

Вы можете настроить некоторые другие параметры безопасности и / или вы можете написать собственный скрипт, чтобы просто написать PID и зависнуть. Принцип одинаков в любом случае.

Когда у вас есть PID процесса, который поддерживает сессию SSH, это просто вопрос сценариев ...

kill `cat tunnel.pid` 

... завершить процесс, закрыв тем самым сессию SSH.

Обратите внимание, что вы можете использовать -iфлаг sshкоманды, чтобы указать закрытый ключ для подключения.

Редактирование: уничтожение процесса оболочки не приведет к закрытию туннеля, если есть активные соединения, поэтому мы уничтожаем родительский процесс SSH.

Редактирование: Хотя мое первое желание при использовании любого автоматического SSH-соединения - использовать SSH-ключи, тот же процесс можно эмулировать с помощью:

ssh -R 8888:localhost:80 myuser@myhost 'echo $PPID > tunnel.pid; for ((;;)); do sleep 1; done' 

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

Благодарю. Почти то, что я хотел. Но это работает, только если в туннеле нет ** открытых соединений **. Потому что `echo $$` дает нам только PID bash, работающего внутри sshd. И sshd не завершится, пока не закроются все открытые соединения, даже если команда завершится. Если в туннеле есть какое-либо соединение, sshd будет ждать их. Возможно, есть какая-нибудь опция ssh для принудительного закрытия всех соединений после завершения команды? martin.macko.47 7 лет назад 0
В заключение. Помогло использование `echo $ PPID` вместо` echo $$ `. Здесь `$ PPID` - это PID родительского процесса bash, который является sshd. После того, как вы отредактируете свой ответ для использования `$ PPID`, я приму его. martin.macko.47 7 лет назад 0
В конце я не использовал файл tunnel.pid, поэтому я не буду случайно убивать какой-то другой процесс с тем же PID на случай, если у меня будет какой-то древний туннель tunnel.pid. Но я использовал файл `.killswich` и ждал, пока его коснутся:` ssh -R 8888: localhost: 80 myuser @ myhost 'inotifywait .killswich; убить $ PPID'`. Поэтому после `touch .killswich` sshd убьет себя. martin.macko.47 7 лет назад 0
@ martin.macko.47, я тестировал только без активных соединений. Спасибо что подметил это. Я отредактировал свой ответ. Google Fail 7 лет назад 0