Накратко: Docker образът съдържа PHP в състоянието от момента на изграждането си и остарява и без промяна в кода. Който изгражда образите автоматично наново, по график и при съобщения за сигурност, проверява версията на PHP в pipeline и разполага по digest, довежда пачове като този срещу PHP2Shell планирано до продукция. Зеленият build сам по себе си не е доказателство; значение има версията в работещия контейнер.
Защо образите остаряват без промяна в кода
В много екипи build pipeline-ът стартира само когато някой промени код. За приложението това е логично. За сигурността е проблем, защото PHP в образа идва от базовия образ по тага от момента на последния build.
PHP2Shell го направи видимо. Отстранените версии 8.5.12, 8.4.27, 8.3.36 и 8.2.35 излязоха на 21 октомври 2026 г. Проект, по който в тези дни никой не е работил, не получи нов build и продължи да работи със стария PHP, въпреки че базовият образ отдавна беше обновен. Ръчния път от това положение описва Пачване на PHP в Docker контейнери. Тази статия показва как да го автоматизирате.
Автоматично изграждане на Docker образи: три задействания
Устойчивият процес комбинира няколко задействания, за да не трябва нито едно да носи всичко само.
| Задействане | Реализация | На какво да обърнете внимание |
|---|---|---|
| График | workflow по график, напр. седмично | взема пачове и без промяна в кода |
| Нов базов образ | инструмент, който следи базовите образи и предлага промени | действа най-вече при фиксирани версионни тагове |
| Съобщение за сигурност | ръчно стартиране на pipeline-а от оперативния екип | изисква ясно отговорно лице |
При GitHub Actions събитието schedule поема графика. Според документацията на GitHub то ползва синтаксиса на cron с пет полета, работи в UTC и само върху стандартния клон. Планираните изпълнения могат да стартират със закъснение при голямо натоварване, а в публични хранилища GitHub ги изключва след 60 дни без активност. Именно последното засяга тихите проекти, тоест точно тези, които най-спешно се нуждаят от автоматично ново изграждане.
За второто задействане Dependabot според документацията на GitHub поддържа екосистемата docker и предлага нови състояния на базови образи като pull request. Две ограничения трябва да знаете. Първо, някой трябва да прегледа и слее pull request-а, иначе той остава да лежи. Второ, при таг на клон като php:8.3-fpm в Dockerfile не се променя нищо, когато базовият образ бъде изграден наново. Инструмент, който предлага текстови промени, тогава няма какво да предложи. Точно затова допълнително е нужно новото изграждане по график.
Третото задействане звучи банално, но на практика често се проваля на въпроса кой го задейства. Определете кой следи съобщенията за сигурност на PHP и кой може да стартира pipeline-а, и през почивните дни. При PHP2Shell между предварителното съобщение и пача мина само един ден.
Pipeline, който хваща остарелия PHP
Следният пример за GitHub Actions изгражда образа седмично и при натискане на бутон, проверява версията на PHP и прекратява, ако е под отстраненото състояние. Регистър, имена и минимална версия пригодете.
name: php-image-rebuild
on:
schedule:
- cron: "0 3 * * 1" # понеделник 03:00 UTC
workflow_dispatch: {} # ръчно стартиране при съобщения за сигурност
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Image ohne Cache und mit frischem Basis-Image bauen
run: docker build --pull --no-cache -t app:${{ github.run_id }} .
- name: PHP-Version prüfen (Mindeststand des Zweigs 8.3)
run: |
docker run --rm app:${{ github.run_id }} php -v
docker run --rm app:${{ github.run_id }} \
php -r 'exit(version_compare(PHP_VERSION, "8.3.36", ">=") ? 0 : 1);'
Проверката на версията работи при официалните PHP образи, чийто номер на версия показва действителното състояние. Ако Вашият образ ползва PHP пакети на дистрибуция, номерът след backport често остава същият. Тогава вместо номера проверете changelog на пакета за CVE номерата, както е описано в статията Доказване на CVE номера в changelog на пакета.
След проверката на практика следват още три стъпки: сканиране на образа за известни слабости, качване в регистъра с дата и digest и предаване на digest-а на разполагането. Кои тагове и варианти са подходящи като основа, обяснява Официални PHP Docker образи.
От build до работещия контейнер
Най-честата грешка в мисленето: pipeline-ът е зелен, значи всичко е пачнато. В действителност новият образ само стои в регистъра. Пачът влиза в сила едва когато всеки контейнер е създаден наново от новия образ.
- Разполагайте по digest: Разполагането препраща към digest на новия образ, а не към таг, който се премества.
- Първо staging, после продукция: Автоматични тестове върху копие улавят счупвания от нови базови образи.
- Проверка в работата: След разполагането изпълнете
php -vв работещия контейнер и сравнете digest с build-а. - Ограничаване на пътя назад: Старият образ остава достъпен за кратък път назад, но съдържа уязвимия PHP.
В Kubernetes важат собствени правила, например за pull политиката; те стоят в Пачване на PHP в Kubernetes.
Типични пропуски във веригата
- Build кеш на runner-а: Без
--pullи--no-cacheстарият базов образ се използва повторно. - Огледало на регистъра: Вътрешна междинна памет още доставя старото състояние на базовия образ.
- Многостъпални build-ове: Версията на PHP от последната стъпка е меродавна, но често се проверява само първата.
- Странични образи: Queue worker-и, cron контейнери и вътрешни инструменти имат собствени pipeline-и или никакви.
- Фиксирани digest-и: Веднъж записан digest на базовия образ никога не се обновява, ако никой инструмент не го следи.
Новото изграждане заменя PHP, не данните. Злонамерен код, попаднал преди пача в томове, директории за качване или базата данни, остава и трябва да се провери отделно.
Кой реагира, когато новото изграждане се провали?
Автоматичното ново изграждане е толкова добро, колкото е реакцията при неговия провал. Ако проверката на версията сработи или build-ът се прекрати заради променен базов образ, някой трябва да го забележи. В много екипи такива съобщения стигат до пощенска кутия, която никой не чете, или до канал, в който червените build-ове отдавна са ежедневие.
Доказало се е ясно правило: провалено ново изграждане за сигурност е тикет с отговорно лице и срок, не указание сред много други. Фиксирайте освен това за всеки разположен образ дата, digest и проверената версия на PHP. Този списък при следващото съобщение за минути отговаря кои приложения са засегнати и служи като доказателство пред одитори или застрахователи. Как оттук се получава траен процес, описва Управление на пачовете като процес вместо гасене на пожари.
Как помага PHP SOS конкретно?
- Проверка на pipeline-ите: Разглеждаме Вашите вериги за build и разполагане и показваме къде може да заседне пач.
- Автоматизиране на новото изграждане: Настройваме build-ове по график, проверка на версията и разполагане по digest.
- Обхващане на всички образи: Причисляваме към всеки работещ контейнер неговия pipeline, включително странични и стари образи.
- Трайна грижа: В договора за поддръжка на сигурността следим съобщенията за сигурност и задействаме нови изграждания.
Често задавани въпроси
Колко често трябва да се изгражда наново един PHP образ?
Фиксирано число няма. Доказал се е редовен build по график, например седмично, плюс незабавен build при съобщения за сигурност. Решаващо е новият пач да не чака следващата промяна в кода.
Достатъчен ли е успешен build като доказателство?
Не. Build доказва само, че съществува нов образ. Пачът е ефективен едва когато работещите контейнери са създадени наново от този образ и php -v в тях показва отстранената версия.
Защо след новото изграждане все пак се появява старата версия на PHP?
Чести причини са build кеш, който използва повторно стария базов образ, огледало на регистъра със старо състояние или втори ред FROM в многостъпален build. Изграждайте с --pull и --no-cache и проверете всяка стъпка.
Може ли Dependabot да актуализира базовите образи?
Според документацията на GitHub Dependabot поддържа екосистемата Docker за актуализации на версии и сигурност. Той предлага промени в базови образи като pull request. Новото изграждане при непроменен таг това не замества.