CI/CD
Cache GitHub Actions: jak działa i co ukrywa
Cache GitHub Actions przechowuje pliki między uruchomieniami workflow, dzięki czemu zadania pomijają pobrania, które już wykonano. Przyspiesza to buildy. Oznacza też, że pakiety trafiają do Państwa buildu bez pobierania, czego większość zapisów buildu nigdy nie widzi.
Zaktualizowano
Jak działa cache
Akcja actions/cache zapisuje zestaw ścieżek pod kluczem na końcu zadania i przywraca je na początku
późniejszego. Typowa konfiguracja npm przechowuje cache pobrań menedżera pakietów i opiera klucz na lockfile:
- uses: actions/cache@v4
with:
path: ~/.npm
key: npm-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
restore-keys: |
npm-${{ runner.os }}-
- run: npm ci
Przy przywracaniu akcja szuka najpierw dokładnego dopasowania do key, potem dopasowania częściowego, a
następnie kolejno każdego prefiksu restore-keys. Gdy prefiksowi odpowiada kilka cache, przywracany jest
ten utworzony najpóźniej. To rozwiązanie zapasowe utrzymuje szybkość buildów po zmianie lockfile, ale oznacza też, że
zadanie może wystartować z cache utworzonego dla starszego lockfile.
Często akcja ta w ogóle nie jest potrzebna. setup-node, setup-python, setup-java,
setup-go, setup-ruby i setup-dotnet mają wbudowane cache. W przypadku Node.js
setup-node przechowuje globalne dane pakietów dla npm, Yarn i pnpm, a nie node_modules, i
automatycznie włącza cache npm, gdy package.json deklaruje npm jako swój packageManager.
Zakres i limity
| Reguła | Co robi GitHub |
|---|---|
| Które cache może przywrócić uruchomienie | Z własnej gałęzi i gałęzi domyślnej; pull requesty także z gałęzi bazowej |
| Co może zapisać pull request | Tylko cache przypisane do jego merge ref, a nie do gałęzi domyślnej |
| Pamięć | Domyślnie 10 GB na repozytorium; właściciele organizacji i przedsiębiorstw mogą to zwiększyć |
| Usuwanie | Wpisy, do których nie było dostępu od ponad 7 dni, oraz najdawniej używane, gdy pamięć się zapełni |
| Sekrety | Nie należy ich zapisywać w cache: każdy z dostępem do pull requestów może odczytać zawartość cache |
Co cache ukrywa przed Państwa SBOM
Cache to skrót omijający rejestr pakietów. Na tym polega jego sens i dlatego też wprowadza w błąd każdy zapis tego, czego użył build:
- Brak pobrania, brak śladu. Pakiety przywrócone z cache są instalowane bez żądania do rejestru. Zapis oparty na ruchu sieciowym buildu pomija je, chyba że śledzi cache.
-
Stara zawartość pod nowym kluczem. Częściowe dopasowanie
restore-keysprzywraca cache utworzony dla wcześniejszego lockfile. Instalacja dodaje lub zastępuje to, co się zmieniło, ale punkt wyjścia zadania nie jest tym, co opisuje bieżący lockfile. - Więcej niż Państwa zależności. Cache przechowują też narzędzia buildu, kompilatory i pliki generowane, których nie wymienia żaden manifest aplikacji.
- Skan tego nie rozstrzygnie. SBOM oparty na lockfile opisuje to, co podaje lockfile, niezależnie od tego, czy zadanie zainstalowało właśnie to ze swojego cache.
Jak wpisuje się to w szerszy problem, wyjaśniają strony dlaczego lockfile nie jest zapisem tego, co dostarczono oraz zależności przechodnie.
Zatruwanie cache
Cache są współdzielone przez workflow w repozytorium, co czyni z nich ścieżkę między nimi. W maju 2024 badacz
bezpieczeństwa Adnan Khan pokazał, że każdy, kto może wykonać kod w kontekście gałęzi domyślnej, przez podatny workflow
pull_request_target lub przejętą zależność, może nadpisywać pliki w workflow przywracających cache. Mając
token cache z działającego zadania, napastnik może zapełnić cache repozytorium tak, aby GitHub usunął prawidłowe wpisy,
a następnie zapisać zatrute wpisy pod tymi samymi kluczami. Workflow wydania, który je przywróci, uruchomi pliki
napastnika z własnymi sekretami.
Aby ograniczyć ryzyko:
- Budować wydania od czystego stanu lub z cache, do których zapisuje tylko workflow wydania.
- Nigdy nie uruchamiać niezaufanego kodu z pull requestów w kontekście gałęzi domyślnej.
- Trzymać sekrety i tokeny poza ścieżkami zapisywanymi w cache.
- Traktować to, co przywrócił cache, jako wejście buildu wymagające takiej samej uwagi jak pobranie.
Jak CRACI obsługuje cache
CRACI to runner GitHub Actions, więc actions/cache i cache setup-* działają tak jak dziś,
obok cache warstw kontenerów. Cache to też jeden z powodów, dla których CRACI uruchamia buildy około dwa razy szybciej
niż runnery hostowane przez GitHub, obok szybszego sprzętu i krótszych kolejek.
Zmienia się zapis. Proxy rozpoznające pakiety w CRACI rejestruje pakiety pobierane przez zadanie, a dowody przenoszone są razem z cache: gdy zadanie przywraca cache, CRACI przenosi znane sobie zależności do SBOM tego zadania.
- Kompletność dla każdego cache. Każdy cache ma stan kompletności, tak jak każde zadanie. Zadanie, które przywraca niekompletny cache, otrzymuje niekompletny SBOM, a tworzony przez nie cache przenosi ten status dalej, więc luka nigdy nie zamienia się po cichu w kompletny zapis.
- Bez zgadywania z archiwum. CRACI nie odczytuje manifestów pakietów wewnątrz archiwum cache. Wie, co znajduje się w cache, ponieważ zarejestrował zadanie, które go wytworzyło.
-
Jeden cache na zadanie, które go wytwarza, utrzymuje wiarygodność tych dowodów, razem z polityką
ruchu wychodzącego ustawioną na
default: denyw tym zadaniu. Szczegóły zawiera przewodnik po kompletności cache i SBOM.
Jak działa rejestrowanie, opisuje strona SBOM-y z czasu buildu. Można też umówić demo, aby uruchomić na CRACI jeden z Państwa workflow korzystających z cache.
Cache GitHub Actions: najczęściej zadawane pytania
Jak długo GitHub przechowuje cache Actions?
GitHub usuwa wpisy cache, do których nie było dostępu od ponad 7 dni. Gdy repozytorium przekroczy limit pamięci, domyślnie 10 GB, usuwa też cache według ostatniego dostępu, zaczynając od najstarszych.
Czy setup-node zapisuje node_modules w cache?
Nie. Parametr cache w actions/setup-node przechowuje globalne dane pakietów menedżera pakietów dla npm, Yarn lub pnpm, a nie node_modules. Krok instalacji nadal się wykonuje, ale odczytuje pakiety z przywróconego cache, zamiast je pobierać.
Czy pull request może odczytać cache z gałęzi main?
Tak. Uruchomienie workflow może przywracać cache z własnej gałęzi i gałęzi domyślnej, a pull request także z gałęzi bazowej. Cache utworzone w pull requeście są przypisane do jego merge ref, więc nie mogą trafić do zakresu gałęzi domyślnej.
Czy używanie cache Actions w workflow wydań jest bezpieczne?
To kompromis. Badania opublikowane w 2024 pokazały, że każdy, kto może uruchomić kod w workflow gałęzi domyślnej, może zatruć cache przywracane przez inne workflow, w tym workflow wydań. Workflow publikujące wydania lub mające dostęp do sekretów wdrożeniowych bezpieczniej budować od czystego stanu lub z cache, do których zapisują tylko one.
Czy przywrócony cache zmienia mój SBOM?
Może. Pakiety przywrócone z cache są instalowane bez pobierania, więc zapis oparty na ruchu sieciowym pomija je, chyba że śledzi cache. A skan lockfile nie mówi, czy cache zawierał dokładnie te wersje.
Zachować cache i kompletny SBOM
Proszę uruchomić swój workflow na CRACI: cache nadal przyspieszają build, a zależności, które przenoszą, pozostają w zarejestrowanym SBOM każdego zadania.
Umów demo