28 июня 2016 г.

Как отсыпать совсем немножечко рута для Java и разрешить захватывать сетевые пакеты

В различных дистрибутивах Linux такие программы, как tcpdump и wireshark, использующиеся для анализа сетевого трафика, обычно требуют прав суперпользователя для получения доступа к возможностям захвата пакетов. Точно также дело обстоит и с Java-программами, которые используют системную библиотеку libpcap. Причем в этом случае вам придется запустить с неограниченными правами саму виртуальную машину Java, которая уже в свою очередь будет исполнять ваш код. Я думаю, мало кто любит лишний раз сорить суперправами направо и налево, поэтому в Linux, начиная с ядра 2.2 появилась такая штука, как Linux Capabilites, которая позволяет наделить непривилегированные процессы только необходимыми возможностями суперпользователя. В данном случае, например, нас интересует только возможность перехвата и отправки сетевых пакетов. Для установки и просмотра Linux Capabilites необходимы программы setcap и getcap, соответственно. У меня в openSUSE эти утилиты находятся в пакете libcap-progs, а в Ubuntu в пакете libcap2-bin.

Установка возможности захвата и отправки сетевых пакетов и возможности изменения конфигурации интерфейсов выглядит так:
setcap cap_net_raw,cap_net_admin=eip /usr/local/lib64/jdk1.8.0_77/bin/java
Просмотр установленных возможностей:
getcap /usr/local/lib64/jdk1.8.0_77/bin/java
/usr/local/lib64/jdk1.8.0_77/bin/java = cap_net_admin,cap_net_raw+eip
Всё просто, но если после установки этих возможностей попробовать запустить программу java из под обычного пользователя, то это не очень получится:
java -version
java: error while loading shared libraries: libjli.so: cannot open shared object file: No such file or directory
Это происходит из-за того, что при наличии у непривилегированного процесса возможностей, которыми он вроде как обладать не должен, загрузчик динамических библиотек запрещает загрузку библиотек из недоверенных каталогов. Доверенными каталогами по умолчанию являются /lib и /usr/lib (а также /lib64 и /usr/lib64).

С помощью утилиты ldd посмотрим, какие динамические библиотеки использует java: 
ldd /usr/local/lib64/jdk1.8.0_77/bin/java
        linux-vdso.so.1 (0x00007ffc91ffd000)
        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fc3dc124000)
        libjli.so => /usr/local/lib64/jdk1.8.0_77/bin/../lib/amd64/jli/libjli.so (0x00007fc3dbf0d000)
        libdl.so.2 => /lib64/libdl.so.2 (0x00007fc3dbd09000)
        libc.so.6 => /lib64/libc.so.6 (0x00007fc3db961000)
        /lib64/ld-linux-x86-64.so.2 (0x00005584e041d000)
Как видно из вывода команды ldd, при запуске java не удалось загрузить как раз ту самую библиотеку, которая находится в недоверенном каталоге ../lib/amd64/jli/.

Это можно легко пофиксить, добавив файл с расширением .conf  в каталог /etc/ld.so.conf.d, в котором перечислить все необходимые пути. В случае с java это всего лишь один каталог:
echo /usr/local/lib64/jdk1.8.0_77/lib/amd64/jli > /etc/ld.so.conf.d/java.conf
После изменения файлов в /etc/ld.so.conf.d может потребоваться перестроить кэш библиотек, для этого нужно выполнить:
ldconfig
Проверяем, что загрузчик видит нашу библиотеку:
ldconfig -v |grep jli
/usr/local/lib64/jdk1.8.0_77/lib/amd64/jli:
     libjli.so -> libjli.so
Ну и на всякий случай, команда для удаления всех дополнительных возможностей у программы:
setcap -r /usr/local/lib64/jdk1.8.0_77/bin/java
Ещё хотелось бы отметить, что не стоит без лишней необходимости добавлять нестандартные возможности вашей глобальной системной Java-машине, потому что в этом случае любая запущенная Java-программа сможет ими воспользоваться. Для разработки и запуска программ с завышенными потребностями лучше завести отдельный экземпляр Java-машины.

Где отрыто

Как приструнить Wireshark
Небаг в багтрекере Java
Куришка: man capabilities


28 марта 2016 г.

Установка Java Development Kit в Linux из архива tar.gz

Данный пост, это просто обновление поста 2010 года, который до сих пор пользуется довольно большой популярностью, а так как с тех времен уже немало воды утекло, то я посчитал своим долгом актуализировать эту информацию. По пути также кое-что было переработано, надеюсь, в сторону понятности.
Если вам потребовался стандартный JDK от Oracle, вместо OpenJDK, который поставляется с современными свободными дистрибутивами Linux, то его можно скачать либо с дополнительных сторонних репозиториев для вашего дистрибутива, либо напрямую с сайта www.oracle.com. Если ставить Java из репозиториев для вашего дистрибутива, то скорее всего всё само настроется и заработает, и ни о чем беспокоится вам не придется, но если вы скачали JDK или JRE в виде архива tar.gz с сайта Oracle, придется совершить несколько дополнительных телодвижений.

На данный момент скачать JDK с сайта Oracle можно в двух видах: это RPM-пакет и вышеупомянутый архив tar.gz. Про то, на сколько удачно ставится JDK из RPM-пакета, мне сказать нечего - не пробовал. Я предпочитаю устанавливать JDK именно из архива, потому что так я получаю полное управление и понимание того, что и откуда у меня будет запускаться, а также универсальное решение, которое подойдет для всех дистрибутивов, а не только для RPM-based.

План действий
  1. Скачать нужную версию JDK (архив tar.gz)
  2. Распаковать архив
  3. Настроить переменные окружения
  4. Настроить update-alternatives
Куда распаковать?

Итак, скачали JDK. Распаковываем. Но куда? Вроде бы тупой вопрос, но я далеко не сразу пришёл к правильному  для себя  решению. Сейчас я все JDK распаковываю в каталог /usr/local/lib64 и там у меня одновременно живёт несколько версий JDK. Например, на данный момент, там у меня есть каталоги jdk1.7.0_79 и jdk1.8.0_77. В дополнение к этим каталогам, в /usr/local/lib64 у меня находится симлинк с именем jdk, который сегодня чаще всего указывает на каталог jdk1.8.0_77. К такой конфигурации я пришел, когда потребовалось периодически переключаться между различными версиями JDK, причем переключаться нужно было глобально во всей системе. Каким образом такая конфигурация помогает переключаться между версиями, станет понятно ниже.

13 февраля 2016 г.

USB redirection error в Spicy на openSUSE

Столкнулся в openSUSE с тем, что в gtk-клиенте для spice не работает проброс USB-устройств внутрь гостевой системы. После выбора устройства для проброса появляется окно с просьбой ввести пароль суперпользователя. Вводим пароль, жмём OK и получаем сообщение вроде этого:
Could not redirect Kingston DataTraveler 3.0 [0951:1666] at 2-13: Error setting USB device node ACL: 'Error setting facl: Operation not permitted'
Оказывается, что в пакете spice-gtk вместе с клиентом spicy есть еще такая программка с длинным названием spice-client-glib-usb-acl-helper. При подключении USB-устройства через spicy эта программа, по всей видимости, проверяет и назначает файлу этого устройства дополнительные права через ACL. Проблема в том, что для этого не помешало бы быть root'ом, но не смотря на запрос пароля суперпользователя, spice-client-glib-usb-acl-helper выполняется с правами обычного пользователя из-под которого запущен spicy. Короче, где-то недокостылили. Самым простым решением этой проблемы является установка бита setuid на файл spice-client-glib-usb-acl-helper:
chmod u+s /usr/bin/spice-client-glib-usb-acl-helper
После этого действия USB-устройства начинают успешно пробрасываться.

Для удобства теперь можно также отключить запрос пароля суперпользователя. Делается это через polkit. В файле /usr/share/polkit-1/actions/org.spice-space.lowlevelusbaccess.policy надо отредактировать содержимое элемента defaults следующим образом: