Недавно я исследовал, какие существуют решения для реализации прокси-сервера web-ssh. Суть задачи — дать пользователям возможность подключаться к произвольному SSH-серверу через веб-интерфейс.
Обычно решения web-ssh предназначены для подключения к серверу, на котором они развернуты, но для моей цели я хотел, чтобы пользователь мог указать IP, порт, имя пользователя и пароль (или ключ) и подключиться к произвольному серверу.
.
Мне не удалось найти такое решение сразу.
На самом деле, конечно, есть Гуакамоле, но для моей задачи использование этого приложения было слишком затратным как с точки зрения ресурсов разработки, так и с точки зрения функций и их организации, поэтому я отказался от Гуакамоле.Однако для открытого пакета Shellinabox я нашел решение по адресу блог на немецком языке, который я решил довести до нужного мне уровня.
В результате получается красивый Docker-контейнер, который можно найти по адресу: GitHub скоро Докерхаб , который решает все необходимые проблемы.
Но статья не об этом, а о сопутствующем Python-коде, который мне пришлось написать.
Дело в том, что мне не понравилось то, что если пользователь открыл web ssh и куда-то зашёл, то сессия зависала на неопределённое время, что на мой взгляд недопустимо.
Это приводит к следующим негативным последствиям:
- ресурсы терминального прокси-сервера используются неэффективно, поскольку он вынужден обслуживать неиспользуемые сессии;
- Необслуживаемое окно с открытым ssh в браузере вызывает чувство тоски и уязвимости.
Довольно долгий поиск в Google показал мне, что цели можно достичь с помощью команды timeout и strace. Команда тайм-аута была для меня новой; его цель — гарантировать, что процесс будет прерван по достижении определенного тайм-аута, если он не завершился сам.
Я часто использую команду strace, но обычно использую ее, чтобы выяснить, почему какая-то служба или команда не работает должным образом.
В рамках поиска того, как отслеживать активность на каналах stdin и stdout процесса, я обнаружил, что strace также может обеспечить это:
В общем, эти команды были тем, что мне было нужно для осуществления моих планов.strace -e write=1,2 -e trace=write -p <PID>
Общая схема выглядит так:
- Когда открывается новое соединение, Shellinabox запускает скрипт Python;
- Сценарий Python запускает (разветвляет) процесс-демон для мониторинга активности процесса, который отслеживает свой собственный PID;
- Скрипт Python выполняет (exec) ssh (заменяя себя на ssh).
В остальном дальше по частям.
Кратко код можно представить следующим образом.
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: Если у кого-то есть желание улучшить код, добро пожаловать в репозиторий , с радостью приму пиар.
Теги: #linux #python #strace #ssh #shellinabox #python #разработка Linux
-
Хорошая Клиент-Серверная Архитектура.
19 Oct, 24 -
Wikia Search: Почти Rip
19 Oct, 24 -
Офлайн-Распространение Google Earth
19 Oct, 24 -
Tesla Motors Становится Прибыльной
19 Oct, 24