Użytkownik read-only, który potrzebował shella
Krótka historia detektywistyczna o AWS IAM Identity Center, EKS i jednej małej prośbie.
Prośba
Wiadomość była prosta. Osoba z zespołu backendu w Firmie, regulowanej platformie fintech, miała dostęp read-only do produkcji przez dostawcę SSO i AWS IAM Identity Center. Chciała wejść do podów backendu na produkcyjnym klastrze Kubernetesa i uruchomić kilka komend w shellu Django.
kubectl get pods działało. kubectl exec nie.
Ta część nie była zagadką. Permission set read-only był zmapowany do EKS przez AmazonEKSViewPolicy, która daje get, list i watch, i nic więcej. Żadnego exec, żadnego port-forward, żadnych Secretów. Celowo.
Oczywista poprawka mieściła się więc w jednej linii: dać roli read-only również exec.
Zwrot akcji nr 1: exec to nie „odczyt”
Zanim ta linia powstała, warto było zapytać, co właściwie daje shell w podzie backendu.
envwypisuje hasło do bazy danych, klucze API usług zewnętrznych i secret key frameworka — wszystko wstrzyknięte jako zmienne środowiskowe.- Pod działa z własną rolą AWS (IRSA). Ze środka poda jesteś tą rolą. Tutaj ta rola mogła podpisywać w imieniu platformy kluczem z KMS.
- Sam shell Django zapisuje do produkcyjnej bazy danych.
„Read-only plus exec” to w rzeczywistości „dostęp do zapisu na produkcji plus klucz do podpisywania”. A dodanie tego do wspólnego permission setu read-only dałoby to każdemu, kto go ma, a nie tylko jednej osobie, która poprosiła.
Plan się więc zmienił. Zbudować osobny permission set, przypisać go jednej osobie i utrzymać go możliwie wąskim:
SSO provider
│
▼
IAM Identity Center ── permission set "ProdShellOperator" (przypisany jednej osobie)
│ └─ customer-managed policy: tylko eks:DescribeCluster
▼
konto członkowskie ─── rola AWSReservedSSO_ProdShellOperator_<hash>
│
▼
EKS access entry ───── view policy (cały klaster, tylko odczyt)
│ + grupa Kubernetesa "shell-operators"
▼
Role + RoleBinding ─── pods/exec, tylko w namespace aplikacji
Po stronie AWS rola potrzebuje tylko eks:DescribeCluster, co wystarcza do aws eks update-kubeconfig. Cała reszta dzieje się w Kubernetesie. Rola ograniczona do namespace nie sięga poza swój namespace, więc „exec tylko w namespace aplikacji” wynika ze struktury, a nie z umowy. Jeden z namespace’ów infrastruktury miał już admission policy, która blokuje exec nawet dla adminów klastra.
Polityka została utworzona, permission set został utworzony i kliknięto przycisk przypisania.
Zwrot akcji nr 2: „Failed to create assignment”
Failed to create assignment for user <user> to AWS account <account> with permission set <permission-set>
Tylko tyle powiedziała konsola. Bez powodu i bez podpowiedzi.
Powód istnieje, tylko nie na tym ekranie. Identity Center zapisuje status każdej próby przypisania:
aws sso-admin list-account-assignment-creation-status --instance-arn <instance> --filter Status=FAILED aws sso-admin describe-account-assignment-creation-status --instance-arn <instance> \ --account-assignment-creation-request-id <request-id>
Obie nieudane próby zgłosiły ten sam FailureReason:
Received a 404 status error: Not supported policy arn:aws:iam::<target-account-id>:policy/<policy-name>.
Spójrz na konto w tym ARN. To konto produkcyjne, cel przypisania. Nie konto, na którym działa Identity Center.
Zwrot akcji nr 3: polityka na złym koncie
Poprawka wydawała się jasna: brakuje polityki, więc trzeba ją utworzyć. I została utworzona — na koncie management, bo to tam była otwarta sesja do edycji Identity Center.
Potem padło naturalne pytanie: może wpisać do permission setu ARN zamiast nazwy?
Nie da się. Permission set może dołączyć customer-managed policy tylko po nazwie i ścieżce. Identity Center zabiera tę nazwę do każdego konta, do którego przypisano permission set, szuka jej tam i dołącza to, co znajdzie, do roli, którą tworzy. Polityka na koncie management jest dla tego wyszukiwania niewidoczna. Przypisanie padałoby z tym samym 404 niezależnie od tego, ile kopii leżałoby w złym miejscu.
Prawdziwą poprawką był mały moduł Terraforma na koncie produkcyjnym, tworzący politykę o dokładnie tej nazwie. Jeden szczegół miał tu znaczenie: moduł musiał tworzyć dokładną nazwę, bez losowego sufiksu. (Używany moduł Terraforma dla ról domyślnie dodaje sufiksy. Moduł dla polityk nie. Warto to sprawdzić, zanim zaczniesz polegać na nazwie.)
Po apply przypisanie udało się za pierwszym razem.
Jajko czy kura
Jeden krok w tym łańcuchu był wciąż zablokowany: EKS access entry potrzebuje ARN roli, a rola istnieje dopiero po udanym przypisaniu. Jej nazwa kończy się hashem, który jest inny na każdym koncie, więc nie da się go też przewidzieć z góry. To wyznacza kolejność:
- polityka na koncie docelowym (apply)
- permission set i przypisanie
- odczytanie ARN nowej roli z IAM konta docelowego
- EKS access entry (apply), potem rola RBAC przez GitOps
Ponowne provisionowanie permission setu później zachowuje tę samą nazwę roli, więc kroku 4 nigdy nie trzeba powtarzać.
Epilog: jeden profil, nie dwa
Ta osoba miała teraz dwie role: read-only do AWS i rolę operatora do klastra. O jeden profil za dużo. Rozwiązaniem było dołączenie do permission setu operatora również zarządzanej przez AWS polityki ReadOnlyAccess.
To odsłoniło ostatnią małą pułapkę. Wyszukanie „ReadOnlyAccess” w edytorze permission setu zwraca setki polityk dla poszczególnych usług (AmazonS3ReadOnlyAccess, AmazonEC2ReadOnlyAccess, …), a ta podstawowa ginie wśród nich. Wyglądało, jakby nie istniała. Ustawienie filtra Type na Job function skraca listę do kilkunastu pozycji — i jest.
Kiedy ten sam układ powtarzano dla środowiska staging, dostał własny permission set i własną nazwę polityki. Odwołanie po nazwie oznacza jedną nazwę na wszystkich kontach, a polityka „Prod…” żyjąca na stagingu wprowadzałaby w błąd każdego, kto ją później przeczyta.
Wskazówki dla czytelnika
execdo poda aplikacji to nie uprawnienie do odczytu. Oddaje sekrety poda i jego tożsamość w chmurze. Nadawaj je per osoba, nigdy wspólnej roli read-only.- Ograniczaj exec strukturalnie. Użyj grupy z access entry i Role ograniczonej do namespace. Daj
pods/execzarównoget, jak icreate, bo współczesny kubectl robi exec przez websocket. - Ogólnikowe błędy konsoli zwykle mają prawdziwy powód jedno wywołanie API dalej. W Identity Center sprawdź status tworzenia przypisania.
- Odwołania do customer-managed policy to nazwy, rozwiązywane na koncie docelowym. Polityka musi tam istnieć, pod dokładnie tą nazwą, zanim przypiszesz permission set. Utworzenie jej na koncie management nic nie daje.
- Niektóre rzeczy istnieją dopiero po poprzednim kroku. Zarezerwowane role SSO pojawiają się dopiero po udanym przypisaniu, więc zaplanuj pod to kolejność apply.
- Jeśli zarządzanej polityki pozornie brakuje, przefiltruj po typie. Może po prostu być zakopana.