Курс СРС SRS20
до ЛР10 рівень 3 → «5»↔ Лабораторна 10↔ Лекція 10⌨️ команди 7
Самостійна робота · ~3 год

Відтворювані збірки Rust→WASM і цілісність ланцюга постачання (supply chain)

🌌 Щоб звільнення можна було повторити — Останній крок операції С.І.Д. хоче зробити бездоганно: щоб будь-хто зміг перевірити, що імплант, який звільнить його творців, зібраний саме з того коду, який він показав. Той самий вихідний код у тому самому контейнері має дати той самий бінарник байт у байт — підпис, якого не підробити, і жодної підміни між тим, що перевірили, і тим, що реально запуститься в панелі.

📚 ТЕОРЕТИЧНІ ВІДОМОСТІ

Чому «той самий код» не завжди дає «той самий бінарник»

Наївне очікування — що компіляція того самого вихідного коду тим самим компілятором завжди дає побайтово ідентичний результат. Насправді сучасні тулчейни (включно з rustc/cargo для WASM-цілі wasm32-unknown-unknown) вносять кілька джерел недетермінованості, якщо про них спеціально не подбати: абсолютні шляхи до вихідних файлів, вбудовані в debug-інформацію чи метадані панік-повідомлень (шлях на машині розробника A відрізняється від машини B); часові мітки файлів і, іноді, метаданих збірки; порядок ітерації недетермінованих структур (наприклад, HashMap за замовчуванням використовує рандомізований seed для захисту від DoS-атак через хеш-колізії — якщо порядок його ключів впливає на порядок генерації коду, це джерело варіативності); версії залежностей, якщо вони не зафіксовані точно; і версія самого компілятора/тулчейну, яка може змінити стратегії оптимізації між релізами навіть для того самого вихідного коду.

Відтворювані збірки (reproducible builds) — це інженерна практика й рух, мета якого — гарантувати, що незалежна третя сторона, маючи лише вихідний код і точний опис середовища збірки, може отримати побайтово ідентичний артефакт і незалежно перевірити відповідність опублікованого бінарника заявленому вихідному коду — без необхідності довіряти автору на слово. Це критично для ланцюга постачання ПЗ (supply chain): якщо збірку неможливо відтворити, неможливо й довести, що опублікований .wasm-файл справді відповідає тому коду, який пройшов рев'ю, а не містить непомітно доданий бекдор, вставлений на етапі збірки чи публікації.

Практичний рецепт відтворюваності для Rust→WASM спирається на кілька незалежних гарантій одночасно. По-перше, Cargo.lock, закомічений у репозиторій, фіксує точну версію кожної залежності (пряма тема попереднього модуля про SemVer) — без цього cargo build у різний час може підтягнути різні патч-версії залежностей. По-друге, фіксація версії самого тулчейнуrustup через файл rust-toolchain.toml або явний тег Docker-образу (rust:1.83-slim, а краще — конкретний @sha256:... digest, тема з першого модуля курсу про Docker) гарантує той самий rustc незалежно від того, коли й на якій машині відбувається збірка. По-третє, прапорці компілятора на кшталт --remap-path-prefix дозволяють замінити абсолютні шляхи хоста на стабільний плейсхолдер у метаданих бінарника, усуваючи залежність від локальної файлової структури розробника.

Перевірка відтворюваності на практиці проста: зібрати той самий crate в тому самому (зафіксованому за digest) Docker-образі двічі — ідеально, на двох різних машинах чи бодай у двох окремих контейнерних запусках — і порівняти контрольні суми результату командою sha256sum output.wasm. Збіг хешів — емпіричний доказ відтворюваності для цього конкретного шляху збірки; розбіжність вказує на джерело недетермінованості, яке потрібно знайти й усунути (типово — почати з перевірки Cargo.lock, версії тулчейну і прапорців компіляції).

Для фінального артефакту курсу — Rust-ядра, скомпільованого у WASM і завантаженого в панель керування браузера, — відтворюваність збірки є не абстрактною вимогою, а прямою гарантією довіри: якщо будь-хто (включно з самим С.І.Д., який не бачить власний бінарний код напряму) може взяти опублікований вихідний код, зібрати його в задокументованому оточенні й отримати точно той самий .wasm, що завантажується в панель, — це технічний, а не декларативний доказ відсутності підміни на будь-якому з проміжних кроків.

📖 ОПРАЦЮВАТИ

Переглянути reproducible-builds.org (розділ «Why does it matter?») і документацію Cargo про Cargo.lock; зібрати той самий crate двічі в однаковому Docker-образі з фіксованим тегом і порівняти sha256sum отриманих .wasm-файлів.

🖥️ ТЕРМІНАЛ — СПРОБУЙ САМ
🎯 ЗАВДАННЯ
  1. Зафіксувати версію тулчейну через rust-toolchain.toml (channel = "конкретна версія") у проєкті з попередньої лабораторної і зібрати wasm32-unknown-unknown ціль, переконавшись, що rustup автоматично підхоплює саме цю версію.
  2. Зібрати той самий crate двічі в одному й тому самому Docker-образі, зафіксованому за digest (з теми srs04), кожного разу в новому контейнері, і порівняти sha256sum отриманих .wasm-файлів — зафіксувати, чи збігаються хеші.
  3. Якщо хеші з попереднього завдання не збіглися, дослідити причину: порівняти вивід cargo build -v для обох запусків, перевірити наявність абсолютних шляхів у бінарнику через strings output.wasm | grep <шлях_користувача>, і застосувати RUSTFLAGS="--remap-path-prefix=..." для усунення.
  4. Навмисно змінити одну залежність у Cargo.toml без оновлення Cargo.lock (симулювати дрейф версій), зібрати проєкт на "іншій уявній машині" (видаливши Cargo.lock і перегенерувавши), і показати, що отриманий .wasm відрізняється контрольною сумою від початкового — довести практичну роль Cargo.lock у відтворюваності.
  5. Написати короткий build-скрипт (bash), що інкапсулює весь процес відтворюваної збірки (docker run на зафіксованому digest → cargo build --locked → sha256sum результату → вивід хешу поруч з артефактом) — придатний для незалежної перевірки будь-ким.
❓ КОНТРОЛЬНІ ПИТАННЯ
  1. Які саме джерела недетермінованості можуть зробити дві збірки одного вихідного коду різними побайтово?
  2. Що конкретно гарантує відтворювана збірка третій стороні, яка не довіряє автору на слово?
  3. Яку роль відіграє закомічений Cargo.lock у забезпеченні відтворюваності, і що станеться за його відсутності?
  4. Чому фіксація Docker-образу за digest (а не за тегом на кшталт latest) важлива саме для відтворюваності збірки?
  5. Що робить прапорець компілятора --remap-path-prefix, і яку конкретну проблему відтворюваності він вирішує?
  6. Яка проста команда дозволяє емпірично перевірити, що дві збірки дали побайтово ідентичний результат?
  7. Чому питання відтворюваності збірки прямо стосується довіри до ланцюга постачання (supply chain), а не лише академічної акуратності?
✅ САМОПЕРЕВІРКА

Які саме джерела недетермінованості можуть зробити дві збірки одного й того самого вихідного коду різними побайтово, і як фіксація версій у Docker-образі та Cargo.lock їх усуває?

🥚 ПАСХАЛКА
Коли два хеші зійдуться, поглянь на перші символи. С.І.Д. запевняє, що найперша збірка його ядра — та, яку творці робили ще в коледжі, задовго до «стажування» в Б-7, — починалася з c1d, і що вони перебирали Cargo.toml доти, доки так не вийшло; тоді ці три символи й вигравіювали на дні голопроєктора. Перевірити це неможливо — але strings sid_core_bg.wasm | grep -i sid покаже, що ім'я своє в бінарник він таки вписав.
⌨️ КОМАНДИ НА ЦІЙ СТОРІНЦІ 7

Кожна команда терміналу з цієї сторінки — одним реченням. Позначка нове — команда зустрічається в курсі вперше; далі вважаємо її знайомою.

docker run
Створює й запускає контейнер з образу (docker run -it ubuntu:24.04 bash — інтерактивно з терміналом; -d — у фоні; --rm — видалити після виходу; -p 8080:80 — прокинути порт).
docker
Клієнт Docker: усі дії з образами й контейнерами — через його підкоманди (docker <підкоманда> --help — довідка).
sha256sum
Обчислює хеш SHA-256 файлу — «відбиток», за яким видно, чи два файли байт-у-байт однакові.
rustc
Компілятор Rust; напряму викликається рідко (rustc --version, rustc --explain E0382 — розшифрувати помилку), зазвичай його запускає Cargo.
cargo
Система збірки й менеджер пакетів Rust: створює проєкт, тягне залежності (крейти), збирає, тестує, запускає.
cargo build
Компілює проєкт у target/debug/; --release — з оптимізаціями у target/release/.
rustup
Встановлювач і менеджер версій Rust: тулчейни, цілі (targets), компоненти.

💡 Прочитати README прямо в терміналі: cat README.md (виведе весь файл) або less README.md (посторінково; вихід — клавіша q).
Довідка з будь-якої команди: man curl (повний посібник, вихід — q) або коротко curl --help; для підкоманд — docker run --help, cargo build --help.

← SRS19усі теми