Jeden wyciekły token z joba testowego potrafi otworzyć rejestr obrazów, stan Terraform i środowisko produkcyjne naraz. Zanim ktokolwiek dopisze kolejny krok do workflow, trzeba wiedzieć, która tożsamość go uruchamia, jaki ma zasięg i czy kod z pull requesta w ogóle powinien ten sekret zobaczyć. Poniższa procedura idzie od granic zaufania, przez tokeny i uprawnienia, aż do zależności, artefaktów i reakcji na incydent.
Punkt startowy: co chroni pipeline i gdzie przebiegają granice zaufania
Pipeline CI/CD nie jest tylko automatem do testów. W praktyce trzyma klucze do repozytorium, rejestru kontenerów, menedżera sekretów, chmury i środowisk wdrożeniowych. Job, który „tylko buduje”, często ma ten sam runner, ten sam token dostawcy i tę samą sieć co job, który publikuje obraz albo stosuje plan infrastruktury.
Najczęstsze ścieżki nadużyć nie wymagają egzotycznego ataku. Wystarczy złośliwa zmiana w pull requeście z forka, przejęte konto z prawem zapisu, podatna zależność wciągnięta przez automatyczną aktualizację, nieaktualny self-hosted runner albo token wypisany do logu. Każda z tych ścieżek kończy się tym samym pytaniem: czyja tożsamość właśnie wykonała polecenie i co jeszcze może zrobić?
CI, CD i maszyna, na której leci zadanie, to trzy różne strefy zaufania. Testy na kodzie z niezatwierdzonej gałęzi nie powinny dziedziczyć poświadczeń wdrożenia. Publikacja pakietu nie powinna używać konta, które może zmieniać polityki IAM. Ten sam token albo ten sam runner „dla wygody” dla wszystkich etapów jest właśnie miejscem, w którym mały błąd staje się pełnym przejęciem łańcucha dostaw.
Kolejność poprawek ma znaczenie. Najpierw odetnij możliwość przejęcia tożsamości: krótki czas życia poświadczeń, brak stałych kluczy w repozytorium, separacja runnerów. Potem zmniejsz zasięg: najmniej uprawnień, sekrety tylko dla zaufanych gałęzi i środowisk. Na końcu dodaj wykrywalność: logi bez sekretów, podpis artefaktu, alert na nietypowe publikacje. Skaner zależności na końcu kolejki nie naprawi tokenu, który już może wdrażać na produkcję. [1]
Tożsamość i tokeny: ogranicz dostęp, zanim zadanie wykona pierwszą komendę
1. Zmapuj tożsamości oraz granice zaufania w każdym etapie pipeline’u
Zacznij od inwentarza, nie od kolejnej reguły w YAML. Wypisz każdą tożsamość, która może coś zrobić w pipeline: konta ludzi, konta serwisowe, tokeny repozytorium, tokeny chmurowe, deploy keys, boty zależności, integracje czatu i samych runnerów. Osobno zaznacz token domyślny platformy, na przykład GITHUB_TOKEN albo token joba w GitLab CI, bo często żyje obok „prawdziwych” sekretów i bywa szerszy, niż zakłada autor workflow. [4]
Każdej tożsamości przypisz jeden cel. Odczyt kodu, publikacja pakietu, wdrożenie, plan Terraform i administracja infrastrukturą to różne role. Jeśli jedno konto serwisowe robi wszystkie pięć, granica zaufania nie istnieje: przejęcie joba testowego jest równoznaczne z przejęciem produkcji. W tabeli inwentarza warto mieć kolumny: właściciel, system, zakres, środowiska, data rotacji, sposób unieważnienia.
Osobno oznacz, skąd pochodzi kod. Job na gałęzi release, job na branchu funkcjonalnym i job na forkach to trzy poziomy ryzyka, nawet jeśli workflow ma tę samą nazwę. Kod z forka może zmienić krok, który czyta sekret, wypisuje go albo wysyła na zewnątrz. Dlatego sama nazwa workflow nie jest granicą zaufania. Granicą jest kombinacja: kto wyzwolił zdarzenie, z jakiego repozytorium, z jakiego refa i na jakim runnerze.
Kryterium zaliczenia tego kroku jest proste. Dla każdego sekretu i tokenu zespół potrafi wskazać właściciela, zakres, termin rotacji i procedurę unieważnienia bez przeszukiwania czatu. Jeśli odpowiedź brzmi „to stary klucz, nikt go nie rusza”, ten klucz jest pierwszym kandydatem do usunięcia albo zamiany na tożsamość federowaną.
2. Preferuj tokeny krótkotrwałe i federację OIDC zamiast stałych kluczy
Stały klucz API wygrywa tylko prostotą wdrożenia. Wystarczy zmienna w ustawieniach projektu. Przegrywa przy rotacji, przy odejściu osoby, która go utworzyła, i przy każdym wycieku: dopóki ktoś go nie unieważni, działa poza kontekstem joba, często z innego adresu i o dowolnej godzinie. Tokeny OIDC w CI odwracają ten układ. Job dostaje krótkotrwałe poświadczenie chmurowe albo do rejestru, a dostawca ufa nie kluczowi wklejonemu do sekretu, lecz zestawowi twierdzeń o tym jobie.
Warunki zaufania trzeba zapisać wąsko. Typowy, zbyt luźny wzorzec to „każdy workflow z tej organizacji”. Bezpieczniejszy wiąże odbiorcę tokenu z repozytorium, refem (gałąź albo tag), środowiskiem, nazwą workflow i typem zdarzenia. Osobno ogranicz audience, żeby token wystawiony dla jednego dostawcy chmury nie został przyjęty przez inny system, który ufa temu samemu issuerowi. Pull request z forka nie powinien spełniać reguły wydania poświadczeń produkcyjnych, nawet jeśli plik workflow wygląda tak samo.
Rozdziel też kierunek uprawnień. Job testowy może dostać tożsamość tylko do odczytu artefaktów albo wcale. Job publikacji obrazu dostaje zapis do jednego repozytorium w rejestrze i nic poza tym. Job wdrożenia dostaje rolę ograniczoną do jednego konta i jednego środowiska, z warunkiem na tag albo chronione środowisko. Współdzielenie jednego klucza między projektami „bo i tak jest w tym samym zespole” kasuje możliwość ustalenia, który pipeline wyciekł.
| Podejście | Kiedy ma sens | Główne ryzyko |
|---|---|---|
| Stały klucz w sekretach CI | Krótki okres przejściowy, system bez OIDC | Długi czas życia po wycieku, trudna rotacja, brak wiązania z jobem |
| Token krótkotrwały z menedżera sekretów | System nie wspiera federacji, ale pozwala na dynamiczne poświadczenia | Błąd w polityce wydania nadal daje szeroki dostęp na czas życia tokenu |
| Federacja OIDC | Chmura, rejestry i narzędzia, które weryfikują claims joba | Zbyt szeroki subject, audience albo brak warunku na ref i środowisko |
Nie przedłużaj życia tokenu „na wszelki wypadek” do godzin albo dni, bo job czasem wisi w kolejce. Jeśli zadanie trwa dłużej niż polityka, skróć zadanie albo odśwież poświadczenie w kontrolowany sposób. Długi TTL omija cały sens federacji i zostawia po incydencie okno, w którym unieważnienie workflow nic już nie daje.
3. Nadaj minimalne uprawnienia domyślnie, a podwyższaj je tylko lokalnie
Zasada najmniejszych uprawnień w CI działa odwrotnie do typowego szablonu z internetu. Najpierw ustaw na poziomie organizacji i repozytorium uprawnienia domyślne na odczyt albo na brak zapisu. Dopiero pojedynczy job, który naprawdę publikuje albo wdraża, deklaruje węższy, wyższy zakres. W GitHub Actions oznacza to między innymi permissions na poziomie workflow oraz nadpisanie tylko w jobie publikacji. W GitLab CI odpowiednikiem jest świadome zawężenie tokenu joba, chronione środowiska i brak szerokich tokenów grupowych w zwykłych pipeline’ach. [2]
Praktyczny podział wygląda tak: testy dostają read do kodu i ewentualnie do cache; publikacja obrazu dostaje write do jednego rejestru i jednej nazwy repozytorium; wdrożenie dostaje dostęp tylko do wskazanego środowiska, a nie do całej subskrypcji. Plan Terraform może czytać stan i generować diff. Apply wymaga osobnej tożsamości, osobnego środowiska i zatwierdzenia. Łączenie plan i apply na jednym koncie z uprawnieniami administratora jest wygodne i jednocześnie maksymalne.
Uprawnienia globalne wygrywają przy pierwszym setupie: każdy nowy workflow „po prostu działa”. Przegrywają przy promieniu rażenia. Lokalne uprawnienia per job wymagają więcej recenzji, ale przejęty krok testowy nie dostaje prawa do tworzenia release ani do pushowania tagów. Kryterium kontroli: zmiana uprawnień jest widoczna w kodzie pipeline’u i przechodzi ten sam przegląd co zmiana aplikacji. Uprawnienie nadane tylko w interfejsie organizacji, bez śladu w repozytorium, łatwo przeoczyć przy audycie.
Jeśli chcesz pogłębić ten wątek, sprawdź też: Jak przyspieszyć pipeline: cache, równoległość i sprytne artefakty w CI.
Wyjątek, który często psuje model, to konta „break-glass” trzymane w tym samym sekret store co codzienne pipeline’y i podpinane do zwykłych jobów. Konto awaryjne może istnieć, ale poza ścieżką CI, z osobnym procesem wydania i z alarmem przy każdym użyciu. Jeśli pipeline potrafi się nim posłużyć bez człowieka, nie jest już awaryjne.
Autoryzacja wykonania: ogranicz, kto uruchamia kod i gdzie może on działać
4. Rozdziel runnerów, środowiska i ścieżki wdrożenia według poziomu ryzyka
Runner to nie neutralna maszyna. To miejsce, w którym niezaufany kod spotyka się z siecią, dyskiem i czasem z poświadczeniami. Publiczne zadania i joby z forków nie powinny współdzielić self-hosted runnera z jobami, które widzą sekrety albo sięgają do produkcji. Współdzielona maszyna zostawia cache, katalogi domowe, klucze SSH i docker socket. Kolejny job nie musi łamać platformy CI, wystarczy, że odziedziczy stan.
Runner hostowany przez dostawcę ogranicza obowiązki operacyjne: nie utrzymujesz obrazu bazowego ani dostępu do sieci firmowej. Self-hosted daje kontrolę nad narzędziami, VPC i rejestrami prywatnymi, ale przenosi na zespół izolację, aktualizacje i czyszczenie. Wybór nie jest ideologiczny. Runner zarządzany przez dostawcę pasuje do buildów bez dostępu do sieci wewnętrznej. Self-hosted ma sens, gdy job musi widzieć prywatne systemy, pod warunkiem że nie jest wielokrotnego użytku dla niezaufanego kodu.
Dla runnerów własnych minimalny standard to jednorazowe środowisko albo twarde czyszczenie workspace, brak trwałych poświadczeń na dysku, ograniczony egress i osobne pule: publiczne PR, buildy wewnętrzne, wdrożenia. Docker-in-Docker i montowanie gniazda dockera na współdzielonym hoście znosi izolację kontenera. Produkcyjne wdrożenie powinno wymagać zaufanej gałęzi, zatwierdzonego artefaktu i dedykowanego środowiska, a nie samego faktu, że ktoś uruchomił workflow.
5. Chroń sekrety centralnie i udostępniaj je warunkowo
Sekrety w pipeline mają jedno dopuszczalne źródło: mechanizm platformy CI albo zewnętrzny menedżer sekretów, z wydaniem na czas joba. Plik w repozytorium, zmienna w obrazie kontenera, sekret w cache i klucz „tymczasowo” w logu konfiguracji odpadają, nawet jeśli repozytorium jest prywatne. Prywatność repozytorium nie jest kontrolą dostępu do produkcji.
Warunek wydania wiąż z gałęzią, tagiem, środowiskiem i zatwierdzeniem. Sekret produkcyjny nie może trafić do pipeline’u pull requesta, także wtedy, gdy autor PR ma konto w organizacji. W GitHubie chronione environment z required reviewers odcina automatyczne wdrożenie z przypadkowego pusha. W GitLabie protected branches, protected environments i protected variables dają podobny podział, o ile zmienne nie są przypadkiem dostępne dla niezabezpieczonych gałęzi. [3]

Porównaj dwa modele przechowywania. Sekrety natywne platformy są prostsze i zwykle maskowane w logach, ale słabiej nadają się do wspólnej rotacji między CI, aplikacją i infrastrukturą. Zewnętrzny menedżer daje jeden audyt i krótkie lease’y, lecz sam potrzebuje tożsamości do odczytu. Ta tożsamość powinna być federowana i ograniczona do ścieżki sekretu danego środowiska. Błąd, który trzeba wyłapać przy przeglądzie: job „na wszelki wypadek” dostaje całą grupę sekretów, choć używa jednego klucza.
6. Pinuj zależności, akcje i obrazy, zamiast ufać ruchomym tagom
Zależności supply chain w pipeline obejmują nie tylko biblioteki aplikacji. Akcja CI, obraz buildera, plugin orkiestratora i skrypt instalowany przez curl w pierwszym kroku mają te same skutki co zależność produkcyjna: wykonują się z uprawnieniami joba. Tag @v1 albo latest jest wygodny i zmienny. Autor akcji może przesunąć tag, a przejęte konto utrzymującego może podmienić kod bez zmiany Twojego YAML.
Pin zapisuj jako niezmienny identyfikator, nie jako etykietę, którą ktoś może przesunąć. Akcję CI wskazuj pełnym SHA commita, a obok, wyłącznie w komentarzu, zostaw czytelną wersję. Obraz buildera i obraz bazowy wskazuj digestem sha256, nie tagiem latest, stable ani nawet 1.2.3, jeśli rejestr pozwala ten tag nadpisać. W pliku workflow zmiana pinu ma być widoczna w diffie tak samo jak zmiana kodu aplikacji.
Dla bibliotek aplikacji źródłem prawdy jest lockfile zapisany w repozytorium. Job buduje z npm ci, poetry install –frozen albo równoważnej komendy, która odmawia pracy, gdy lockfile nie zgadza się z manifestem. npm install i „dociągnij najnowsze w CI” nie są trybem powtarzalnym. Aktualizacja zależności idzie osobnym pull requestem, z przeglądem i z tym samym zestawem testów co zmiana funkcjonalna. Automatyczny merge do gałęzi wydania bez przeglądu człowieka znosi pinowanie.
Kolejność dla tego kroku jest stała. Najpierw inwentaryzacja: akcje, obrazy, pluginy, skrypty pobierane w jobie i menedżery pakietów. Potem zamiana ruchomych referencji na SHA albo digest. Na końcu bramka w CI, która odrzuca workflow używający @v1, latest albo instalacji potokiem curl do powłoki. Skrypt z internetu wykonuje się z uprawnieniami joba, zanim jakikolwiek test zdąży coś wykryć.
Sam pin nie potwierdza pochodzenia. Przy pierwszym wprowadzeniu zależności sprawdź sumę albo podpis wydawcy i zapisz oczekiwany identyfikator. Przy każdej zmianie pinu powtórz to sprawdzenie. Komentarz z numerem wersji obok SHA może być nieaktualny. Liczy się hash w polu, którego używa runner.
Kryterium zaliczenia: dla każdego joba da się wskazać commit akcji, digest obrazu i lockfile, a zmiana któregokolwiek z tych trzech elementów wymaga przeglądu. Ostrzeżenie: pin bez procesu aktualizacji zamraża znane podatności. Pin bez weryfikacji przy zmianie tylko utrwala to, co ktoś podstawił w dniu dodania.
Artefakty i obserwacja: od budowy do nietypowej publikacji
7. Buduj raz, podpisz artefakt i wdrażaj tylko ten sam digest
Artefakt produkcyjny powstaje w jednym, zaufanym jobie, na zaufanym runnerze, z zaufanego refa. Wdrożenie nie buduje drugi raz z gałęzi „bo i tak jest ten sam commit”. Drugi build może dociągnąć inny stan rejestru, inny cache albo inny skrypt. Do środowiska idzie obiekt o znanym digestie, nie nazwa tagu, którą da się przepiąć.
Po buildzie zapisz dowód pochodzenia: digest, commit, nazwę workflow i identyfikator joba. Podpisz artefakt albo obraz kluczem albo tożsamością wydania ograniczoną do joba publikacji. Do pakietu dołóż SBOM wygenerowany w tym samym jobie, nie dorysowany później ręcznie. Rejestr i repozytorium wydań trzymają podpis obok obiektu. Job wdrożenia przed użyciem sprawdza podpis i zgodność digestu z tym, co zatwierdzono w chronionym środowisku.
Jeśli chcesz pogłębić ten wątek, sprawdź też: Kiedy potrzebujesz pentestu pipeline’u?.
Promocja między środowiskami przenosi ten sam obiekt. Test, staging i produkcja nie dostają trzech niezależnych buildów z tej samej etykiety. Jeśli weryfikacja podpisu się nie powiedzie, wdrożenie kończy się błędem, a nie ostrzeżeniem w logu, które i tak nikt nie czyta.
Kryterium zaliczenia: z jednego wdrożenia potrafisz odtworzyć ciąg commit → job → digest → podpis → środowisko, bez pytania autora pipeline’u. Ostrzeżenie: tag prod przestawiony na nowy obraz bez zmiany podpisu wygląda jak zwykły deploy. Dlatego bramka patrzy na digest i podpis, nie na nazwę tagu.
8. Loguj publikacje i alarmuj, zanim nietypowy push stanie się wdrożeniem
Log platformy CI, rejestru obrazów i chmury ma odpowiadać na cztery pytania: kto uruchomił job, z jakiego workflow i refa, jaki digest opublikował i do którego rejestru albo środowiska. Sam zielony status pipeline’u tego nie zastępuje. Retencja musi pokrywać co najmniej okno, w którym jeszcze da się powiązać publikację z rotacją poświadczeń i z listą wdrożeń.
Alarmy ustawiaj na zdarzenia, nie na ogólne „coś się dzieje w CI”. Praktyczny zestaw: push obrazu do rejestru produkcyjnego spoza joba wdrożenia, publikacja poza ustalonym oknem, nowy workflow z uprawnieniem zapisu, zmiana reguły zaufania OIDC, użycie tożsamości wydania z nieoczekiwanego repozytorium oraz seria nieudanych weryfikacji podpisu. Alert ma trafiać do kanału operacyjnego z identyfikatorem joba i digestem, nie do skrzynki, którą zespół czyta raz w tygodniu.
Maskowanie sekretów w logach jest konieczne, ale nie jest monitoringiem. Job, który wypisuje cały kontekst środowiska „dla debugowania”, omija maskowanie nietypowym formatowaniem i zostawia ślad w retencji. Debug z sekretami włączaj świadomie, na czas jednego uruchomienia, i usuń log po zamknięciu problemu.
Kryterium zaliczenia: ćwiczenie na sucho. Wybierz wczorajszą publikację i w ciągu kilku minut wskaż job, osobę zatwierdzającą, digest i środowisko wyłącznie z logów. Jeśli trzeba zgadywać po nazwie tagu, telemetria jest niekompletna.
Poświadczenia po fakcie: rotacja i reakcja
9. Rotuj i unieważniaj poświadczenia według właściciela, nie według okazji
Każde poświadczenie, którego nie da się zastąpić federacją, ma właściciela, zakres, datę następnej rotacji i sprawdzony sposób unieważnienia. Rotacja nie jest zadaniem „jak ktoś będzie miał czas”. Wpisz ją w kalendarz zespołu i traktuj przeterminowany sekret tak samo jak zaległą poprawkę bezpieczeństwa: build może iść dalej tylko świadomie, z terminem usunięcia wyjątku.
Kolejność rotacji jest ważniejsza niż sam fakt wymiany wartości. Najpierw wydaj nowe poświadczenie albo zawęż nową regułę zaufania. Potem przełącz jobi, które naprawdę go używają. Na końcu unieważnij stare i potwierdź w logach dostawcy, że stary identyfikator nie jest już przyjmowany. Odwrotna kolejność zatrzymuje wdrożenia w połowie i kusi ludzi do tymczasowego przywrócenia szerokiego klucza.
Unieważnienie dopasuj do typu dostępu. Token platformy odetnij w ustawieniach projektu i zabierz mu uprawnienia zapisu. Tożsamość chmurową odetnij przez zmianę warunku zaufania, nie przez „wyłączenie pipeline’u na chwilę”. Klucz rejestru unieważnij po stronie rejestru, bo skasowanie zmiennej w CI nie cofa kopii, która już wyszła w logu albo w cache runnera. Po rotacji uruchom jeden kontrolowany job i sprawdź, że stary sekret już nie działa, a nowy działa tylko w oczekiwanym jobie.
Ostrzeżenie: współdzielony sekret „tymczasowo” skopiowany do drugiego projektu resetuje właściciela. Po incydencie nie wiadomo, ile kopii trzeba unieważnić. Kryterium zaliczenia: lista poświadczeń produkcyjnych mieści się na jednej stronie i dla każdej pozycji zespół potrafi wykonać unieważnienie bez autora pierwotnego klucza.
10. Przy incydencie zatrzymaj wydanie, odetnij dostęp i wskaż skażone digest’y
Reakcja ma ustaloną kolejność, zanim pojawi się pierwszy alarm. Najpierw zatrzymaj ścieżki, które mogą dalej publikować: wstrzymaj joby wdrożeniowe, zablokuj chronione środowisko i wyłącz workflow podejrzany o nieautoryzowaną zmianę. Potem unieważnij poświadczenia użyte przez ten job i warunki zaufania, które mu je wydały. Dopiero wtedy szukaj zakresu. Szukanie „co się stało” przy nadal ważnym tokenie przedłuża okno publikacji.
- Przed incydentem: aktualna lista właścicieli poświadczeń, procedura unieważnienia, retencja logów CI i rejestru, zakaz wdrożenia bez podpisu i digestu.
- W trakcie: pauza wdrożeń, unieważnienie tożsamości joba, zamrożenie tagów rejestru, eksport identyfikatorów jobów i digestów, odcięcie runnera, na którym wykonał się podejrzany kod.
- Po zatrzymaniu: porównanie digestów wdrożonych w środowisku z listą podpisanych artefaktów, wycofanie niezgodnych obiektów, przebudowa z ostatniego zaufanego commita, rotacja wszystkiego, co ten job mógł odczytać.
Zakres ustalaj po digestach i identyfikatorach jobów, nie po tagach. Tag da się przepiąć wstecz i do przodu. Lista „co weszło na produkcję od godziny X” jest wiarygodna tylko wtedy, gdy wdrożenie zapisało digest. Artefakt bez ważnego podpisu traktuj jako niegodny zaufania, nawet jeśli „wygląda jak nasz build”.
Runner, który wykonał nieautoryzowany kod, nie wraca do puli po restarcie joba. Wyłącz go, zachowaj logi i obraz dysku na potrzeby analizy, a nowe buildy kieruj na świeżą maszynę. Przywrócenie współdzielonego runnera „bo pipeline stoi” jest decyzją o ponownym wpuszczeniu tego samego stanu do kolejnego joba.
Ostrzeżenia, których nie wolno pominąć w tej procedurze: nie debuguj incydentu tokenem o szerszych uprawnieniach niż codzienna tożsamość; nie kasuj logów, żeby „posprzątać”; nie promuj nowego obrazu, dopóki nie wiesz, który commit i który job są czyste. Przywrócenie ruchu jest ostatnim krokiem, po jednym kontrolowanym wdrożeniu podpisanego artefaktu i po potwierdzeniu, że stare poświadczenie jest odrzucane.
Checklista zamknięcia pipeline’u, zanim zespół uzna temat za domknięty:
- Każda akcja i każdy obraz bazowy ma SHA albo digest, a lockfile jest wymuszany w CI.
- Na produkcję wchodzi tylko artefakt zbudowany raz, z podpisem i z zapisanym digestem.
- Log CI i rejestru pozwala wskazać job, ref, digest i środowisko bez zgadywania.
- Alarm obejmuje publikację spoza joba wdrożenia, zmianę zaufania OIDC i nieudaną weryfikację podpisu.
- Każde pozostałe stałe poświadczenie ma właściciela, termin rotacji i sprawdzone unieważnienie.
- Procedura incydentu zaczyna się od pauzy wydań i odcięcia dostępu, a kończy przebudową ze znanego commita.
Kluczowe Wnioski
- Zanim zmienisz workflow, zmapuj tożsamości, sekrety, zakresy uprawnień i granice zaufania dla każdego etapu pipeline’u.
- Zastępuj stałe klucze tokenami krótkotrwałymi i federacją OIDC, a warunki zaufania wiąż z repozytorium, refem, środowiskiem, workflow oraz typem zdarzenia.
- Ustaw minimalne uprawnienia domyślne i podwyższaj je wyłącznie lokalnie, w jobach, które rzeczywiście publikują artefakty albo wdrażają zmiany.
- Oddziel joby z niezaufanym kodem od runnerów, sekretów i środowisk wdrożeniowych. Szczególnej ostrożności wymagają forki oraz self-hosted runnery.
- Projektuj pipeline tak, aby ograniczać promień rażenia pojedynczego wycieku i umożliwiać szybkie unieważnienie poświadczeń.
