Пошук уроків, статей та іншого контенту
Передавайте паролі, токени й ключі через Docker Secrets, не зберігаючи їх у образах або відкритій конфігурації.
ENVПаролі, токени, приватні ключі та інші конфіденційні дані не повинні потрапляти до:
Dockerfile через ENV або ARG;
файлів образу;
відкритих змінних середовища в конфігурації;
репозиторію;
журналів контейнера;
команд, які зберігаються в історії shell.
Наприклад, такий Dockerfile небезпечний:
FROM node:22-alpine
ENV DATABASE_PASSWORD=super-secret-passwordЗначення може бути доступним через метадані образу або під час запуску контейнера. Навіть якщо пізніше видалити змінну в наступному шарі, секрет може залишитися в попередньому шарі образу.
Docker Secrets передають секрет як файл, змонтований у контейнер. За замовчуванням файл доступний за шляхом:
/run/secrets/<назва-секрету>Секрет не додається до образу та не зберігається у відкритому вигляді в декларації сервісу.
Класичний Docker Secrets призначений для сервісів Docker Swarm:
секрет створюється в Swarm;
доступ до нього отримують лише вибрані сервіси;
секрет монтується у файлову систему контейнера;
значення секрету не повертається командою docker secret inspect;
секрет не входить до образу;
після видалення або зупинки задачі файл секрету видаляється з контейнера.
Спочатку потрібно ініціалізувати Swarm на менеджері:
docker swarm initЯкщо вузол уже належить Swarm, повторно виконувати цю команду не потрібно.
Не передавайте конфіденційні значення безпосередньо в аргументах командного рядка:
docker secret create db_password super-secret-passwordТаке значення може залишитися в історії shell або бути видимим у списку процесів.
Краще передати значення через стандартний ввід:
printf '%s' 'super-secret-password' | docker secret create db_password -Крапка або дефіс наприкінці означає, що Docker має прочитати значення зі стандартного вводу.
Перевірити наявність секрету можна так:
docker secret lsПерегляд метаданих:
docker secret inspect db_passwordКоманда покаже ім’я, ідентифікатор і службову інформацію, але не саме значення секрету.
Розглянемо PostgreSQL. Офіційний образ PostgreSQL підтримує змінну POSTGRES_PASSWORD_FILE. У ній потрібно вказати шлях до файлу, а не саме значення пароля.
Створимо сервіс:
docker service create \
--name database \
--secret source=db_password,target=db_password \
--env POSTGRES_DB=app \
--env POSTGRES_USER=app \
--env POSTGRES_PASSWORD_FILE=/run/secrets/db_password \
postgres:16Параметр:
--secret source=db_password,target=db_passwordозначає:
source=db_password — ім’я секрету в Docker;
target=db_password — ім’я файлу всередині контейнера.
У результаті контейнер отримує файл:
/run/secrets/db_passwordPostgreSQL читає пароль із цього файлу через:
POSTGRES_PASSWORD_FILE=/run/secrets/db_passwordЗначення пароля не потрібно записувати в environment.
Перевірити стан сервісу:
docker service ps databaseПеревірити конфігурацію сервісу:
docker service inspect databaseУ конфігурації буде видно, що сервіс використовує секрет, але саме значення пароля там не відображається.
Для розгортання кількох сервісів зручно використовувати Docker Stack. Створімо файл stack.yml:
services:
database:
image: postgres:16
environment:
POSTGRES_DB: app
POSTGRES_USER: app
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- source: db_password
target: db_password
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
secrets:
db_password:
external: trueСекція:
secrets:
db_password:
external: trueозначає, що секрет уже має існувати в Docker Swarm. Stack не створюватиме його з файлу або з текстового значення.
Створимо секрет:
printf '%s' 'super-secret-password' | docker secret create db_password -Розгорнемо стек:
docker stack deploy -c stack.yml applicationПеревіримо сервіси:
docker stack services applicationПеревіримо задачі:
docker stack ps applicationВидалити стек можна так:
docker stack rm applicationВидалити секрет:
docker secret rm db_passwordСекрет не можна видалити, поки його використовує сервіс. Спочатку потрібно видалити або оновити цей сервіс.
Застосунок повинен читати секрет із файлу під час запуску. Наприклад, у Node.js:
import { readFileSync } from "node:fs";
const secretPath =
process.env.DATABASE_PASSWORD_FILE || "/run/secrets/db_password";
const databasePassword = readFileSync(secretPath, "utf8").trim();
if (!databasePassword) {
throw new Error("Пароль бази даних порожній");
}
console.log("Секрет успішно прочитано");У цьому прикладі пароль:
не записаний у коді;
не передається через DATABASE_PASSWORD;
читається з файлу, змонтованого Docker;
не виводиться в журнал.
Не слід робити так:
console.log(databasePassword);Секрет може потрапити до логів, системи збору помилок або журналів CI/CD.
Docker монтує секрет як файл у контейнері. Типовий шлях:
/run/secrets/<name>Наприклад:
/run/secrets/db_passwordЯкщо застосунок працює не від імені root, потрібно перевірити, чи може його користувач прочитати файл. Для сервісу можна явно вказати параметри секрету:
services:
api:
image: example/api:1.0
secrets:
- source: api_token
target: api_token
uid: "1000"
gid: "1000"
mode: 0400
secrets:
api_token:
external: trueТут:
uid: "1000" — власник файлу;
gid: "1000" — група файлу;
mode: 0400 — читати може лише власник.
Значення uid і gid мають відповідати користувачу, від імені якого запускається застосунок.
Секрети призначені для виконання контейнера, а не для docker build.
Небезпечний варіант:
FROM alpine:3.20
ARG NPM_TOKEN
RUN npm config set //registry.example.com/:_authToken="$NPM_TOKEN"Токен може потрапити до шарів образу або журналів складання.
Правильний підхід:
зібрати образ без конфіденційних значень;
створити секрет у середовищі розгортання;
передати секрет сервісу під час запуску;
прочитати його з /run/secrets/....
Образ має містити код і публічну конфігурацію, але не конкретні паролі або ключі певного середовища.
Секрет у Docker Swarm не редагується на місці. Для ротації потрібно:
створити новий секрет з іншою назвою;
оновити сервіс;
переконатися, що нові задачі використовують новий секрет;
видалити старий секрет після завершення переходу.
Приклад:
printf '%s' 'new-secret-password' | docker secret create db_password_v2 -Оновлення сервісу:
docker service update \
--secret-rm db_password \
--secret-add source=db_password_v2,target=db_password \
databaseПісля цього Docker перезапустить задачі сервісу. Застосунок має прочитати новий файл під час запуску.
Перевірити оновлення:
docker service ps databaseПісля завершення ротації старий секрет можна видалити:
docker secret rm db_passwordРотація пароля в самому сервісі та заміна Docker Secret — не завжди одна й та сама операція. Наприклад, PostgreSQL може продовжувати використовувати старий пароль для вже створеного користувача. Тому ротацію потрібно узгоджувати з механізмом зміни облікових даних конкретної системи.
Формат Compose також має секцію secrets, але поведінка залежить від способу запуску.
У Docker Compose без Swarm секрет часто описують через локальний файл:
services:
api:
image: example/api:1.0
secrets:
- api_token
secrets:
api_token:
file: ./secrets/api_token.txtУ такому випадку Compose бере файл із хоста та монтує його в контейнер. Це може бути корисніше за змінні середовища, але це не те саме, що централізоване керування секретами Docker Swarm:
файл потрібно захистити на хості;
його потрібно виключити з Git;
доступ до нього має користувач, який запускає Docker;
відповідальність за зберігання та ротацію лежить на операторі.
Для Swarm використовуйте зовнішній секрет:
secrets:
api_token:
external: trueА потім створюйте його командою docker secret create.
Не змішуйте ці два сценарії: локальний Compose-файл із file: і Swarm Secret мають різну модель зберігання та керування.
Для кожного секрету варто дотримуватися такої схеми:
Зберігати секрет поза репозиторієм.
Створювати його в цільовому середовищі розгортання.
Передавати його лише сервісам, яким він потрібен.
Читати значення з файлу /run/secrets/....
Не копіювати його у змінні середовища без необхідності.
Не виводити значення в логи.
Використовувати версійовані імена для ротації.
Видаляти старі секрети після переходу на нові.
Обмежувати доступ до Docker Engine і вузлів Swarm.
Не перевіряти секрет у репозиторій разом із конфігурацією.
ENVenvironment:
DATABASE_PASSWORD: super-secret-passwordЗмінна середовища може бути доступною через інструменти діагностики, метадані контейнера або журнали. Для секретів використовуйте файл.
COPY .env /app/.envФайл потрапляє до образу та може залишитися в його шарах. Образ потрібно збирати без секретів.
console.log("Token:", token);Навіть тимчасовий діагностичний вивід може залишитися в централізованій системі логування.
docker secret create api_token "token-value"Значення може залишитися в історії shell. Використовуйте стандартний ввід:
printf '%s' "$TOKEN_VALUE" | docker secret create api_token -Якщо значення вже зберігається у змінній середовища, не виводьте цю змінну та захищайте середовище, у якому запускається команда.
Секрет потрібно додавати лише до тих сервісів, яким він необхідний:
services:
api:
secrets:
- api_token
frontend:
image: example/frontend:1.0У цьому прикладі frontend не отримує api_token.
Якщо застосунок очікує:
DATABASE_PASSWORDа Docker Secret змонтований як:
/run/secrets/db_passwordзастосунок не прочитає секрет автоматично. Потрібно або налаштувати параметр на кшталт DATABASE_PASSWORD_FILE, або явно прочитати файл у коді.
Docker Secrets передають конфіденційні дані у вигляді файлів, а не змінних середовища.
У Docker Swarm секрет доступний лише сервісам, яким його явно надали.
Типовий шлях до секрету — /run/secrets/<ім’я>.
Секрети не повинні потрапляти до Dockerfile, образів, репозиторію або логів.
Значення створюйте через стандартний ввід, щоб не залишати його в історії команд.
Для ротації створюйте новий секрет і оновлюйте сервіс, оскільки секрети не редагуються на місці.
Локальні Compose-секрети через file: — це монтування файлу з хоста, а не повноцінне керування секретами Swarm.