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

Керування секретами: env проти файлів проти docker secrets

🌌 Ключар: передати ключ без звуку — Ключ, вигукнутий у команді запуску, чує кожен, хто зазирне в docker inspect чи історію команд; Ключар С.І.Д. цей трюк давно опанував і залюбки навчить: просувати ключ під двері — файлом, а не через -e, — щоб у метаданих капсули не лишилося ні звуку.

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

Чому «просто -e SECRET=...» — погана звичка

Найпростіший спосіб передати конфіденційне значення в контейнер — змінна середовища (docker run -e API_KEY=xyz). Проблема в тому, що змінні середовища контейнера зберігаються у відкритому вигляді в кількох легкодоступних місцях: docker inspect <container> показує повний список Config.Env будь-кому з доступом до Docker-сокета; файл /proc/<pid>/environ на хості читається з правами root і показує ті самі значення для процесу контейнера; якщо значення передане прямо в командному рядку — воно осідає в історії shell і в виводі docker history, якщо секрет випадково потрапив у Dockerfile через ENV чи ARG (останній до того ж може «просочитися» в проміжні шари образу, навіть якщо фінальний ENV його не встановлює). Крім того, змінні середовища часто успадковуються дочірніми процесами й потрапляють у трасування помилок чи crash-дампи, якщо застосунок їх не фільтрує явно.

Значно безпечніший підхід — монтування секрету як файлу (bind mount або tmpfs-mount у read-only режимі), а не як змінної середовища. Файл не потрапляє в docker inspect, не успадковується автоматично дочірніми процесами (застосунок сам повинен його прочитати) і може бути змонтований із правами доступу 0400, обмежуючи коло процесів, здатних його прочитати, навіть усередині самого контейнера. Багато офіційних образів (наприклад, postgres) підтримують конвенцію *_FILE (POSTGRES_PASSWORD_FILE=/run/secrets/db_password) саме для цього — замість значення передається шлях до файлу з ним.

Формальний, «промисловий» механізм — Docker secrets, доступний у Swarm-режимі (docker secret create, потім docker service create --secret). Секрет зберігається в зашифрованому вигляді в консенсус-сховищі Swarm (Raft-лог менеджерів), передається воркер-вузлам лише через TLS і монтується всередину контейнера як файл у tmpfs-каталозі /run/secrets/<ім'я> — тобто фізично ніколи не торкається постійного диска ні на хості, ні у шарах образу; після зупинки контейнера tmpfs очищується разом з усім вмістом. Це найближчий аналог рішень на кшталт HashiCorp Vault чи AWS Secrets Manager у межах штатної екосистеми Docker, хоч і прив'язаний до Swarm-оркестрації.

Для одноконтейнерного docker run (без Swarm) практичний компроміс — bind-mount файлу секрету з хоста в режимі :ro (-v ./secrets/db_password:/run/secrets/ db_password:ro), за умови, що сам файл на хості не потрапляє в систему контролю версій (додається в .gitignore) і має обмежені права доступу (chmod 400). Важливо розуміти ієрархію ризику: ENV/ARG у Dockerfile — найгірший варіант (секрет «запечений» у образ і в будь-який його публічний реєстр); -e під час docker run — краще, але видимий у метаданих контейнера; змонтований файл — прийнятно для розробки; Docker secrets/зовнішній секрет-менеджер — правильний вибір для продакшну.

📖 ОПРАЦЮВАТИ

Прочитати Docker docs «Manage sensitive data with secrets» і порівняти три способи передати секрет контейнеру: змінну середовища, змонтований файл, docker secret (Swarm-режим); звернути увагу на видимість кожного способу в docker inspect та в списку процесів.

🖥️ ТЕРМІНАЛ — СПРОБУЙ САМ
🎯 ЗАВДАННЯ
  1. Запустити контейнер із docker run -e DB_PASSWORD=supersecret ... і показати, що значення видно і в docker inspect --format '{{.Config.Env}}', і в /proc//environ на хості.
  2. Створити файл-секрет на хості (chmod 400), змонтувати його в контейнер через -v ...:/run/secrets/db_password:ro, змінити застосунок (або скрипт-обгортку) на читання значення з файлу, і показати, що docker inspect більше не містить самого значення пароля.
  3. Навмисно додати ARG DB_PASSWORD=... у проміжний етап Dockerfile, зібрати образ і знайти значення в docker history --no-trunc для проміжного шару — продемонструвати витік навіть тоді, коли фінальний ENV секрет не встановлює.
  4. Якщо доступний Swarm (docker swarm init локально), створити docker secret create db_password ./secrets/db_password.txt, підключити його до сервісу через --secret і перевірити наявність файлу /run/secrets/db_password усередині контейнера сервісу.
  5. Скласти таблицю з чотирьох способів передачі секрету (ENV у Dockerfile, -e, bind-mount файлу, docker secret) з колонками «видно в docker inspect», «потрапляє в шари образу», «переживає docker rm» і заповнити її на основі проведених експериментів.
❓ КОНТРОЛЬНІ ПИТАННЯ
  1. Чому значення, передане через -e, видно в docker inspect навіть після завершення роботи застосунку?
  2. Чим небезпечний ARG із секретним значенням у проміжному етапі багатоетапного Dockerfile, навіть якщо фінальний образ його не використовує?
  3. Яку конвенцію (*_FILE) використовують офіційні образи для передачі секретів файлом замість змінної середовища?
  4. Де фізично зберігається файл секрету, змонтований у контейнер Docker secrets, і що з ним відбувається після зупинки контейнера?
  5. Чим шифрування секретів у Raft-сховищі Swarm-менеджерів відрізняється від простого bind-mount файлу з хоста?
  6. Які права доступу варто виставити на файл секрету на хості перед bind-mount, і навіщо?
  7. Розташуйте чотири способи передачі секрету (ENV у Dockerfile, -e, bind-mount файлу, Docker secrets) за зростанням рівня безпеки та обґрунтуйте порядок.
✅ САМОПЕРЕВІРКА

Чому змінна середовища, передана через -e, менш безпечна за секрет, змонтований файлом, з погляду видимості в docker inspect і /proc/<pid>/environ?

🥚 ПАСХАЛКА
Свій перший секрет двоє творців С.І.Д. не тримали ні в -e, ні у файлі: вони вигравіювали його на дні голопроєктора — три символи C1D, які всі приймали за серійний номер. С.І.Д. вважає це найкращим уроком про секрети: найгірше сховати ключ — там, де його прочитає перший-ліпший docker inspect; найкраще — там, де його ніхто не візьме за ключ. Що відмикає 0xC1D, він поки що не каже.
⌨️ КОМАНДИ НА ЦІЙ СТОРІНЦІ 7 · 2 нових

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

docker run
Створює й запускає контейнер з образу (docker run -it ubuntu:24.04 bash — інтерактивно з терміналом; -d — у фоні; --rm — видалити після виходу; -p 8080:80 — прокинути порт).
docker
Клієнт Docker: усі дії з образами й контейнерами — через його підкоманди (docker <підкоманда> --help — довідка).
docker inspect
Друкує повний JSON-опис контейнера чи образу; -f '{{.State.Pid}}' — витягнути одне поле.
chmodнове
Змінює права доступу до файлу (chmod 400 f — читати може лише власник; chmod 000 f — нікому).
docker history
Показує шари образу: яка інструкція Dockerfile створила кожен і скільки він займає.
docker secret
Керує секретами у режимі Swarm — файли, які монтуються в контейнер у /run/secrets/, а не передаються через змінні середовища.
docker serviceнове
Керує сервісами Swarm — контейнерами, які оркестратор тримає запущеними й яким видає секрети (docker service create --secret ...).

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

← SRS05усі темиSRS07 →