- PVSM.RU - https://www.pvsm.ru -
Недавно я занимался тем, что исследовал какие существуют решения для реализации web-ssh прокси-сервера. Суть задачи заключается в том, чтобы дать пользователям возможность соединяться с произвольным ssh-сервером посредством web-интерфейса. Обычно, решения web-ssh предназначены для соединения с сервером, на котором они развернуты, но в рамках моей задачи мне хотелось, чтобы пользователь мог указать IP, порт, имя и пароль пользователя (или ключ) и выполнить соединение с произвольным сервером. С ходу найти подобного решения мне не удалось.
Вообще-то, конечно, есть Guacamole, но для моей задачи использование этого приложения было слишком затратным как по ресурсам разработки, так и по функциям и их организации, поэтому от Guacamole я отказался.
Однако, для открытого пакета shellinabox я обнаружил решение на блоге [1] на немецком языке, которое я и решил довести до нужного мне уровня. В итоге, получился симпатичный контейнер Docker, который можно найти как на GitHub [2] так и на Dockerhub [3], который решает все необходимые задачи.
Но, статья не об этом, а о сопутствующем коде на Python, который мне пришлось написать. Дело в том, что мне не нравилось, что если пользователь открыл web ssh и куда-то ушел, то сессия будет висеть бесконечно, что на мой взгляд неприемлемо. Это ведет к следующим отрицательным последствиям:
В общем, я решил, что хочу добиться того, чтобы shellinabox разрывал соединение в том случае, если пользователь несколько минут не пишет ничего на консоль (stdin) и на stdout не поступают данные.
Достаточно продолжительный поиск в Google показал мне, что цели можно добиться с использованием команды timeout и strace. Команда timeout оказалась для меня новой, ее назначение — обеспечить прерывание процесса по достижению некоторого таймаута в том случае, если он сам не завершился.
Команду strace я использую часто, однако, обычно применяю ее для того, чтобы отслеживать причину, по которой какая-то служба или команда не работает как ожидается. В рамках моего поиска на тему того, как осуществить мониторинг активности на каналах stdin, stdout процесса я обнаружил, что strace так же может это обеспечить:
strace -e write=1,2 -e trace=write -p <PID>
В общем, данные команды были тем, что мне было необходимо для осуществления задуманного. Общая схема выглядит так:
Для тех, кто хочет сразу посмотреть как же устроен весь скрипт, отправляю сюда [4]. Для остальных далее по частям.
Кратко, код можно представить следующим образом.
monitor_daemon(inactivity_interval, identity_file)
...
...
os.execv("/usr/bin/ssh", ["/usr/bin/ssh"] + identity_args + ["-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-p", str(peer_port), "%s@%s" % (peer_login, peer_ip)])
Код простой и самоочевидный. Вся магия находится внутри функции monitor_daemon:
def monitor_daemon(inactivity_interval, identity_file):
orig_pid = os.getpid()
try:
pid = os.fork()
if pid > 0:
return
except OSError as e:
print("Fork #1 failed: %d (%s)" % (e.errno, e.strerror))
sys.exit(1)
os.chdir("/")
os.setsid()
os.umask(0)
try:
pid = os.fork()
if pid > 0:
sys.exit(0)
except OSError as e:
print("Fork #2 failed: %d (%s)" % (e.errno, e.strerror))
sys.exit(1)
if identity_file != "":
time.sleep(1)
os.unlink(identity_file)
try:
while True:
proc = subprocess.Popen('timeout %d strace -e write=1,2 -e trace=write -p %d' % (inactivity_interval, orig_pid), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
proc.poll()
counter = 0
for line in proc.stderr.readlines():
counter += 1
if(counter <= 3):
os.kill(orig_pid, signal.SIGKILL)
sys.exit(0)
except Exception as e:
pass
sys.exit(0)
где нас более всего интересует часть:
while True:
proc = subprocess.Popen('timeout %d strace -e write=1,2 -e trace=write -p %d' % (inactivity_interval, orig_pid), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
proc.poll()
counter = 0
for line in proc.stderr.readlines():
counter += 1
if(counter <= 3):
os.kill(orig_pid, signal.SIGKILL)
sys.exit(0)
в которой и происходит в цикле запуск мониторинга, который после заданного периода ожидания выполняет завершение процесса ssh, если требуется.
Вот собственно и все. Решение оказалось достаточно простым, но потребовало некоторое время на изучение доступных инструментов.
PS: Я не являюсь профессиональным python-разработчиком, код субоптимален.
PPS: Если у кого-то возникнет желание улучшить код, добро пожаловать в репозиторий [5], я с удовольствием приму PR-ы.
Автор: Иван Кудрявцев
Источник [6]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/ssh/259754
Ссылки в тексте:
[1] блоге: https://tools.bartlweb.net/webssh/
[2] GitHub: https://github.com/bwsw/webshell
[3] Dockerhub: https://hub.docker.com/r/bwsw/webshell/
[4] сюда: https://github.com/bwsw/webshell/blob/master/shellinabox.py
[5] репозиторий: https://github.com/bwsw/webshell/blob/master/Dockerfile
[6] Источник: https://habrahabr.ru/post/332544/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best
Нажмите здесь для печати.