Про технологию "RemoteApp", когда услужливая форточка (Windows Server) транслирует на RDP-клиента отдельно взятое окошко с отдельно взятым приложением, наверное все слышали. Особенно она распространена среди 1С-пользователей, хотя в последнее время всё больше вытесняется HTML5-интерфейсом. Довелось тут городить на Linux-е нечто подобное.
Задача. Есть некая очень кривая Java-софтина, доступ к которой нужен для N разных сотрудников. Ситуацию отягощают нижеследующие вводные.
- Она обвешана всевозможными лицензионными ограничениями, поэтому запускается только на строго определенной виртуалке.
- Нельзя допустить, чтобы её запустили два разных пользователя одновременно, иначе она побьёт свою внутреннюю базу данных.
- Путь к этой самой внутренней базе данных где-то внутри hard-coded и не меняется.
- Как следствие, предыдущего пункта, приложение может запускаться только от имени какого-то определенного заранее заданного пользователя.
До меня это решалось так. "Полноценный" виртуальный Linux-десктоп с "иксами". Все желающие туда ходят VNC-ой, логинятся под тем самым "определенным" пользователем, делают что им нужно. Но не всегда удобно, потому что местами не работает clipboard, местами проблемы с раскладками, местами VNC-клиенты кривые и всё вот это. Поэтому я решил закопать стюардессу и пересадить всё это безобразие на
XPRA.
Штука довольно мощная и фичастая. Умеет много разных режимов работы. Но плотно завязана на "иксы" и D-Bus, поэтому как системный демон запускается крайне неохотно. Во всяком случае, у меня не получилось. Я довольно долго с ней промучался, но из-под systemd завести её так и не смог. То прав в пользовательские rundirs не хватало, то переменных окружения, то xauth / magic cookies, то с Dbus не получалось общаться, то ещё чего-нибудь. И тогда я вспомнил, что бывают systemd user units.
Отлично. Пишем unit типа такого.
[Unit]
Description=Xpra SuperApp
[Service]
Type=simple
ExecStart=/usr/bin/xpra start :10 --daemon=no --start='/usr/bin/chmod 660 /run/xpra/myhost-10' --start-child='/usr/bin/java -Xms128M -Xmx128M -jar /opt/SuperApp/foo-applet.jar' --exit-with-children
Restart=always
RestartSec=5
[Install]
WantedBy=default.target
Называем эту штуку, например, 'xpra.service' и кладём в '/home/john/.config/systemd/user', где 'john' - имя пользователя, от которого всё это будет запускаться.
Дальше есть несколько нюансов.
Первый. Этому самому "Джону" нужно разрешить lingering, то есть запуск каких-то процессов без его непосредственного интерактивного участия. Делается так, из-под рута:
loginctl enable-linger john
Второй. Из-под самого Джона нужно один раз произнести заклининания интерактивно, без повышения:
systemctl --user daemon-reload
systemctl --user enable xpra
systemctl --user start xpra
чтобы поставить сервис в автозагрузку и опционально сразу же стартовать его.
Третий. У xpra есть самые разные параметры запуска. Например, "--window-close=disconnect". Тогда при закрытии окна приложения на клиенте оно не будет завершено на сервере. Но при этом никто не запретит пользователю сделать что-нибудь типа "File → Exit" в меню. Тогда приложение таки закроется, но xpra останется "висеть". Следующий пользователь успешно подключится, но ничего не увидит. Потому что терминал формально есть, а работать внутри него нечему. Поэтому персонально я предпочитаю комбинацию параметров "--start-child=", "--exit-with-children", "Restart=always". Тогда xpra остановится вместе с приложением, systemd обнаружит данный факт и сам запустит всё взад.
Четвёртый. Пришлось городить вот такой костыль: "--start='/usr/bin/chmod 660 /run/xpra/myhost-10'", где "myhost" - имя машины (hostname), на которой всё запускается, "10" - номер X-дисплея (мы его придумываем сами), задается параметром ранее после двоеточия. После запуска xpra создает три Unix-сокета в разных папках с правами типа "john:xpra 600" или вариациями на тему. То есть по умолчанию подключиться к "чужой" X-сессии нельзя, только сам Джон может сделать это. Меня же это не устраивает, т.к. по условию задачи требуется выдать доступ некоей большой ораве пользователей. Поэтому всех заинтересованных приходится включать в группы "john" и "xpra", плюс накидывать прав на чтение-запись для группы "xpra" на один из трёх таких создаваемых сокетов. Тогда подключиться к созданному X-дисплею смогут все, кто входит в группу "xpra".
Клиенту остается только загрузить в SSH-агента свой закрытый ключ и сделать "attach" на сервер типа такого: "xpra attach ssh:superserver.com:10". Я у себя протестировал под Linux-ом и под виндой, всё работает как задумано. Под Mac-ом протестировать не могу, но xpra-клиент для него тоже существует. Если к серверу цепляются одновременно двое пользователей, то первого "вышибает". Если первый сделает "disconnect", то второй увидит приложение в том же состоянии, в котором его оставил первый. Если же первый именно что закроет приложение, то второй увидит его "с нуля" в "свежезапущенном" облике.
При перезагрузке сервера всё запускается автомагически посредством systemd, это тоже я проверил.
Всем поменьше кривых приложений и быстрых терминалов.