Під час PHP security-оновлення я могла зупинитися на простому результаті: нові PHP-FPM, workspace і phpMyAdmin образи вже використовують виправлені версії PHP.
Але окрема перевірка показала іншу проблему: у метаданих Workspace-образів були непорожні змінні BLACKFIRE_CLIENT_ID і BLACKFIRE_CLIENT_TOKEN. Значення ми не виводили, але сам факт їхньої присутності означав, що оновлення базового PHP-образу не закриває питання секретів.
Це важливе розділення: patched runtime, старі image layers, image metadata і build cache - різні речі. Їх не можна вважати очищеними лише тому, що новий контейнер показує безпечну версію PHP.
Новий базовий образ не переписує стару історію
Docker-образ складається з шарів. Коли ми збираємо новий образ, Docker не відкриває старий образ і не видаляє з його історії те, що там було раніше.
Новий образ може використовувати новий PHP base image, але старі локальні образи, їхні шари або метадані можуть залишатися на машині, доки їх не буде окремо замінено або видалено.
Тому "PHP у новому контейнері вже patched" і "старі образи більше не містять чутливих даних" - це два різні твердження.
Спочатку перевіряємо метадані без виведення значень
Під час security-аудиту не варто друкувати весь .Config.Env, тому що там можуть бути реальні credentials.
Мені було достатньо перевірити назви змінних і факт, що значення непорожнє. Значення секрету при цьому не потрапляє в консоль.
IMAGE='your-workspace-image:tag' docker image inspect "$IMAGE" \ --format '{{json .Config.Env}}' |python3 -c 'import jsonimport sys env = json.load(sys.stdin) or [] for item in env: if "=" not in item: continue key, value = item.split("=", 1) Image metadata і історія шарів - теж окремі перевірки
Навіть якщо змінну прибрали з поточної конфігурації контейнера, це ще не доводить, що її ніколи не було в попередніх шарах або командах build.
Для локального аналізу можна переглянути Docker history. Але такий вивід теж може містити чутливі фрагменти, тому його не потрібно публікувати сирим.
IMAGE='your-workspace-image:tag' docker history "$IMAGE"Build cache - це не те саме, що старі Docker-образи
Після багатьох rebuild локальний Docker build cache у мене виріс до десятків гігабайтів. Ми окремо перевірили використання диска, а потім очистили саме кеш складання.
Перед очищенням важливо розуміти: docker builder prune працює з build cache. Це не команда "видалити всі старі images", не видалення контейнерів і не очищення volumes.
docker system dfdocker builder prune \ --all \ --force docker system dfУ моєму випадку очищення повернуло приблизно 16.33 GB build cache. Після цього кеш складання був 0B, а images, containers і local volumes залишилися на місці.
Після cleanup я повторно перевірила image pins, щоб переконатися, що очищення кешу не змінило набір потрібних PHP-образів.
Чому builder prune не вирішує проблему секретів у старих layers
Очищення build cache звільнило місце, але це не було доказом, що секрети зникли зі старих Workspace-образів.
Якщо image усе ще існує, його власні layers і metadata залишаються частиною цього image. Тому cache cleanup і security cleanup не можна вважати одним і тим самим кроком.
Для секрету потрібні два окремі рішення: clean rebuild і rotation
Якщо credential потрапив у image metadata або міг потрапити в layer history, я не вважаю достатнім просто прибрати його з нового Dockerfile.
Потрібні два окремі кроки:
- чисте повторне складання нового образу без вбудованого secret, бажано без успадкування проблемного локального image history;
- ротація credential, якщо він був реальним і міг бути доступний у старому образі.
Ротація важлива тому, що новий чистий образ не робить старе значення автоматично недійсним.
Як має виглядати майбутній clean rebuild
Конкретна команда залежить від Laradock Dockerfile, build arguments і потрібних PHP extensions, тому я не підміняю реальний rebuild універсальною командою.
Але принцип такий: новий build повинен починатися з перевіреної бази, не передавати secrets через ARG або постійний ENV, не використовувати старий проблемний image як source layer і після складання пройти повторну metadata/history перевірку.
# Generic example only.# Use the real Laradock Dockerfile, build args and service name. docker compose build \ --pull \ --no-cache \ workspace--pull змушує перевірити свіжішу базу, а --no-cache не використовує попередній build cache. Але самі ці прапорці не гарантують відсутність секретів: Dockerfile і спосіб передавання credentials усе одно потрібно перевірити.
Що потрібно перевірити після нового rebuild
Після чистого складання я б не переходила одразу до звичайного docker compose up. Спочатку потрібно перевірити новий image окремо:
- новий immutable image ID;
- відсутність небажаних credential names у
.Config.Env; - локальний review
docker history; - правильну PHP-версію і потрібні extensions;
- що effective Compose model вказує саме на новий image;
- що старі images можна видаляти лише після підтвердження rollback/retention policy.
Не плутаємо чотири різні задачі
Після цього кейсу я розділяю роботу на чотири незалежні задачі:
- Patch runtime - перейти на безпечну версію PHP;
- Pin image - гарантувати, що Compose використовує саме виправлений образ;
- Clean build - прибрати небажану історію або metadata з нового образу;
- Rotate credential - зробити старий secret недійсним, якщо він міг бути розкритий.
Виконання першого пункту не означає автоматичного виконання трьох інших.
Оновлена версія PHP доводить, що runtime patched. Вона не доводить, що старі Docker layers, metadata і credentials уже безпечні.
Підсумок
У моєму Laradock PHP runtime був успішно оновлений, образи зафіксовані в Compose, а build cache окремо очищений приблизно на 16.33 GB.
Але перевірка Workspace metadata показала окремий security follow-up: credentials були присутні в image environment metadata, а історичні layers ще потребували clean rebuild і, за потреби, rotation.
Саме тому я більше не сприймаю "новий Docker image" як завершення security-оновлення. Потрібно окремо перевірити runtime, Compose pinning, metadata, layers, cache і життєвий цикл секретів.

