пятница, 23 января 2015 г.

Упаковка своего ПО в rpm-пакет

Детально описание rpm-пакетов, и особенности их создания описаны тут (оригиналперевод).
В данной статье будет дана краткая справка для ленивых =).

Общая информация

RPM пакеты бывают двух видов:
  • бинарные rpm-пакеты (<name>.rpm)
  • пакеты исходников (<name>.src.rpm)
В бинарные rpm пакеты упакована следующая информация:
  • файлы, которые необходимо установить на компьютер
  • пути, по которым эти файлы должны располагаться
  • права доступа к этим файлам
  • скрипты, которые должны выполняться в следующих ситуациях:
    • перед установкой пакета (%pre)
    • после установки пакета (%post)
    • перед удалением пакета (%preun)
    • после удаления пакета (%postun)
В src.rpm пакеты упаковываются исходники, из которых необходимо собрать ПО, а также
*.spec файл. В spec-файл записывается следующая информация:
  • Общая информация о пакете/пакетах.
  • Информация об исходниках
  • Скрипт подготовки к сборке (%prep)
  • Скрипт сборки ПО из исходников (%build)
  • Скрипт инсталляции ПО (%install) ( тут осуществляется установка собранных файлов в "корневую директорию бинарного пакета" ).
  • Скрипты установки/удаления. (%pre,%post,%preun,%postun).
  • Информация о файлах (%files) ( описание файлов, их атрибутов из"корневой директории бинарного пакета", которые войдут в пакет ).
  • История изменений (%changelog) данного spec-файла.
Исходники упаковываются в архив tar.gz (тарбол). Дополнительно в src.rpm пакете могут быть патчи ( накладываются поверх распакованных источников ), картинки, desktop-файлы.
Кроме исходников и патчей все дополнительные файлы src.rpm пакета должны быть скопированы в "корневую директорию бинарного пакета" в секции %install, а также должны быть указаны в секции %files.

Состав spec-файла

Spec-файл - это текстовый файл, содержащий директивы RPM.
Подробнее про spec-файл можно почитать тут.

В случае если из одного дерева исходников получается несколько программных продуктов (например серверное и клиентское приложение, которые могут быть установлены отдельно), то рациональнее написать один spec-файл, который будет собирать несколько rpm-пакетов (в указанном случае один пакет для серверного приложения, и один для клиентского приложения). Дополнительные пакеты называются субпакеты. Подробнее о субпакетах следует почитать тут и, обязательно тут. Отдельно замечу, что можно сделать так, что бы из spec-файла создавались бинарные rpm только для субпакетов, для этого в spec-файле должна отсутствовать секция %files без имения субпакета ( это секция %files для основного пакета ).

Заголовок и описание пакета

Достаточно подробно про заголовок и описание пакета написано тут.
Под заголовком понимается выставление значений для директив: Name, Version, и так далее.
В упрощённом виде, необходимо задать значения для директив:
  • Name (обязательно);
  • Version (обязательно);
  • Release (обязательно);
  • Epoch (в случае необходимости, описано ниже);
  • Requires (обязательно, для корректной установки пакета);
  • BuildRequires (крайне желательно, для корректной пересборки бинарных пакетов, из src.rpm пакета);
  • Provides (крайне желательно, в случае если вы заменяете установленный в системе пакет, для того, что бы RPM установил, что данный пакет предоставляет теже функции, что и ранее установленный пакет, и старый пакет можно обновить);
  • Group ( желательно для графический менеджеров, для отображения информации о вашем ПО в соответствующих разделах меню;
  • Summary ( желательно, для того, что бы можно было прочитать краткую информацию о вашем пакете, например в графическом менеджере, или в терминале, удобно при администрировании );
Многострочное описание пакета ( %description ) также очень желательно.
В случае, если ваш пакет зависит от стороннего ПО определённой версии,  и в системе, под которую вы собираете ПО для rpm-пакета стороннего ПО указана эпоха (Epoch), то следует, обязательно, в зависимостях указать эпоху пакета ( лучше такую же, что и у оригинального стороннего пакета ).
Например: Requires: qt >= 1:4.7.4
Подробнее про эту тонкость можно прочитать тут.

Подготовка к сборке ПО

Подготовка к сборке выполняется в секции %prep.
Достаточно подробно об этапе подготовке к сборке ПО описано тут.
По сути, в данной секции макрос %setup распаковывает исходники в директорию сборки, и накладывает на распакованные исходники патчи (если это указано).Если не требуется наложить патчи на исходники, или ещё какой-экзотики, то секция %prep выглядит так:
%prep
%setup -q

Сборка ПО

Cборка выполняется в секции %build.
Достаточно подробно об этапе сборки ПО описано тут.
По сути в данной секции записывается shell-скрипт, который выполняется в корневой директории распакованного тарбола (BUILD - обычно это папка ~/rpmbuild/BUILD/<tarbol_unpacket_folder> - например ~/rpmbuild/BUILD/qt-everywhere-opensource-src-4.7.4) с исходниками.
Пример секции %build:
%build
qmake -r "CONFIG+=static" %{silent_cmd}

#make as quickly as possible
cpu_count=$(cat /proc/cpuinfo | grep ^processor | wc -l)
make -j${cpu_count} %{silent_cmd}

#сборка документации (требуется много дополнительных пакетов)
docs/latex/make_qthelp.sh %{silent_cmd}
В примере скрипт выполняет следующие действия:
  1. Через вызов qmake, формирует Makefile для сбора релизной версии ПО с определённой конфигурацией.
  2. Считывает из /proc/cpuinfo количество процессоров, для более быстрой сборки исходников.
  3. Запускает команду make (по умолчанию цель all), для которой указывает количество потоков выполнения, равным количеству процессоров в системе, для более быстрой сборки.
  4. Выполняет отдельный скрипт, для сборки файлов документации.

Инсталляция ПО

Инсталляция ПО в директорию RPM_BUILD_ROOT осуществляется в секции %install.
Упрощая: RPM_BUILD_ROOT - это директория, которая является корнем для файловой системы бинарных rpm-пакетов (обычно это ~/rpmbuild/BUILDROOT/<packet-name> например ~/rpmbuild/BUILD/qt-4.7.4-4.el6.386)
Достаточно подробно об этапе подготовке к сборке ПО описано тут.
По сути, это скрипт, который тоже выполняется в директории BUILD.
Лучше всего, если в Makefile-е будет определена цель install - тогда достаточно просто вызывать INSTALL_ROOT="$RPM_BUILD_ROOT" make install
Если же цель install не определена в make-файле, тогда необходимо в данной секции расписать копирование необходимых файлов, с учётом того, что вместо корня ( / ) путь для файлов следует начинать с $RPM_BUILD_ROOT. Копирование надо расписать в синтаксисе shell-скрипта. Если необходимо для копируемых файлов создать папки, то их создание должно быть прописано до копирования, также, в виде shell-скрипта.

Пример секции %install:
%install

#запускаем make install
INSTALL_ROOT="$RPM_BUILD_ROOT" make install

# создаём папку doc - для файлов документации
install -d "$RPM_BUILD_ROOT/%{tds_install_path}/doc"

# копируем файлы документации в папку doc ( это не прописано в Makefile-е для цели install)
cp docs/latex/tds_cpanel.qch "$RPM_BUILD_ROOT/%{tds_install_path}/doc"
cp docs/latex/tds_cpanel.qhc "$RPM_BUILD_ROOT/%{tds_install_path}/doc"

Очистка после инсталляции ПО

После сборки и инсталляции ПО, крайне желательно очистить директорию RPM_BUILD_ROOT. Если этого не сделать, то сохранившиеся файлы будут влиять на сборку следующего пакета. Это выполняется в секции %clean

Пример секции %clean
%clean

rm -rf "$RPM_BUILD_ROOT"

Определение скриптов установки/удаления

К описанному тут добавлю следующее:
  • для субпакетов после маркера скрипта необходимо указывать имя субпакета ( тоже, что указывали в макросу %package данного субпакета )
  • при смене версии пакета ( вне зависимости от того, обновляем мы пакет до более новой версии, или откатываем пакет к более старой версии ), порядок выполнения скриптов следующий:
    • %pre скрипт устанавливаемого пакета
    • %post скрипт устанавливаемого пакета
    • %preun скрипт удаляемого пакета
    • %postun скрипт удаляемого пакета.
    Подробнее о подводных камнях при обновлении rpm-пакета написано тут.
Пример скриптов:
%pre server
if (( ${1} > 1 )); then
    # upgrade
    stop tds-server || true
fi

%post server
if (( ${1} == 1 )); then
    # new installation
    grep ^%{tds_user} /etc/passwd > /dev/null || useradd -r -U -M -s /bin/bash -d %{tds_install_path} %{tds_user}
    TDS_ARCHIVE_DIR=$(dialog --stdout --title "Specify archive's path" --clear --dselect %{default_archive_path} 10 60)
    case $? in
        0)
            echo "Select ${TDS_ARCHIVE_DIR}";;
        1)
            TDS_ARCHIVE_DIR="%{default_archive_path}";;
        255)
            TDS_ARCHIVE_DIR="%{default_archive_path}";;
    esac
    [ -d "${TDS_ARCHIVE_DIR}" ] || mkdir -p ${TDS_ARCHIVE_DIR}
    chown -R %{tds_user}:%{tds_user} ${TDS_ARCHIVE_DIR}

    echo -e "
    description \"Topaz's documentation server\"
    exec su -l tds -c '/usr/local/Lemz/tds/tds_server --archive_path ${TDS_ARCHIVE_DIR} --listen_port 3108' >> /var/log/tds/tds_server.log 2>&1
    start on (runlevel [5])
    stop  on (runlevel [!5])
    respawn
    respawn limit 10 90
    " > /etc/init/tds-server.conf
fi
start tds-server

%preun server
if (( $1 == 0 )); then
    # removing package (not upgrade)
    stop tds-server || true
    rm -f /etc/init/tds-server.conf
fi

%postun server
if (( $1 == 0 )); then
    # removing package (not upgrade)
    userdel %{tds_user}
    ls -A %{tds_install_path}/* || rm -rf %{tds_install_path}
    ls -A %{lemz_install_path}/* || rm -rf %{lemz_install_path}
    rm -rf /var/log/tds
fi
Как видно из примера, в данных скриптах используется синтаксис shell-скриптов, плюс к этому, можно использовать значения rpm-переменных через форму %{varname} (например %{tds_install_path}).

Заполнение списка файлов

Заполнение списка файлов, которые будут установлены в систему, при установке пакетов, а также какие надо/не надо удалить при удалении пакетов, осуществляется в секции %files.
Подробно это расписано тут (а также в подпунктах параграфа 9 из руководства, указанного ссылке).
Особенное внимание следует уделить информации, относительно файлов конфигураций и создаваемых во время работы ПО файлов, которые должны быть удалены при удалении пакетов,  которую можно прочитать тут.
Добавлю, что для субпакетов надо писать отдельную секцию %files с именем субпакета через пробел. Также рекомендуется в начале секции пакета/субпакета, указывать атрибуты по умолчанию через %detattr.
Пример секции %files:
%files server
# бинарный файл сервера
%attr(0555,root,root) %{tds_install_path}/tds_server
# папка для логов сервера
%attr(1777,root,root) /var/log/tds

%files cpanel
# бинарный файл клиента
%attr(0735,root,root) %{tds_install_path}/tds_cpanel
# папка для встроенной в клиент справки
%attr(1777,root,root) %{tds_install_path}/doc
# файлы справки
%doc %attr(0444,root,root) %{tds_install_path}/doc/tds_cpanel.qch
%doc %attr(0444,root,root) %{tds_install_path}/doc/tds_cpanel.qhc
Также добавлю, что если в директории RPM_BUILD_ROOT ( куда скопировали файлы при инсталляции ), будут файлы, которые, так или иначе, не указаны в какой-либо секции %files, то сборка пакета завершится с ошибкой. Такие файлы надо, либо удалять в конце секции %install, либо помечать их в какой-либо секции %files через макрос %exclude.

История изменения пакета

История изменения пакета записывается в секцию %changelog.
Подробнее об этом написано тут.
Обязательно обратите внимание на правильность формата даты, иначе при сборке пакета получите сообщение об ошибке.
Пример заполнения секции %changelog
%changelog
* Thu Dec 30 2015 Rinat Gadelshin <rinat@example.com> - 4.7.4-1
- Initial build for Oracle linux 6.5 i686

Сборка и отладка rpm-пакета

Для сборки rpm-пакетов используется утилита rpmbuild.
Также для сборки под различные дистрибутивы можно использовать mock. Но это отдельная тема, выходящая за рамки данной справки.
Самый "чистый" способ, это собирать бинарные пакеты в том же дистрибутиве и на той же архитектуре, как и те, для которых пакеты предназначены. Для этого можно развернуть образ в виртуальной машине ( например в VirtualBox ).
В примере ниже мы будем собирать пакеты для Oracle Linux 6 на этом же дистрибутиве.

Подготовка к сборке

Устанавливаем утилиту rpmbuild: [root@grinvb ~] yum install rpm-build
Подготавливаем структуру папок. По умолчанию rpmbuild "работает" в директории ~/rpmbuild. Этот путь можно изменить в ~/.rpmmacros. Подробнее в man-е.
Создадим структуру папок, для сборки пакетов:
[user@grinvb ~] mkdir -p ~/rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}
Назначение директорий следующее:
BUILDУтилита rpmbuild использует этот каталог в качестве каталога сборки ПО.
RPMSУтилита rpmbuild помещает в этот каталог собранные бинарные rpm-пакеты.
SOURCESВ этот каталог необходимо поместить тарболлы с исходным кодом проектов, запланированных на сборку.
SPECSВ этот каталог помещаются spec-файлы всех rpm-пакетов, которые запланированы на сборку.
SRPMSУтилита rpmbuild помещает в этот каталог собранные src.rpm-пакеты с исходным кодом.
В директорию SPECS следует положить написанный нами spec-файл. В директорию SOUCES, следует положить исходники ( тарбол, патчи, картинки и т.д. ).

Маленький совет по подготовке к сборке rpm-пакетов для ПО

Обычно разработку я осуществляю не в директории ~/rpmbuild. Соответственно, мне нужно упаковать свои исходники для сборки в tarbol. Также при упаковке следует учитывать, что там должно быть достаточно исходников, что бы из них можно было пересобрать rpm-файлы для других дистрибутивов/платформ ( а вот лишние файлы увеличат размер тарбола, а вслед за ним и srp.rpm архива ). Также хочется автоматизировать процесс установки текущей версии ПО для rpm-ок.

Обычно в качестве системы контроля версий я использую git. Приведу пример скрипта в одном из проектов, который автоматически пакует необходимые для сборки файлы в тарбол в директорию ~/rpmbuild/SOUCES, а также на основе шаблона spec-файла и данных, возвращаемых комадой git describe формирует spec файл для сборки rpm-ок для текущей версии ПО.
#!/bin/bash

# Говорим, где у нас находится корень для rpmbuild
rpmbuild_root=~/rpmbuild

# Запоминаем оригинальную директорию ( откуда вызван скрипт )
prevdir=$(pwd)
curdir=$(dirname ${0})

# Указываем, где относительно скрипта располагается корень проекта
repodir=$(cd ${curdir}/../../;pwd)     # workspace/tds
reponame=$(basename ${repodir})  # tds

# Переходим в корневую папку проекта
cd ${repodir}

# Обновляемся из репозитория до последней версии, включая подмодули
git pull
git submodule update --init --recursive

# Запоминаем в переменные текущую версию и релиз ( на основе git tags )
tds_version=$(git describe --tags --abbrev=0)
tds_release=$(echo $(git describe --tags)-0-0 | cut -d- -f2)

# Формируем имя тарблоа, в соответствии со считанной версией ПО
tarbol=${rpmbuild_root}/SOURCES/tds-${tds_version}.tar.gz

# Переходим на папку выше каталога с исходиками,
# для того, что упаковать этот каталог
cd "${repodir}/.." # workspace

# Удаляем старый тарбол, если он есть
[ -e "${tarbol}" ] && rm "${tarbol}"

# Упаковываем в тарбол, каталог с исходниками, исключая из него файлы,
#  не требующиеся для непосредственной сборки проекта
tar c --exclude-vcs \
--exclude=*.pro.user \
--exclude=Makefile \
--exclude=tds/.git* \
--exclude=tds/bin \
--exclude=tds/bin/* \
--exclude=tds/modules/ads_libs/tests \
--exclude=tds/modules/ads_libs/tests/* \
--exclude=tds/modules/ads_libs/apps \
--exclude=tds/modules/ads_libs/apps/* \
--exclude=tds/scripts \
--exclude=tds/scripts/* \
--exclude=tds/modules/ads_libs/ads_libs.pro \
--exclude=tds/modules/ads_libs/.git* \
--exclude=tds/modules/ads_libs/docs \
--exclude=tds/modules/ads_libs/docs/* \
--exclude=tds/modules/ads_libs/libs/tds_protocol \
--exclude=tds/modules/ads_libs/libs/tds_protocol/* \
--exclude=tds/modules/ads_libs/libs/ads_essential \
--exclude=tds/modules/ads_libs/libs/ads_essential/* \
--exclude=tds/modules/ads_libs/libs/3rdparty/lib_ucl/ucl-1.03 \
--exclude=tds/modules/ads_libs/libs/3rdparty/lib_ucl/ucl-1.03/* \
--exclude=tds/modules/ads_libs/libs/3rdparty/pcap \
--exclude=tds/modules/ads_libs/libs/3rdparty/pcap/* \
--exclude=tds/modules/ads_libs/libs/3rdparty/ads_codec_g726 \
--exclude=tds/modules/ads_libs/libs/3rdparty/ads_codec_g726/* \
--exclude=tds/modules/ads_libs/libs/3rdparty/segvcatch \
--exclude=tds/modules/ads_libs/libs/3rdparty/segvcatch/* \
--exclude=tds/modules/ads_libs/libs/3rdparty/asn \
--exclude=tds/modules/ads_libs/libs/3rdparty/asn/* \
--exclude=tds/modules/ads_libs/libs/3rdparty/jrtp \
--exclude=tds/modules/ads_libs/libs/3rdparty/jrtp/* \
--exclude=tds/modules/ads_libs/libs/3rdparty/breakpad \
--exclude=tds/modules/ads_libs/libs/3rdparty/breakpad/* \
--exclude=tds/modules/ads_libs/libs/3rdparty/ksv_srv_ctl \
--exclude=tds/modules/ads_libs/libs/3rdparty/ksv_srv_ctl/* \
--exclude=tds/modules/ads_libs/libs/3rdparty/portaudio \
--exclude=tds/modules/ads_libs/libs/3rdparty/portaudio/* \
--exclude=tds/modules/ads_libs/libs/3rdparty/qtlockedfile \
--exclude=tds/modules/ads_libs/libs/3rdparty/qtlockedfile/* \
--exclude=tds/modules/ads_libs/libs/3rdparty/pixfc-sse \
--exclude=tds/modules/ads_libs/libs/3rdparty/pixfc-sse/* \
--exclude=tds/modules/ads_libs/libs/3rdparty/qt47_backports \
--exclude=tds/modules/ads_libs/libs/3rdparty/qt47_backports/* \
--exclude=tds/src/apps/tds_readinput* \
--exclude=tds/src/libs/tds_readinput* \
--exclude=tds/src/libs/tds_endpoint* \
--file "${tarbol}" \
"${reponame}"

# Из шаблона ( scripts/rpm/tds.spec ) на основании считанных версий и релиза
# формируем ~/rpmbuild/SPECS/tds.spec для сборки rpm-пакетов
spectarget="${rpmbuild_root}/SPECS/tds.spec"
cat "${repodir}/scripts/rpm/tds.spec" | sed "s/tds-version-sed/${tds_version}/" | sed "s/tds-release-sed/${tds_release}/" > ${spectarget}

# Возвращаемся в оригинальную директорию ( откуда вызван скрипт )
cd ${prevdir}
Данный скрипт легко адаптировать под свой проект и свою систему контроля версий ( svn, hg, etc... ).

В шаблоне speс-файла ( scrpits/rpm/tds.spec ) по сути записан полноценный spec-файл, за следующими исключениями:
  • Для поля Version задан текст tds-version-sed
  • Для поля Release задан текст tds-release-sed
Скрипт читает тект из шаблона, считанное записывает в файл ~/rpmbuild/SPECS/tds.spec, а указанные фрагменты текста шаблона меняет на актуальные версию и релиз соответственно.

Сборка пакетов

Достаточно подробно сборка пакетов при помощи утилиты rpmbuild описана тут.
В большинстве случаев следует набрать команду:
rpmbuild -ba --target $(uname -m) --clean <path to spec-file>

С этими опциями будут собираться бинарные пакеты и src.rpm пакет для текущей архитектуры (той же, что и архитектура машины, на которой выполняется команда), и после сборки все сгенерённые файлы будут удалены.

При успешном выполнении в папке RPMS будут находится бинарные rpm-пакеты (в подпапке своей архитектуры, например i686). А в папке SRPMS будет находится src.rpm пакет.

В случае если при сборке пакета возникла ошибка, то следует:
  1. Идентифицировать стадию, на которой возникла ошибка (%prep, %build, %install, %files). Для этого надо посмотреть на лог выполнения в обратном порядке, и найти одну из этих надписей, как можно ближе к концу вывода.
  2. Проанализировать ошибку и внести исправления в spec-файл, либо добавить недостающее для сборки ПО, не забыв дописать его в BuildRequires директиву в spec-файле.
  3. Запустить сборку пакета с той стадии, на которой возникла ошибка, для этого надо воспользоваться опцией --short-circuit. Например для возобновления сборки с секции %install: rpmbuild -bi --short-circuit ~/rpmbuild/SPECS/tds.spec
После того, как все ошибки сборки исправлены и все стадии пройдены успешно, следует пересобрать пакеты полностью ( с опциями -ba --target --clean ).

Подписывание пакета

Для идентификации того, кто поставляет пакет ( на первом этапе, это сборщик пакета ), необходимо подписывать пакет. Стандартным механизмом для подписи пакетов является GPG-подпись. Для использования этого механизма необходимо сделать следующее:
  1. Сгенерировать пару GPG-ключей.
  2. Сконфигурировать RPM для использование ключа.
  3. Подписать пакет.
  4. Экспортировать  публичный  ключ  и  выложить  его  в общедоступное место.
Генерация пары GPG-ключей
Для генерации ключа необходимо вызвать команду gpg --gen-key и отвечать на задаваемые вопросы.
Пример генерации ключа.
grin@grin:~$ gpg --gen-key 
gpg (GnuPG) 1.4.16; Copyright (C) 2013 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
Your selection? 1
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048) 
Requested keysize is 2048 bits
Please specify how long the key should be valid.
         0 = key does not expire
        = key expires in n days
      w = key expires in n weeks
      m = key expires in n months
      y = key expires in n years
Key is valid for? (0) 
Key does not expire at all
Is this correct? (y/N) y

You need a user ID to identify your key; the software constructs the user ID
from the Real Name, Comment and Email Address in this form:
    "Heinrich Heine (Der Dichter) <heinrichh@duesseldorf.de>

Real name: Rinat Gadelshin
Email address: rgadelsh@gmail.com
Comment: grin
You selected this USER-ID:
    "Rinat Gadelshin (grin)<rgadelsh@gmail.com>

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
You need a Passphrase to protect your secret key.

We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.

Not enough random bytes available.  Please do some other work to give
the OS a chance to collect more entropy! (Need 237 more bytes)
......+++++
...........................+++++
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
..+++++
+++++
gpg: key 52D7329A marked as ultimately trusted
public and secret key created and signed.

gpg: checking the trustdb
gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model
gpg: depth: 0  valid:   2  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 2u
pub   2048R/52D7329A 2015-01-26
      Key fingerprint = 565C 5622 BD66 F17B DDB0  0378 CBF8 F0C1 52D7 329A
uid                  Rinat Gadelshin (grin) <rgadelsh@gmail.com>
sub   2048R/A394DD73 2015-01-26

В процессе генерации случайных данных (после того как в лог первый раз вывелась надпись We need to generate a lot of random bytes), я открыл флэш-игрушку в браузере ( стрелялка ) и играл в неё, пока не увидел, что ключ сгенерировался ( увидел надпись gpg: checking the trustdb ).
Пароль, заданный для данного ключа надо обязательно запомнить, мы будем вводить его каждый раз, когда будем подписывать пакет.
Также стоить запомнить идентификатор сгенерённого ключа ( в примере выше: 52D7329A ).
Посмотреть список сгенерённых ключей можно вызвав команду gpg --fingerprint
grin@grin:~$ gpg --fingerprint
/home/grin/.gnupg/pubring.gpg
-----------------------------
pub   2048R/8EF3B046 2015-01-26
      Key fingerprint = EBBC 7265 2A5E B117 E397  E63E FE30 9913 8EF3 B046
uid                  Rinat Gadelshin <rgadelsh@gmail.com>
sub   2048R/97A7AD93 2015-01-26

pub   2048R/52D7329A 2015-01-26
      Key fingerprint = 565C 5622 BD66 F17B DDB0  0378 CBF8 F0C1 52D7 329A
uid                  Rinat Gadelshin (grin) <rgadelsh@gmail.com>
sub   2048R/A394DD73 2015-01-26
Как видно из примера, может присутствовать сразу несколько сгенерённых ключей.

Конфигурирование RPM для использования ключа
Для того, что бы подписывать rpm-пакеты сгенерённым ключом, необходимо в файл ~/.rpmmacros добавить следующие строки:
%_signature gpg
%_gpg_name Rinat Gadelshin (grin) <rgadelsh@gmail.com>
По сути, мы прописываем строку uid, относящуюся к нашему ключу из вывода команды gpg --fingerprint. Видно что для нашего ключа ( 52D7329A ) строка содержит комментарий (grin), а для другого ключа ( 8EF3B046 ) комментарий не содержится.

Подпись пакетов
Для подписи пакетов, при их создании, для rpmbuild требуется указать опцию --sign, тогда, перед сборкой пакета (до выполнения секции %prep), он будет спрашивать у вас пароль для ключа (конфигурация которого прописана в ~/.rpmmacros). Дальнейшую сборку rpmbuild будет продолжать только, если вы указали правильный пароль.
Подписать можно и готовый пакет.
Для добавления пакету подписи нужно вызвать команду
rpm --addsign <rpm-packet-file>
Аналогично команда
rpm --resign <rpm-packet-file>
позволяет изменить подпись у подписанного rpm-пакета.

Экспорт ключа
При установке пакета RPM проверяет его подпись. В случае его ключ, которым подписан пакет, уже добавлен в базу RPM, то он устанавливает пакет без вопросов. В противном случае он уведомляет пользователя, что пакет подписан неизвестным ему ключом, или не подписан вовсе. Можно конечно поставить из терминала пакет без подписей, но это не верно, с точки зрения безопасности, и не каждый грамотный администратор на это пойдёт.
Для того, что бы RPM мог добавить себе в список доверенных ключей gpg-ключ, его открытую (public) часть необходимо сначала экспортировать в файл, который в дальнейшем RPM сможет импортировать.
Экспорт ключа осуществляется командой
gpg --export --armor 52D7329A > /tmp/grin-second-gpg-key
Вместо 52D7329A необходимо поставить идентификатор своего ключа. Вместо /tmp/grin-second-gpg-key можно поставить другой путь для файла экспортированного ключа.
Для того, что бы другие машины могли импортировать этот ключ, необходимо обеспечить свободный доступ к нему. Например его можно выложить на свой web-сервер.
После этого на прочих системах достаточно выполнить команду
rpm --import http://server.name/grin-second-gpg-key
Где вместо server.name/grin-second-gpg-key  указать путь к файлу экспортированного ключа.

Комментариев нет:

Отправить комментарий