В настоящее время я работаю над панелью VPS, которая использует модель «ведущий-ведомый». Один главный сервер запускает панель, написанную на PHP, и управляет несколькими подчиненными серверами через SSH. Доступ к подчиненным серверам осуществляется через ограниченную учетную запись, которая может подчиняться конкретным командам, связанным с администрированием сервера, и все взаимодействия регистрируются в каталоге, к которому у самой учетной записи нет доступа.
В настоящее время я использую PHP-SSH2, но этот подход имеет несколько проблем:
- Коды выхода не надежно возвращаются, поэтому все команды должны выполняться в сценарии оболочки, который упаковывает stdout, stderr и код выхода в объект JSON и возвращает его через stdout. Этот скрипт должен существовать на каждом подчиненном сервере.
- Библиотека PHP-SSH2 не знает понятия «пользовательского таймаута соединения», что означает, что я должен исследовать сервер с помощью fsockopen, прежде чем пытаться использовать PHP-SSH2 для подключения – если я этого не делаю, недоступный сервер может задерживайте pageload в течение минуты или более. Это еще хуже из-за следующего выпуска.
- Стойкие соединения невозможны. Это вызывает совершенно смешное время pageload в панели, особенно в сочетании с предыдущей проблемой с таймаутами.
Сейчас я пытаюсь решить последнюю проблему в первую очередь.
Есть несколько возможных решений, с которыми я столкнулся, но у всех из них есть проблема:
- Использование PHPSecLib, чистой реализации PHP SSH и замена всех вызовов fsockopen вызовами pfsockopen. Это сделает соединения постоянными, но это хаккер, чем я хотел бы, и последствия безопасности постоянных сокетов в PHP неясно.
- Настройка постоянного SSH-туннеля с главного сервера на каждый подчиненный сервер и запуск простого демона (связанного с localhost) на каждом подчиненном сервере, который запускает все, что ему говорят. Это проблема по двум причинам. Во-первых, он вводит необходимость в демон на ведомых серверах, чего я бы скорее избежал. Вторая проблема заключается в том, что если кто-то должен был компрометировать ограниченную учетную запись на подчиненном сервере, они все равно могли бы запускать определенные системные команды, просто подключившись к «демонам команд», даже если бы у них не было доступа к этим командам из их собственной оболочки. Это проблема.
- Запуск демона на главном сервере, который управляет постоянными подключениями SSH к подчиненным серверам от имени панели. Это связано с написанием SSH-клиента в Python (это единственный подходящий язык, с которым я знаком), и, вероятно, придет к использованию paramiko. Поскольку документация paramiko плохой, это не очень привлекательный вариант и может даже вызвать проблемы с безопасностью, потому что мне не совсем ясно, как все должно быть использовано в paramiko.
Ниже перечислены следующие варианты:
- Переход на другой язык для самой панели. Я пишу панель на PHP, потому что это тот язык, с которым я больше всего знаком, и я знаю об особенностях и потенциальных проблемах, с которыми я мог бы столкнуться. Написание важного публичного проекта, подобного этому на языке, с которым я не знаком, будет плохой идеей.
- Использование Twisted для третьего «возможного решения». Twisted – очень большая и запутанная зависимость, и документация кажется еще хуже, чем у paramiko.
- Запуск HTTP-сервера или другого, но не SSH-открытого демон-сервера на подчиненных серверах.
На практике, я вижу время pageload иногда за минуту, когда к нескольким серверам нужно связаться с pageload. Это явно неприемлемо для панели VPS.
Моя цель состоит в том, чтобы иметь какую-то реализацию, которая позволяет избежать накладных расходов на подключение, которые появляются при использовании PHP-SSH2. Какой был бы лучший способ сделать это безопасным образом, введя минимальное количество зависимостей на подчиненных серверах?
Вы можете использовать autossh и создавать обратные (portforward) туннели с autossh. Затем пусть ваше приложение php поговорит с этими обратными портами ssh. Если сбой подключения ssh, autossh будет продолжать пытаться воссоздать соединение. Приложение php не сможет подключиться к обратному туннелю, а не к таймауту.
Как насчет опции 3, но и написать демона в PHP? Это маршрут, который я пытаюсь использовать в своем собственном проекте.
Вы можете использовать файл FIFO вместо сокетов для связи с ним.