Воспроизводимые сборки на примере GNU/Guix

Эта обзорная статья рассматривает концепцию воспроизводимых сборок. Она не затрагивает глубокие технические детали. В качестве демонстрации используется пакетный менеджер GNU/Guix.

Воспроизводимая сборка также называется детерменированной компиляцией 1. Сборка называется воспроизводимой, если исходный код, программное окружение и инструкции сборки будут давать на выходе побитово идентичные копии всех артефактов.

Соответствующее программное окружение, инструкции сборки и исходный код также как и ожидаемый воспроизводимый артефакт определены авторами ПО или дистрибьюторами. Артефакты сборки – это часть сборки, которая является основным ожидаемым результатом.

Термины

Исходный код
Берется из системы контроля версий определенной ревизии или архива.
Соответствующие аттрибуты среды сборки
Включают в себя зависимости и их версии, флаги конфигурации сборки и переменные окружения.
Артефакты
Являются исполняемыми файлами, пакетами дистрибутива или образами файловой системы. Обычно не содержат в себе логов сборки или похожих на логи вещей. Воспроизводимость артефактов достигается путём побитового сравнивания при помощи криптографических хеш-функций 2.
Авторы и дистрибьюторы
Лица, которые утверждают воспроизводимость артефактов. Они могут быть авторами ПО, сопровождающими дистрибутива или иным лицом, распространяющим ПО.

Почему воспроизводимые сборки важны

Sorry, your browser does not support SVG.
Рис. 1.: Логотип движения за воспроизводимые сборки

Не смотря на то, что каждый может исследовать исходный код свободного и открытого ПО на наличие вредоносных дыр, большая часть этого ПО распространяется в виде собранных исполняемых файлов без методов проверки соответствия с исходным кодом.

Это стимулирует нападки на разработчиков, которые выпускают ПО, не только посредством традиционного использования эксплоитов, но также в форме политического влияния или шантажа.

Это особенно важно для разработчиков, создающих ПО, связанное с приватностью и безопасностью: атака на это ПО, обычно компроментирует политические цели таких как: инакомыслящие, журналисты, информаторы, а также любого, кто желает использовать безопасные от репрессивного режима каналы связи.

Не смотря на то, что отдельно взятые разработчики кажутся естественной мишенью, под удар также попадают инфраструктуры для сборки, которые при успешной атаке предоставляют доступ к большому числу компьютерных систем. Модифицирование собранных исполняемых файлов в сборочной инфраструктуре вместо модифицирования исходного кода делает эти изменения практически невидимыми для оригинальных авторов и пользователей программы.

Целью воспроизводимых сборок является возможность проверки на факт наличия уязвимостей и дыр в безопасности в процессе сборки. Один и тот же вход гарантирет один и тот же выход. Это позволяет понять находится перед нами оригинальная правильная или скомпроментированная неправильная сборка. Это позволяет предотвращать такие угрозы и атаки как можно раньше, так как любое изменение будет быстро обнаружено.

Как добиться воспроизводимости

Достижение воспроизводимости сборок требует сотрудничества нескольких ролей в производстве ПО.

Создание детерменированного окружения сборки

Чтобы ПО могло быть воспроизводимым, исходный код не должен создавать неконтроллируемые вариации в логе вывода.

Будет лучше, если пользователи обнаружат вариации, которые выдают невоспроизводимые исполняемые файлы. Создание протокола для тестов, который бы пересобирал программу в нужной вариации, сильно поможет.

Определение окружения сборки

Разные версии утилит для сборки скорее всего выдадут разный результат, пользователи должны быть в состоянии воссоздать окружение сборки. Это не обозначает, что сами утилиты для сборки должны быть побитово идентичными, вернее результат их работы должен оставаться одинаковым.

Окружение сборки может быть определено, пока ПО только разрабатывается или поверх существующего процесса сборки.

Дистрибуция окружения сборки

Пользователи должны знать какое окружение должно быть воссоздано, чтобы собрать ПО.

Если окружение сборки уже определено или является частью исходного кода, то дальнейшие шаги не требуются.

В остальных же случаях, необходимо поместить описание рядом с исполняемыми файлами о том, как воспроизвести окружение.

Предоставление протокола сравнения

Пользователи должны иметь легкий способ воссоздания окружения сборки, получения исходного кода, проведения процесса сборки и сравнения результата.

В идеале протокол сравнения должен быть прост в сравнении исполняемых файлов. Сравнение байтов и криптографических хешей легко реализовать и воспринимать.

Как проверить воспроизводимость

Техника, используемая в воспроизводимых сборках, называется Diverse Double-Compilation3 и создана она David A. Wheeler.

Алгоритм примерно следующий:

  • Собрать в первый раз
  • Сохранить результат
  • Сделать максимальное количество изменений окружения
  • Собрать второй раз
  • Сравнить результаты

diffoscope – утилита, которая разработана для обнаружения проблем в сравнении результатов сборки.

Список вариаций:

  • Дата и время
  • путь сборки
  • hostname
  • domain name
  • Файловая система
  • Переменные окружения
  • Часовой пояс
  • Язык
  • Локаль
  • Имя пользователя
  • ID пользователя
  • Группа пользователя
  • ID группы
  • Версия ядра
  • umask,
  • Тип CPU
  • Количество ядер CPU

Пример вариаций в Debian

disorderfs – утилита, помогающая тестировать файловую систему в воспроизводимой манере.

Воспроизводимые сборки в GNU/Guix

Sorry, your browser does not support SVG.
Рис. 2.: Логотип GNU/Guix

GNU/GUIX (произносится гикс ɡiːks) — функциональный пакетный менеджер и операционная система, разработанные Ludovic Courtès и сообществом GNU. Отличительной особенностью является использование воспроизводимых сборок и наличие только свободного ПО. Guix можно поставить на существующий дистрибутив GNU/Linux или в качестве отдельной системы на базе Linux или GNU Hurd. Раньше существовало разделение менеджера пакетов Guix и операционной системы GuixSD.

Определения пакетов описываются на диалекте языка Scheme – GNU/Guile. Большая часть исходников написана на нём же. Система изначально была основана на Nix. Отличиями от Nix(OS) являются язык для описания пакетов и сервисов, система инициализации (GNU Shepherd), использование ядра Linux-Libre (Linux без проприетарных блобов) и отсутствие проприетарных пакетов.

Все пакеты Guix собраны из исходников, включая различные прошивки (firmware), которые имеют тенденцию быть представлены в виде предсобранных исполняемых файлов без предоставления доступа к исходному коду. В Guix возможно получение предсобранных исполняемых файлов в виде подстановок. Эти подстановки представляют из себя программы, собранные на отдельных машинах (build farm), одинаковый результат которых вы получите, если бы собирали их на своей машине.

Guix собирает пакеты в полностью изолированном программном окружении для максимизации воспроизводимости – это важнейшая особенность, унаследованная от пакетного менеджера Nix.

Таким образом возможно очень малое количество вариаций между различными экземплярами окружений сборки; список доступных файлов в окружении, имя хоста, переменные окружения, локаль и так далее полностью под контролем и недоступны для изменения.

Единственное, что может различаться – это ядро и железо. Самый популярный пример того, как детали "железа" могут просочиться в процесс сборки – временная метка (timestamp). К сожалению это очень частое явление, которое используется в выводе сборки.

Подробнее о преимуществах GUIX в статье «Guix: Самая совершенная операционная система»4.

Существует русскоязычная конференция пользователей Guix в Telegram.

Установка Guix

Нам понадобится Guix в качестве пакетного менеджера, он предоставляет все инструменты для воспроизводимых сборок. Я подразумеваю, что вы использует GNU/Linux дистрибутив. Вы можете установить Guix, используя пакетный менеджер вашего дистрибутива:

Arch Linux | Gentoo

Если в вашем дистрибутиве нет готового пакета с Guix, то это не проблема. Guix возможно установить на любой дистрибутив GNU/Linux, единственное требование – иметь программы GNU tar и Xz. Для этого необходимо воспользоваться инструкцией «Бинарная установка».

При установке Guix создаст директории в следующих местах:

  • /gnu/store/
  • /var/guix/
  • ~/.guix-profile/

В /gnu/ помещаются все собранные программы в так называемый store. Если программа предполагается к установке, то сначала она собирается в store, а далее линкуется в ~/.guix-profile/ и помещается в переменную окружения PATH. Много версий программ может существовать на одной системе без конфликтов и хранится в store. Неиспользуемые программы могут быть удалены сборщиком мусора guix gc.

В отличии от большинства системных пакетных менеджеров (apt, dnf, pacman, portage) guix не требует доступа суперпользователя для функционирования. Guix, будучи функциональным пакетным менеджером, не использует состояние системы, поэтому ему не надо лазать в системные директории, для которых нужны права суперпользователя.

GNU Hello

Рассмотрим программу GNU Hello. Это небольшая hello-world программа, написанная на языке Си в соответствии со стилем и практиками проекта GNU. Найдём ее описание при помощи guix show hello.

w96k ~$ guix show hello
name: hello
version: 2.10
outputs: out
systems: x86_64-linux i686-linux
dependencies: 
location: gnu/packages/base.scm:73:2
homepage: https://www.gnu.org/software/hello/
license: GPL 3+
synopsis: Hello, GNU world: An example GNU package  
description: GNU Hello prints the message "Hello, world!" and then exits.
It serves as an example of standard GNU coding practices.  As such, it
+ supports command-line arguments, multiple languages, and so on.

Если не знаете название пакета, то используйте команду для поиска guix search.

Определение пакета

Определение пакета5 можно найти в файле ~/.config/guix/current/share/guile/site/2.2/ + строка location (location: gnu/packages/base.scm:73:2). Для простоты взаимодействия с системой существует мод для редактора Emacsemacs-guix. Из него можно проще навигироваться по системе.

Пакет GNU Hello в Guix определен следующим образом6:

(define-public hello
    (package
      (name "hello")
      (version "2.10")
      (source (origin
                (method url-fetch)
                (uri (string-append "mirror://gnu/hello/hello-" version
                                    ".tar.gz"))
                (sha256
                 (base32
                  "0ssi1wpaf7plaswqqjwigppsg5fyh99vdlb9kzl7c9lng89ndq1i"))))
      (build-system gnu-build-system)
      (synopsis "Hello, GNU world: An example GNU package")
      (description
       "GNU Hello prints the message \"Hello, world!\" and then exits.  It
  serves as an example of standard GNU coding practices.  As such, it supports
  command-line arguments, multiple languages, and so on.")
      (home-page "https://www.gnu.org/software/hello/")
      (license gpl3+)))

Это определение написано на языке GNU/Guile, диалекте лиспа Scheme. Вам не нужно понимать тонкости языка, чтобы его использовать в Guix. Как мы видим пакет описан декларативно.

Разберем это определение на составные части.

(package
 (name "hello")
 (version "2.10")
 ...

Имя и версия пакета.

(package 
    ...
    (source (origin
          (method url-fetch)
          (uri (string-append "mirror://gnu/hello/hello-" version
                              ".tar.gz"))
          (sha256
           (base32
            "0ssi1wpaf7plaswqqjwigppsg5fyh99vdlb9kzl7c9lng89ndq1i"))))
    ...

Источник пакета и его хеш-сумма. Источником пакета может являться как гит репозиторий, так и обычная ссылка на архив. Если хеш-сумма не совпадает, то guix не установит пакет. Строка mirror заменяет строку на url доступного зеркала7.

(package
   ...
   (build-system gnu-build-system)
   ...

Система сборки8. В Guix существуют несколько систем сборки, содержащие инструкции и окружение сборки. В данном случае подразумевается, что система сборки запустит команды ./configure && make && make check && make install и будет иметь соответствующие утилиты.

 (package
    ...
    (synopsis "Hello, GNU world: An example GNU package")
    (description
     "GNU Hello prints the message \"Hello, world!\" and then exits.  It
serves as an example of standard GNU coding practices.  As such, it supports
command-line arguments, multiple languages, and so on.")
    (home-page "https://www.gnu.org/software/hello/")
    (license gpl3+))

Синопсис, описание, домашняя страница и лицензия. Информация, полезная при поиске пакета.

Также определение пакета может содержать зависимости inputs, native-inputes и propogated-inputs, но в нашем случае пакет hello не содержит зависимостей. Если бы зависимости были, то они бы собрались раньше пакета и были бы добавлены в окружение, а зависимости зависимостей собрались бы еще раньше. Таким образом образуется дерево зависимостей. Например дерево зависимостей программы grep.

grep.png
Рис. 3.: дерево зависимостей программы grep

Вы можете создать такую же визуализацию командой guix graph grep | dot -Tpng > grep.png. Для выполнения нужен установленный пакет graphviz.

Загрузка исходного кода

Создадим отдельную директорию для нашей демонстрации и переместимся в нее:

mkdir hello
cd hello

Загрузим исходный код при помощи команды guix download и ссылки на пакет из определения пакета (необходимо подставить версию):

guix download -o hello.tar.gz mirror://gnu/hello/hello-2.10.tar.gz

Starting download of hello.tar.gz
From https://ftpmirror.gnu.org/gnu/hello/hello-2.10.tar.gz...
following redirection to `https://mirror.tochlab.net/pub/gnu/hello/hello-2.10.tar.gz`
709KiB                                    5.2MiB/s 00:00 [##################] 100.0%
hello.tar.gz
0ssi1wpaf7plaswqqjwigppsg5fyh99vdlb9kzl7c9lng89ndq1i    

Мы получили файл hello.tar.gz в текущей директории. Последняя строка вывода консоли – это хеш-сумма, которую мы видели в определении пакета.

Создание детерминированного окружения

Детерминированное окружение одно из требований для создания воспроизводимых сборок. Для создания изолированного окружения воспользуемся командой guix environment:

guix environment hello --container

Флаг --container использует возможность ядра cgroups, которая может не поддерживаться вашим процессором. Если это так, то используется вместо --container флаг --pure.

Выполнив данную команду Guix скачивает необходимые программы для окружения сборки и перемещает нас в него. То есть нам доступны только непосредственно программы для сборки (gnu-build-system) + при наличии зависимостей – собранные зависимости.

Строка [env] говорит о том, что вы находитесь в изолированном окружении. Чтобы выйти из окружения, используйте сочетание клавиш C-d. Для дальнейшей работы оставайтесь в изолированном окружении.

Сборка

Разархивируем архив при помощи tar и переместимся в папку с исходным кодом:

tar -xf hello.tar.gz

ls
hello-2.10  hello.tar.gz
cd hello-2.10

Далее соберем проект из серии команд:

  1. ./configure
  2. make
  3. make check
  4. make prefix=./dist install

И так мы получили в директории dist:

cd dist; cd bin;
./hello 
Hello, world!

Теперь можно выйти из виртуального окружения, нажав C-d.

Сборка через пакетный менеджер

Теперь вы примерно понимаете как происходит процесс сборки. Всё что мы сделали уже реализовано пакетным менеджером, для повторения всей процедуры используем guix build:

guix build hello --check

На самом деле данная команда использует дополнительные шаги, но основные – те, что мы уже выполнили.

Установка через пакетный менеджер

Основная команда для взаимодействия с пакетами guix package9, но есть удобные алиасы guix install для установки и guix remove для удаления. Попробуем установить пакет hello:

guix install hello
hello
Здравствуй, мир!
# Я использую русскую локаль в системе, поэтому выводит на русском. В
# окружении сборки переменная LANG не настроена. Поменяем переменную
# окружения LANG на en_US
LANG=en_US hello
Hello, world!

Проверка воспроизводимости

Для проверки существует команда guix challenge. Она проверяет совпадает ли хеш, собранной программы с тем, что находится на сборочной ферме (build farm):

guix challenge hello -v
/gnu/store/kg9mirg6xbvzcp0a98v7326n1nvvwgsj-hello-2.10 contents match:
local hash: 101vrhsf9zrzkir4yz01934il2b8wp3z30cc8h0aymzdzbwrikq9
https://ci.guix.gnu.org/nar/lzip/kg9mirg6xbvzcp0a98v7326n1nvvwgsj-hello-2.10: 101vrhsf9zrzkir4yz01934il2b8wp3z30cc8h0aymzdzbwrikq9

1 store items were analyzed:
- 1 (100.0%) were identical
- 0 (0.0%) differed
- 0 (0.0%) were inconclusive

Наша сборка побитово идентична, а значит воспроизводима. Таким образом мы рассмотрели воспроизводимые сборки на примере уже существующей программы в репозитории Guix.

Запись действий

Воспроизводимые сборки в других дистрибутивах

NixOS

nixos-logo-small.png
Рис. 4.: Логотип Nix(OS)

Дистрибутив GNU/Linux, которым вдохновлялся Guix.

1528 out of 1541 (99.16%) paths in the minimal installation image are reproducible! 10

Debian

openlogo-100.jpg
Рис. 5.: Логотип Debian

Проект Reproducible Debian11 ставит целью достижение воспроизводимости как можно большего числа пакетов в дистрибутиве Debian. Достаточно много пакетов уже воспроизводимы:

stats_pkg_state.png

Arch Linux

archlinux-logo-dark-90dpi.ebdee92a15b3.png
Рис. 7.: Логотип Arch Linux

Дистрибутив Arch Linux также старается использовать практику воспроизводимых сборок12.

archlinux.png

1 13 2 14 15 11 12 10 16 17 4 6 7 8 5 9 3

Сноски:

1
Детерминированная компиляция: https://ru.wikipedia.org/wiki/Детерминированная_компиляция
2
Что такое хеш-функция: https://ru.wikipedia.org/wiki/Хеширование
3
Diverse Double-Compilation: https://dwheeler.com/trusting-trust/
4
Оригинал статьи «Guix: Самая совершенная операционная система»: https://ambrevar.xyz/guix-advance/index.html
5
Определение пакета в Guix: https://guix.gnu.org/manual/ru/html_node/Opisanie-paketov.html
10
Воспроизводимые сборки NixOS: https://r13y.com
11
Воспроизводимые сборки Debian: https://wiki.debian.org/ReproducibleBuilds/
12
Воспроизводимые сборки Arch Linux: https://tests.reproducible-builds.org/archlinux/archlinux.html
13
Сайт движения воспроизводимых сборок: https://reproducible-builds.org
14
Пакетный менеджер, а также свободный дистрибутив GNU/Linux с воспроизводимым ПО: https://guix.gnu.org/
15
Пакетный менеджер, а также открытый дистрибутив GNU/Linux с воспроизводимым ПО: https://nixos.org
16
Статья "Reproducible builds: a means to an end": https://guix.gnu.org/blog/2015/reproducible-builds-a-means-to-an-end/
17
Статья "Reproducible computations with Guix": https://guix.gnu.org/blog/2020/reproducible-computations-with-guix/

Emacs 26.3 (Org mode 9.1.9)

2020-05-17 Вс 21:00


Содержимое доступно по лицензии Creative Commons «Attribution-ShareAlike» («Атрибуция — На тех же условиях») 4.0 Всемирная (CC BY-SA 4.0)

Исходный код доступен по лицензии GNU General Public License Version 3

Подробнее о правах