Репозиторий вендора#

Репозиторий Flatpak - это каталог формата OSTree, доступный по HTTP. Отдельного серверного ПО он не требует: достаточно раздать каталог любым веб-сервером. Инструменты сборки кладут результат прямо в такой каталог, поэтому репозиторий получается побочным продуктом сборки.

Весь путь ниже показан на одном примере: домен flatpak.example.ru, репозиторий в /srv/flatpak/repo, приложение ru.example.Hello.

Ключ подписи#

Ключ создаётся один раз и переиспользуется для всех сборок. Отдельный ключ только для подписи репозитория удобнее личного: его проще передать сборочной машине и отозвать:

gpg --batch --quick-generate-key "Example Repo Signing <repo@example.ru>" \
    rsa3072 sign never

Отпечаток понадобится и при сборке, и при подготовке файла подключения:

gpg --list-secret-keys --with-colons | awk -F: '/^fpr:/{print $10; exit}'
ED26FA18B97CF57F1169B0DB653C2A83A443C168

Публичная часть выгружается в файл: он пойдёт в файл подключения и пользователям:

gpg --export ED26FA18B97CF57F1169B0DB653C2A83A443C168 > repo-key.gpg

Закрытая часть остаётся на сборочной машине. Если ключ защищён паролем, для сборки без запроса пароль кладут в файл и передают его механизмом секретов конвейера.

Сборка с подписью#

Для aft-app подпись и каталог репозитория задаются секцией export манифеста:

export:
  repository: /srv/flatpak/repo
  gpgSign: ED26FA18B97CF57F1169B0DB653C2A83A443C168
  gpgHomedir: ~/.gnupg
  stable: true
  collection-id: ru.example.Apps
  generate-static-deltas: true
  prune: false

Сборка:

sudo aft-app build ru.example.Hello.yaml

Для flatpak-builder то же самое задаётся флагами:

flatpak-builder --force-clean --repo=/srv/flatpak/repo \
    --default-branch=stable \
    --gpg-sign=ED26FA18B97CF57F1169B0DB653C2A83A443C168 \
    build-dir ru.example.Hello.yml

Оба инструмента после экспорта сами обновляют сводку репозитория. Отдельно вызывать flatpak build-update-repo нужно только тогда, когда репозиторий наполняется вручную командами flatpak build-export.

Поле generate-static-deltas включает генерацию двоичных дельт между версиями: пользователь при обновлении скачивает разницу, а не приложение целиком, но репозиторий занимает больше места. Поле prune удаляет объекты, на которые больше не ссылается ни одна ветка. Поле collection-id задаёт идентификатор набора: он записывается в репозиторий и в сборки, и клиент проверяет, что ветка пришла из того же набора, что и заявлено.

Что получилось#

После сборки в каталоге репозитория лежит примерно это:

/srv/flatpak/repo/
|-- config
|-- extensions/
|-- objects/
|-- refs/
|-- state/
|-- summaries/
|-- summary
|-- summary.idx
|-- summary.idx.sig
|-- summary.sig
`-- tmp/

Файлы summary.sig и summary.idx.sig появляются только у подписанного репозитория. Если их нет, подпись не отработала, и подключить репозиторий с проверкой не получится.

Посмотреть, что внутри:

ostree --repo=/srv/flatpak/repo refs
app/ru.example.Hello/x86_64/stable
appstream/x86_64
appstream2/x86_64

Ветки appstream собираются автоматически: из них центры приложений берут описания и иконки.

Публикация#

Каталог раздаётся веб-сервером как есть, без обработки:

server {
    listen 80;
    server_name flatpak.example.ru;
    root /srv/flatpak;
    autoindex off;
}

При такой настройке репозиторий доступен по http://flatpak.example.ru/repo/, а файл подключения ляжет рядом, в /srv/flatpak.

Проверить, что снаружи всё читается, можно обычным curl:

curl -o /dev/null -w '%{http_code}\n' http://flatpak.example.ru/repo/summary
200

Каталог обновляется сборкой напрямую, поэтому во время экспорта клиент может поймать несогласованное состояние. Если сборки идут часто, репозиторий собирают в стороне и переносят на веб-сервер готовым.

Файл подключения#

Чтобы пользователю не пришлось вводить адрес и импортировать ключ руками, рядом с репозиторием выкладывается файл .flatpakrepo. Значение GPGKey - это выгруженный публичный ключ, закодированный base64 в одну строку:

base64 -w0 repo-key.gpg

Готовый файл /srv/flatpak/example.flatpakrepo:

[Flatpak Repo]
Title=Приложения Example
Url=http://flatpak.example.ru/repo/
Homepage=https://example.ru
Comment=Репозиторий приложений Example
Description=Приложения Example для Astra Linux
GPGKey=mQGNBGqeptABDADJK/7d9J8CGpsOaT97TEjrUJsCyiWvbNlyslInjF1ZOEnDnqC...

Обязательны только Title и Url. Ключ GPGKey не обязателен формально, но без него пользователю придётся подключать репозиторий с отключённой проверкой подписи. Полный разбор полей - в блоке Отладка и подготовка репозиториев.

Значение Url заканчивается косой чертой и указывает на каталог репозитория, а не на сам файл подключения.

Проверка со стороны пользователя#

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

flatpak --user remote-add --if-not-exists --from example \
    http://flatpak.example.ru/example.flatpakrepo
flatpak --user remote-ls example
flatpak --user install -y example ru.example.Hello
flatpak run ru.example.Hello

Убрать за собой после проверки:

flatpak --user uninstall -y ru.example.Hello
flatpak --user remote-delete example

Если файла подключения нет, репозиторий подключается по адресу каталога, и тогда ключ указывается отдельно:

flatpak --user remote-add --if-not-exists example \
    http://flatpak.example.ru/repo/ --gpg-import=repo-key.gpg

Вариант без подписи существует, но годится только для проверки у себя:

flatpak --user remote-add --if-not-exists --no-gpg-verify example \
    http://flatpak.example.ru/repo/

Выпуск новой версии#

Новая версия кладётся в тот же каталог той же командой сборки. Она становится новым коммитом в той же ветке, а старые объекты остаются, пока их не удалит очистка. Пользователь получает её обычным flatpak update.

Ветка задаётся полем metadata.branch в манифесте aft-app или флагом --default-branch у flatpak-builder. Разные ветки живут в репозитории параллельно, поэтому под тестовые сборки удобно завести отдельную ветку и не трогать ту, что стоит у пользователей.

Со временем репозиторий растёт за счёт старых объектов. Удалить то, на что уже никто не ссылается:

flatpak build-update-repo --prune --prune-depth=1 \
    --gpg-sign=ED26FA18B97CF57F1169B0DB653C2A83A443C168 /srv/flatpak/repo

Значение --prune-depth задаёт, сколько прошлых версий оставить в каждой ветке. Ноль оставляет только текущую, и откатиться на предыдущую версию после этого будет нельзя.

Что проверить перед выдачей#

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

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

Разрешения песочницы сведены к минимуму. Что попало в готовую сборку, разберёт aft-analyzer: он сообщает о лишних разрешениях, битых метаданных и неразрешённых зависимостях:

aft-analyzer ru.example.Hello --report=./report

Известные уязвимости по составу ПО проверяет aft-scanner:

aft-scanner scan ru.example.Hello --fail-on=high