В связи с полным и безвозвратным переходом на Linux, как на работе, так и дома, столкнулся с проблемой запуска клиент банка. На момент написания статьи я пользуюсь услугами OTP Bank. Типичный банк для Украины, ничего выдающегося, но вот в техническом и технологическом плане, тут все очень печально.
И пока я дома использовал ОС Windows, все было более менее нормально. У них есть и десктопная версия клиент банка, но работает она только под Windows, а судя по интерфейсу написана она на Delphi. Мобильная версия не позволяет производить никаких операций, а только просматривать данные, так что она тоже не подходит. И остается только веб клиент, но и тут меня ждало разочарование. У данного банка есть два вида веб клиентов - для физических лиц и для юридических. Если с первым все нормально, и он работает на любой ОС, то вот со вторым беда полная.
И да, в 2018 году, их клиент банк для юридических лиц работает на технологии java applet!!!. А как многие уже знают, на данный момент практически не осталось веб проводников, которые поддерживали java applet. На странице самого банка красуется такое предупреждение
обращаем ваше внимание, что с 7-го марта 2017, после планового обновления браузера Firefox до версии 52, закончится поддержка всех NPAPI плагинов.
В связи с этим, единственным браузером в котором будет корректная работа стандартной web-версии системы клиент-банк OTP-Online на платформе OS Windows остается Internet Explorer версии 9 и выше. В качестве альтернативного варианта предлагаем воспользоваться портейбл версиею браузера Firefox, которая после установки не будет обновляться.
Собственно вариантов остается не так уж и много:
Первый пункт отпадает сразу. Второй в процессе реализации, но требует много времени, так как с ФОП все немного сложнее, чем с обычным физ лицом. Третий не понравился по причине необходимости установки ПО для виртуализации и установке самой Windows, а по хорошему у вас должна быть куплена и лицензия под это дело. Wine в принципе как вариант, но там много шаманства, возможно как нибудь попробую. Так что, по сути, остается последний пункт, а так как последние 3 года я работаю devops инженером, и по долгу службы почти каждый день приходится сталкиваться с docker, то именно на нем мы и попробуем решить данную задачу.
Итак, при попытке запустить веб клиент на современных версиях веб проводников, а на домашней машине я использую ubuntu 18.04 и Firefox 61.0.1, вы получите примерно такое сообщение с ошибкой
Запустить firefox в docker не проблема, проблема именно в необходимости поддержки NPAPI. Ибо тот же FF заявляет
Немного поискав, нашел на странице самого FF информацию о специальной сборке под названием ESR (Extended Support Release)
Так как нам придется установить еще и плагин для работы с Java, то тут возникает вопрос - а какую сборку Java использовать. На данный момент есть две сборки - православная от Oracle и OpenJDK.
С первой проблема заключается в том, что вы не сможете нормально скачать архив с произвольной версией Java c сайта Oracle, они требуют регистрации. Если очень надо автоматизировать данный шаг, то можно попробовать варианты со StackOverflow. А с OpenJDK проблема в том, что в ее составе нет плагина (libnpjp2.so). Вы должны использовать сторонний плагин, например icedtea-plugin (IcedTeaPlugin.so). Какую версию Java использовать в итоге - дело вкуса, в данной статье я буду использовать версию от Oracle.
Создаем папку для нашего образа и предварительно скачиваем Java и FireFox ESR. В итоге у нас должно получится примерно такое
$ mkdir -p ~/sandbox/docker/otp-ifobs-ffesr52/src $ cd ~/sandbox/docker/otp-ifobs-ffesr52/ $ tree . . ├── Dockerfile └── src ├── firefox-52.9.0esr.tar.bz2 └── jre-8u181-linux-x64.tar.gz 1 directory, 3 files
FROM ubuntu:16.04 ENV TERM=xterm \ JAVA_HOME=/otp/jre1.8.0_181 \ PATH=$PATH:/otp/jre1.8.0_181/bin # Устанавливаем зависимости, необходимые для запуска firefox RUN DEBIAN_FRONTEND=noninteractive apt-get update \ && DEBIAN_FRONTEND=noninteractive apt-get -y install --no-install-recommends \ bzip2 apt-transport-https libx11-6 libfreetype6 \ libfontconfig1 hicolor-icon-theme libxrender1 \ libxext6 libxdamage1 libxcomposite1 libasound2 libxt6 \ libxtst6 libdbus-glib-1-2 libxtst6 fontconfig \ xfonts-cyrillic xfonts-100dpi ttf-ubuntu-font-family \ ca-certificates dbus-x11 libgtk-3-0 desktop-file-utils \ libgtk2.0-0 libcanberra-gtk3-module libcanberra-gtk-module \ && fc-cache -fv \ && apt-get clean \ && apt-get autoremove -y \ && rm -rf /var/lib/apt/lists/* # Копируем архивы с java и firefox esr внутрь контейнера COPY src/* /otp/ # Создаем отдельного пользователя firefox, от которого будет запускаться наш веб проводник, # так как от root он работать не будет. А так же подключаем и настраиваем java plugin RUN useradd firefox -s /bin/bash -m -d /otp/firefox/ \ && cd /otp/ && tar jxf firefox-52.9.0esr.tar.bz2 \ && tar zxf jre-8u181-linux-x64.tar.gz \ && mkdir -p /otp/firefox/.mozilla/plugins \ && cd /otp/firefox/.mozilla/plugins \ && ln -s /otp/jre1.8.0_181/lib/amd64/libnpjp2.so . \ && chown -R firefox:firefox /otp/firefox/ \ && echo '577fed1c21a64f2cb186c800ae467e9d' > /etc/machine-id WORKDIR /otp/firefox/ USER firefox ENTRYPOINT ["/otp/firefox/firefox"]
После этого запускаем сборку контейнера
$ docker build -t otp-ifobs:ff-esr-52 . Sending build context to Docker daemon 139MB Step 1/8 : FROM ubuntu:16.04 ---> 7aa3602ab41e Step 2/8 : ENV TERM=xterm JAVA_HOME=/otp/jre1.8.0_181 PATH=$PATH:/otp/jre1.8.0_181/bin ---> Running in bec83477b887 Removing intermediate container bec83477b887 ---> dfd60f7b3d5c ... ... ... Step 7/8 : USER firefox ---> Running in a4a162ec103b Removing intermediate container a4a162ec103b ---> 059011e8d3e1 Step 8/8 : ENTRYPOINT ["/otp/firefox/firefox"] ---> Running in 5a6cc84b6ae3 Removing intermediate container 5a6cc84b6ae3 ---> 11c92fce4b37 Successfully built 11c92fce4b37 Successfully tagged otp-ifobs:ff-esr-52
Ну а теперь переходим к самому интересному. Запускаем наш образ. Нам осталось решить только один вопрос - а как нам отображать собственно окно самого firefox? А для этого нам необходимо пробросить в наш докер контейнер папку с сокетом X server, на современных Linux дистрибутивах это как правило каталог /tmp/.X11-unix. Если запустить lsof, то мы увидим примерно такой вывод
$ lsof /tmp/.X11-unix/X0 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME Xorg 2011 alex 7u unix 0x0000000000000000 0t0 30532 /tmp/.X11-unix/X0 type=STREAM
Но этого недостаточно, так же нам надо передать номер дисплея, куда осуществлять вывод, который хранится в переменной $DISPLAY. Вы можете посмотреть значение этой переменной
$ echo $DISPLAY :0
Итак, у нас все готово для запуска, так что пробуем взлететь
$ docker run -it --rm -e DISPLAY=${DISPLAY} -v /tmp/.X11-unix:/tmp/.X11-unix otp-ifobs:ff-esr-52
После этой команды на рабочем столе должно появится окно с Firefox. Для проверки смотрим Help → About Firefox
Проверяем, что java plugin был подключен и загружен, для этого в строке адреса открываем about:plugin
Если видим в списке плагинов Java, значит все у нас собранно и настроенно правильно. Для проверки работы самого плагина есть тестовая страница - http://www.java.com/en/download/installed.jsp?detect=jre
Нажимаем на Activate Java и соглашаемся с запуском Java приложения
В итоге мы должны увидеть такое сообщение
На этом проверку можно считать завершенной и переходим непосредственно к клиент банку - https://ibank.otpbank.com.ua/ifobsClient/. Вводим логин и пароль, после этого у нас должно появится окно с запросом подтверждения запуска Java аплета
После того как мы даем разрешение, мы наконец то попадаем в клиент банк
Для удобства запуска, я добавил адрес клиент банка параметром в ENTRYPOINT
ENTRYPOINT ["/opt/firefox/firefox", "https://ibank.otpbank.com.ua/ifobsClient/LoginShow.action?localeName=ru"]
Так же если хочется, чтобы между запусками сохранялись все настройки самого Firefox, то достаточно при запуске контейнера пробрасывать папку /otp/firefox/.mozilla/firefox
$ docker run -it --rm -e DISPLAY=${DISPLAY} -v /tmp/.X11-unix:/tmp/.X11-unix \ -v ~/sandbox/docker/otp-ifobs-ffesr52/profile:/otp/firefox/.mozilla/firefox otp-ifobs:ff-esr-52
Решил таки добавить информацию о запуске данной системы на базе OpenJDK. Как оказалось там не все так просто как c Oracle Java. Создаем следующий Dockerfile, но на этот раз наш образ будет базироваться на базе alpine. Самое главное отличие alpine от других дистрибутивов, в том числе и Ubuntu, в том, что вместо традиционной библиотеки glibc используется альтернативная реализация - musl. С основными отличиями от других реализаций можно ознакомиться по данной ссылке
FROM alpine:3.8 ENV TERM=xterm \ JAVA_HOME=/usr/lib/jvm/java-1.8-openjdk/jre \ PATH=$PATH:/usr/lib/jvm/java-1.8-openjdk/jre/bin:/usr/lib/jvm/java-1.8-openjdk/bin RUN apk update \ && apk add bash \ firefox-esr \ icedtea-web-mozilla \ icedtea-web \ libcanberra-gtk3 \ adwaita-gtk2-theme \ adwaita-icon-theme \ ttf-ubuntu-font-family \ ttf-liberation \ ttf-dejavu \ shadow \ && mkdir /otp RUN useradd firefox -s /bin/bash -m -d /otp/firefox/ \ && chown -R firefox:firefox /otp/firefox/ \ && echo '577fed1c21a64f2cb186c800ae467e9d' > /etc/machine-id WORKDIR /otp/firefox/ USER firefox ENTRYPOINT ["firefox", "https://ibank.otpbank.com.ua/ifobsClient/LoginShow.action?localeName=ru"]
У alpine есть несколько преимуществ - он легковесный, так например, результирующий образ получился 295 Mb, против 810 на базе Ubuntu. Так же в составе уже идет Firefox 52 ESR и OpenJDK. Собираем и запускаем образ, на первый взгляд все тоже самое, разве что окна с запросом на разрешения запуска аплетов немного отличаются
После этого мы попадаем в сам клиент банк, но как оказалось чуть позже - радоваться было рано. При выполнение базовых операций, например клонирование документа, начали появляться ошибки
При этом в самой консоли были следующие ошибки
com.cs.crypto.applet.client.CSApplet Exception: java.security.AccessControlException: access denied ("java.util.PropertyPermission" "java.io.tmpdir" "read") at java.security.AccessControlContext.checkPermission(AccessControlContext.java:472) at java.security.AccessController.checkPermission(AccessController.java:884) at java.lang.SecurityManager.checkPermission(SecurityManager.java:549) at net.sourceforge.jnlp.runtime.JNLPSecurityManager.checkPermission(JNLPSecurityManager.java:292) at java.lang.SecurityManager.checkPropertyAccess(SecurityManager.java:1294) at java.lang.System.getProperty(System.java:717) at com.cs.crypto.applet.processor.IOProcessor.getTmpPathFile(Unknown Source) at com.cs.crypto.applet.processor.IOProcessor.loadPathProp(Unknown Source) at com.cs.crypto.applet.processor.IOProcessor.getLastPath(Unknown Source) at com.cs.crypto.applet.client.CSApplet.doRun(Unknown Source) at com.cs.crypto.applet.base.AbstractThreadJApplet.run(Unknown Source) at java.lang.Thread.run(Thread.java:748) com.cs.crypto.applet.client.CSApplet Exception: Code: CRYPTO_ERR_SYSTEM_OPERATION. CryptoMsg: Произошла системная ошибка при выполнения крипто-операции. Попробуйте еще раз.
Немного поискав информацию по данной ошибке, стало очевидно, что дело связано с java.policy. Я не знаю тонкостей, но получается, что java плагины от Oracle и Icedtea абсолютно по разному работают в контексте java applets. Так что, для успешной работы в OpenJDK нам необходимо создать файл с политикой и разместить его в домашней папке пользователя firefox
$ cat ~/.java.policy grant codeBase "https://ibank.otpbank.com.ua/ifobsClient/-" { permission java.security.AllPermission; };
Модифицируем наш Dockerfile и добавляем соответствующий файл
FROM alpine:3.8 ENV TERM=xterm \ JAVA_HOME=/usr/lib/jvm/java-1.8-openjdk/jre \ PATH=$PATH:/usr/lib/jvm/java-1.8-openjdk/jre/bin:/usr/lib/jvm/java-1.8-openjdk/bin RUN apk update \ && apk add bash \ firefox-esr \ icedtea-web-mozilla \ icedtea-web \ libcanberra-gtk3 \ adwaita-gtk2-theme \ adwaita-icon-theme \ ttf-ubuntu-font-family \ ttf-liberation \ ttf-dejavu \ shadow \ && mkdir /otp RUN useradd firefox -s /bin/bash -m -d /otp/firefox/ \ && chown -R firefox:firefox /otp/firefox/ \ && echo '577fed1c21a64f2cb186c800ae467e9d' > /etc/machine-id \ && { \ echo 'grant codeBase "https://ibank.otpbank.com.ua/ifobsClient/-" {'; \ echo ' permission java.security.AllPermission;'; \ echo '};'; \ } > /otp/firefox/.java.policy WORKDIR /otp/firefox/ USER firefox ENTRYPOINT ["firefox", "https://ibank.otpbank.com.ua/ifobsClient/LoginShow.action?localeName=ru"]
После этого пересобираем образ и проверяем, что все работает. Вообще давать полные права как в случае с java.security.AllPermission является плохой практикой, и по хорошему в файле ~/.java.policy необходимо прописать только необходимые права, но так как мы используем контейнер только для работы с клиент банком, это не так критично. Так что формирование списка политик оставляю вам в качестве домашнего задания.