Wstęp
Pewnie nieraz widziałeś w czyimś kodzie – albo, co gorsza, w publicznym repo na GitHubie – wklejony na sztywno Access Key i Secret Key do AWS. Działa? Działa. Do pierwszego wycieku. A potem ktoś znajduje te klucze, stawia sobie za nie farmę do kopania kryptowalut i robi Ci rachunek na kilka tysięcy dolarów. AWS IAM (Identity and Access Management) istnieje między innymi po to, żeby tak nie kończyć – żeby Twoja aplikacja miała dostęp do tego, czego potrzebuje, ale bez ani jednego klucza wklejonego do kodu.
W poprzednim wpisie o AWS przy pierwszych krokach pokazywałem utworzenie osobnego użytkownika IAM, żeby nie pracować na koncie root. Na potrzeby nauki to dobry start. W realnych organizacjach dla ludzi coraz częściej wybiera się jednak IAM Identity Center, czyli dostęp federacyjny i krótkie sesje zamiast stałych użytkowników IAM. W tym wpisie zostaniemy przy samym IAM, bo to on odpowiada za dużą część decyzji o dostępie w AWS.
Zaczniemy od czterech elementów, z których zbudowany jest IAM – użytkowników, grup, ról i polityk. Potem zajmiemy się polityką w formacie JSON i różnicą między rolą a użytkownikiem – kiedy używać czego i czemu role są bezpieczniejsze. A na koniec podłączymy aplikację Spring Boot do bucketa S3 tak, że w kodzie nie pojawi się ani jeden klucz dostępu – wszystko oprze się o rolę IAM.
Czym jest IAM i dlaczego dotyczy każdej usługi
IAM odpowiada w AWS za dwie rzeczy: kto jest kim (uwierzytelnianie) i co komu wolno (autoryzacja). To brzmi dość prosto, ale właśnie na tym stoi bezpieczeństwo całego konta.
Najłatwiej zrozumieć to przez schemat, który powtarza się przy każdym żądaniu w AWS. Nie ma znaczenia, czy klikasz w konsoli, czy Twoja aplikacja woła API – za każdym razem dzieje się to samo. Ktoś (principal) chce wykonać jakąś operację (action) na jakimś zasobie (resource). IAM sprawdza, czy ten ktoś ma do tego uprawnienia. Jeśli tak – przepuszcza żądanie. Jeśli nie – odrzuca je z błędem AccessDenied.
Weźmy konkret. Twoja aplikacja chce pobrać plik z bucketa S3. Principal to rola, na której działa aplikacja. Action to s3:GetObject. Resource to konkretny plik w konkretnym buckecie. IAM zagląda w polityki przypisane do tej roli i podejmuje decyzję. Cała usługa sprowadza się do odpowiadania na jedno pytanie: „czy temu principalowi wolno wykonać tę akcję na tym zasobie?”.
Działa to jak ochrona w dużym biurowcu z kartami dostępu. Karta to Twoja tożsamość, uprawnienia zapisane na karcie to polityka, a drzwi, które chcesz otworzyć, to zasób. Ochroniarz (czyli IAM) przy każdych drzwiach sprawdza, czy Twoja karta otwiera akurat te.
Jedna rzecz, o której warto wiedzieć od początku – IAM jest globalny. W poprzednim wpisie pisałem o regionach i o tym, że większość zasobów tworzysz w konkretnym regionie. Z IAM jest inaczej. Użytkownicy, role i polityki nie należą do żadnego regionu – są widoczne na całym koncie, niezależnie od tego, gdzie stawiasz instancje EC2 czy buckety.

Użytkownicy, grupy, role i polityki – cztery elementy IAM
Cały IAM da się rozłożyć na cztery elementy. Każdy odpowiada za co innego, a razem tworzą model dostępu w AWS.
Użytkownicy i grupy
Użytkownik IAM (user) to stała tożsamość na koncie AWS. Może mieć długoterminowe dane logowania: login i hasło do konsoli webowej albo Access Key ID i Secret Access Key do API i CLI. To te same klucze, które ustawia komenda aws configure – pokazywałem ją w poprzednim wpisie.
Na start, przy nauce, taki użytkownik pomaga zrozumieć podstawy. W firmowym środowisku dla ludzi lepszym kierunkiem jest IAM Identity Center, dawniej AWS SSO. Użytkownik loguje się wtedy przez firmową tożsamość, a AWS wystawia mu tymczasowy dostęp do roli. Nie musisz rozdawać stałych kluczy każdej osobie.
Grupa (group) to zbiór użytkowników. Zamiast przypinać uprawnienia do każdej osoby z osobna, tworzysz grupę i to do niej przypinasz polityki. Masz zespół pięciu developerów? Tworzysz grupę Developers z dostępem do S3 i CloudWatch, a każdego nowego developera po prostu dodajesz do tej grupy. Odchodzi z projektu – usuwasz z grupy i po sprawie. Nie musisz pamiętać, co dokładnie miał poustawiane.
Tu pojawia się rzecz, którą warto sobie zapamiętać – użytkownik IAM ze stałymi kluczami nie jest dobrym pomysłem dla aplikacji produkcyjnej. Klucze użytkownika są stałe i nie wygasają same z siebie. Jeśli wyciekną, ktoś ma dostęp tak długo, aż ręcznie ich nie skasujesz. A skoro nie użytkownik, to co? Tu wchodzą role.
Role
Rola (role) to tożsamość z uprawnieniami, ale bez stałych danych logowania. To zwykle najtrudniejszy do złapania koncept dla osób zaczynających z AWS, więc rozłóżmy go powoli.
Nikt nie „jest” rolą na stałe. Rolę się przejmuje (assume) na chwilę. W momencie przejęcia AWS wystawia tymczasowe dane logowania, które po jakimś czasie wygasają. Często domyślnie po godzinie, ale czas sesji zależy od konfiguracji roli, sposobu przejęcia roli i limitów AWS STS. Rolę może przejąć instancja EC2, funkcja Lambda, kontener w ECS, użytkownik z innego konta AWS albo inna usługa AWS.
Wyobraź sobie, że masz instancję EC2, na której działa Twoja aplikacja, i ta aplikacja musi czytać pliki z S3. Zamiast wgrywać na serwer klucze jakiegoś użytkownika, przypisujesz instancji rolę z dostępem do S3. Aplikacja działająca na tej instancji dostaje tymczasowe klucze automatycznie – nie musisz nigdzie nic wpisywać. Gdy klucze wygasają, AWS wymienia je na nowe bez Twojego udziału – aplikacja po prostu dostaje świeże i działa dalej.
To jest właśnie powód, dla którego role są podstawą bezpiecznej pracy z AWS. Do tego, jak wygląda to od strony aplikacji Spring Boot, wrócimy w dalszej części.
Polityki
Polityka (policy) to dokument w formacie JSON, który opisuje, co wolno (albo czego nie wolno). Sama w sobie nic nie robi – dopóki nie przypniesz jej do użytkownika, grupy albo roli, jest tylko kawałkiem tekstu.
Polityki dzielą się na dwa rodzaje. Tożsamościowe (identity-based) przypinasz do użytkownika, grupy lub roli i mówią „ten principal może robić to i to”. Zasobowe (resource-based) przypinasz bezpośrednio do zasobu i mówią „ten zasób jest dostępny dla tego i tego principala”. Najczęstszy przykład polityki zasobowej to bucket policy w S3. Na start skupimy się na tych tożsamościowych, bo z nimi pracujesz najczęściej.
Użytkownik czy rola – co kiedy
Żeby uporządkować różnicę między użytkownikiem a rolą, najprościej zestawić je obok siebie:
Cecha | Użytkownik IAM | Rola IAM |
Dane logowania | stałe (klucze, hasło) | tymczasowe, wygasają |
Dla kogo | ludzie | aplikacje, usługi AWS, dostęp z innego konta |
Skąd bierze uprawnienia | przypięte polityki | przypięte polityki |
Jak się „loguje" | login/hasło lub klucze | przejęcie roli (assume) |
Ryzyko przy wycieku | wysokie (klucze nie wygasają) | niższe (tymczasowe, krótkie okno) |
Reguła, którą warto trzymać w głowie, jest prosta: aplikacje dostają role, a ludzie najlepiej przechodzą przez IAM Identity Center i też finalnie pracują na rolach. Użytkowników IAM traktuj jako narzędzie do nauki albo wyjątek, nie jako domyślny model dostępu dla zespołu.
Anatomia polityki IAM
Skoro polityki rządzą tym, co komu wolno, musisz umieć je czytać. Na szczęście, gdy raz załapiesz strukturę, reszta to wariacje na ten sam temat. Zacznijmy od przykładu – polityka, która pozwala czytać pliki z jednego konkretnego bucketa:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ReadAppBucket",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::moja-aplikacja-pliki",
"arn:aws:s3:::moja-aplikacja-pliki/*"
]
}
]
}
Rozłóżmy to na czynniki pierwsze:
Version– wersja języka polityk. Prawie zawsze będzie to2012-10-17. To nie jest data, którą zmieniasz pod aktualny dzień, tylko numer wersji schematu. Zostaw tak, jak jest.Statement– lista reguł. Polityka może mieć ich wiele, każda w osobnym obiekcie.Sid– opcjonalny identyfikator reguły (Statement ID). Służy tylko czytelności, możesz go pominąć.Effect– alboAllow, alboDeny. Tu decyduje się, czy reguła nadaje uprawnienie, czy je odbiera.Action– jakie operacje obejmuje reguła. Format tousługa:Operacja, na przykłads3:GetObject. Możesz użyć gwiazdki jako symbolu wieloznacznego, na przykłads3:Get*obejmie wszystkie operacje zaczynające się odGet.Resource– na jakich zasobach reguła obowiązuje. Zasoby zapisujesz jako ARN (Amazon Resource Name), czyli unikalny identyfikator zasobu w AWS. Zwróć uwagę, że mamy tu dwa wpisy: sam bucket (potrzebny dlas3:ListBucket, czyli listowania zawartości) oraz obiekty w środku zapisane z/*(potrzebne dlas3:GetObject, czyli pobierania plików). To częsta pułapka – jak podasz tylko jeden z nich, część operacji nie zadziała.
Warunki (Condition)
Polityka może mieć też opcjonalny blok Condition, który zawęża, kiedy reguła obowiązuje. Załóżmy, że chcesz zareagować tylko na żądania kierowane do regionu Frankfurt. Sam warunek może wyglądać tak:
"Condition": {
"StringEquals": {
"aws:RequestedRegion": "eu-central-1"
}
}
To nie jest jeszcze cała polityka, tylko fragment statementu. Przy blokowaniu regionów w praktycznych politykach często używa się jawnego Deny, warunku na aws:RequestedRegion i wyjątków dla usług globalnych, które nie działają jak zwykłe usługi regionalne.
Warunki potrafią być bardzo precyzyjne – tylko z konkretnego zakresu adresów IP, tylko gdy użytkownik zalogował się z MFA, tylko w określonych godzinach. Na początek nie musisz ich pisać samodzielnie, ale dobrze wiedzieć, że istnieją i do czego służą.
Allow, Deny i kto wygrywa
Tu jest reguła, którą musisz znać, bo łatwo się na niej potknąć. W AWS domyślnie wszystko jest zabronione – to tak zwany implicit deny. Żeby cokolwiek było dozwolone, musi istnieć jawny Allow. Ale jest jeszcze jedna zasada nadrzędna: jawny Deny wygrywa z każdym Allow. Jeśli jedna polityka pozwala na s3:DeleteObject, a druga tego jawnie zabrania, to ostatecznie nie usuniesz pliku. Deny zawsze ma pierwszeństwo.
Polityki zarządzane kontra inline
Polityki występują w kilku odmianach. Polityki zarządzane przez AWS (AWS managed) to gotowce utrzymywane przez Amazon, na przykład AmazonS3ReadOnlyAccess. Wygodne na szybko, ale często dają więcej, niż faktycznie potrzebujesz. Polityki zarządzane przez Ciebie (customer managed) to Twoje własne dokumenty, które możesz przypiąć do wielu ról czy grup naraz – to z nich będziesz korzystać najczęściej w realnych projektach. Polityki inline są wpięte na sztywno w jeden konkretny obiekt i nie da się ich użyć ponownie gdzie indziej – z reguły lepiej ich unikać.
Zasada najmniejszych uprawnień
Przewija się przez cały IAM jedna myśl – nadawaj minimum uprawnień, jakie wystarczą do zadania. To zasada najmniejszych uprawnień (least privilege). Przy politykach dla aplikacji oznacza to, że zamiast s3:* (wszystkie operacje na S3) wpisujesz konkretnie s3:GetObject i s3:PutObject. Zamiast Resource: "*" (każdy zasób) podajesz ARN jednego bucketa. Na początku kusi, żeby dać szeroko i mieć spokój, ale uwierz mi – dużo łatwiej jest dodać brakujące uprawnienie, gdy aplikacja zgłosi AccessDenied, niż później sprzątać po polityce, która pozwalała na wszystko.
Role IAM
Wiemy już, że rola to tożsamość bez stałych kluczy. Teraz wejdźmy w szczegóły, bo to właśnie role spinają większość scenariuszy w AWS.
Każda rola ma tak naprawdę dwie polityki, które pełnią różne funkcje. Pierwsza to polityka uprawnień (permissions policy) – dokładnie taka, jaką rozkładaliśmy wyżej. Mówi, co rola może robić. Druga to polityka zaufania (trust policy) – mówi, kto może tę rolę przejąć. To rozróżnienie jest istotne, bo bez polityki zaufania rola jest bezużyteczna – nikt nie może jej założyć.
Zobaczmy politykę zaufania, która pozwala usłudze EC2 przejmować rolę:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
Czytamy to tak: usługa EC2 (ec2.amazonaws.com) może wykonać akcję sts:AssumeRole na tej roli, czyli ją przejąć. Dzięki temu instancja EC2, do której podepniesz tę rolę, dostaje jej uprawnienia. Pojawia się tu sts – czyli Security Token Service, usługa AWS, która wystawia tymczasowe dane logowania w momencie przejmowania roli. Za każdym razem, gdy coś przejmuje rolę, pod spodem działa właśnie STS.
Instance profile, czyli rola na instancji EC2
Gdy w konsoli przypisujesz rolę do instancji EC2, klikasz to jednym ruchem. Pod spodem AWS tworzy coś, co nazywa się instance profile – to opakowanie roli, które fizycznie podpina się do instancji. W praktyce nie musisz się tym za bardzo przejmować, konsola robi to za Ciebie, ale warto znać nazwę, bo pojawia się w dokumentacji i przy pracy z CLI czy Terraformem.
Jak aplikacja na takiej instancji dostaje klucze? Każda instancja EC2 ma wewnętrzny endpoint metadanych (IMDS) pod adresem 169.254.169.254. Biblioteka AWS SDK odpytuje ten endpoint, dostaje tymczasowe dane logowania roli, używa ich do wołania API i odnawia, gdy wygasną. Dzieje się to automatycznie. Ty nie wpisujesz żadnych kluczy – one po prostu są dostępne dla aplikacji z poziomu instancji.
Role dla innych usług
EC2 to tylko jeden przypadek. Funkcja Lambda ma swoją execution role, z którą działa i z której bierze uprawnienia, na przykład do zapisu logów w CloudWatch czy odczytu z DynamoDB. Kontenery w ECS mają task role. A jeśli pracujesz z Kubernetesem na EKS, tam role podpina się do kont serwisowych przez mechanizm IRSA (IAM Roles for Service Accounts). Schemat jest jednak zawsze ten sam – usługa przejmuje rolę, dostaje tymczasowe klucze i działa z jej uprawnieniami.
Dlaczego role biją klucze na głowę
Zebrałem kilka powodów, dla których w realnych projektach role wygrywają ze stałymi kluczami użytkownika:
- Dane logowania są tymczasowe i wygasają, więc nawet jak coś wycieknie, okno na nadużycie jest krótkie.
- Rotacja dzieje się automatycznie – nie musisz pamiętać o ręcznej wymianie kluczy co jakiś czas.
- Nie ma czego wkleić do repo, bo nie istnieje żaden stały sekret do schowania.
- Możesz nadać każdej aplikacji osobną rolę z dokładnie takimi uprawnieniami, jakich potrzebuje.
Tworzymy rolę dla EC2 krok po kroku
Teoria teorią, ale zobaczmy, jak utworzyć taką rolę w konsoli. Cały proces zajmuje minutę i wygląda tak:
- Wchodzisz w IAM, potem
Rolesi klikaszCreate role. - Jako typ zaufanej tożsamości (trusted entity) wybierasz
AWS service, a w przypadku użycia (use case) wskazujeszEC2. To w tle ustawia dokładnie tę politykę zaufania zec2.amazonaws.com, którą rozkładaliśmy wyżej. - Wybierasz polityki uprawnień. Możesz wskazać gotową politykę zarządzaną albo własną customer managed – na przykład tę z dostępem do naszego bucketa S3.
- Nadajesz roli nazwę, na przykład
app-s3-access, i tworzysz ją. - Na koniec wchodzisz w swoją instancję EC2, potem
Actions,Security,Modify IAM rolei wybieraszapp-s3-access.
Gotowe. Od tej chwili aplikacja działająca na tej instancji ma dostęp do S3 bez ani jednego klucza. To samo da się zrobić z poziomu CLI (aws iam create-role, potem attach-role-policy i utworzenie instance profile), ale na początek konsola jest wygodniejsza, bo widzisz wszystkie kroki po kolei i trudniej coś przeoczyć.

Spring Boot a IAM – dostęp do AWS bez kluczy w kodzie
Pora połączyć teorię z praktyką. Mamy aplikację Spring Boot, która ma wrzucać i czytać pliki w S3. Pytanie brzmi – skąd ta aplikacja weźmie uprawnienia do AWS?
Zacznijmy od tego, jak tego nie robić. Niestety widuję takie coś zbyt często:
// TAK NIE ROBIMY
S3Client s3 = S3Client.builder()
.credentialsProvider(StaticCredentialsProvider.create(
AwsBasicCredentials.create("AKIA...", "wJalrXU...")))
.build();
Klucze wpisane na sztywno w kodzie to klucze w repozytorium. A klucze w repozytorium to klucze w historii Gita – i to na zawsze, bo nawet jak je później usuniesz i zacommitujesz, stara wersja zostaje w historii. Nawet prywatne repo to ryzyko, bo wystarczy jeden zły dostęp albo przypadkowe upublicznienie. Tego po prostu się nie robi.
Default Credentials Provider Chain
Biblioteka AWS SDK for Java (w wersji 2) ma mechanizm, który nazywa się DefaultCredentialsProvider. To łańcuch, który szuka danych logowania po kolei, w ustalonej kolejności, i bierze pierwsze, jakie znajdzie:
- Właściwości systemowe Javy (
aws.accessKeyId,aws.secretAccessKey) - Zmienne środowiskowe (
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY) - Token web identity (używany na EKS przez IRSA)
- Shared
credentialsiconfigfiles (~/.aws/credentials,~/.aws/config) – profile zaws configure, IAM Identity Center,role_arn,credential_processi podobne ustawienia - Dane logowania kontenera (na ECS albo EKS w zależności od środowiska)
- Metadane instancji EC2, czyli rola z IMDS
Pierwsze trafienie wygrywa i SDK przestaje szukać dalej. I tu jest cały myk, dla którego to działa tak wygodnie – ten sam kod działa lokalnie i na produkcji, bez zmiany ani jednej linijki. Na swoim komputerze SDK znajdzie profil w ~/.aws (krok 4) i użyje go. Na instancji EC2 dojdzie do kroku 6 i pobierze rolę z IMDS. Aplikacja nie ma pojęcia, skąd przyszły klucze, i nie musi mieć.
Spring Cloud AWS
Można pracować bezpośrednio na AWS SDK, ale w aplikacji Spring Boot wygodniej sięgnąć po Spring Cloud AWS (io.awspring.cloud). To projekt, który opakowuje SDK w springowe abstrakcje – autokonfiguruje klienty, daje gotowe komponenty typu S3Template i ładnie integruje się z konfiguracją Springa. Przykłady niżej piszę na Javie 25 i Spring Boot 4. Spring Cloud AWS ma własny cykl wydań, niezależny od Spring Boota – dobierz wersję Spring Cloud AWS do konkretnej wersji Spring Boota z tabeli kompatybilności w repozytorium projektu.
Zaczynamy od zależności. W pom.xml dodajemy BOM (Bill of Materials, który pilnuje spójnych wersji) oraz starter do S3:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.awspring.cloud</groupId>
<artifactId>spring-cloud-aws-dependencies</artifactId>
<version>4.0.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.awspring.cloud</groupId>
<artifactId>spring-cloud-aws-starter-s3</artifactId>
</dependency>
</dependencies>
Teraz konfiguracja w application.yml. I tu zwróć szczególną uwagę na to, czego tu nie ma:
spring:
cloud:
aws:
region:
static: eu-central-1
Podajemy tylko region. Nie ma tu access-key ani secret-key. Świadomie. Dane logowania przyjdą z łańcucha, który opisałem wyżej – lokalnie z profilu, na EC2 z roli. Gdybyś teraz wpisał tu klucze, cofnąłbyś się dokładnie do tego, czego chcemy uniknąć.
Przykład: zapis i odczyt pliku w S3
Napiszmy prosty serwis, który korzysta z S3Template do wrzucenia i odczytania pliku. Dla czytelności zostawiam importy w kodzie:
import io.awspring.cloud.s3.S3Resource;
import io.awspring.cloud.s3.S3Template;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import org.springframework.stereotype.Service;
@Service
class FileStorageService {
private static final String BUCKET = "moja-aplikacja-pliki";
private final S3Template s3Template;
FileStorageService(S3Template s3Template) {
this.s3Template = s3Template;
}
void upload(String key, InputStream content) {
s3Template.upload(BUCKET, key, content);
}
String readAsString(String key) {
S3Resource resource = s3Template.download(BUCKET, key);
try (InputStream is = resource.getInputStream()) {
return new String(is.readAllBytes(), StandardCharsets.UTF_8);
} catch (IOException e) {
throw new UncheckedIOException("Failed to read S3 object: " + key, e);
}
}
}
I teraz najważniejsze pytanie – gdzie w tym kodzie są klucze do AWS? Nigdzie. S3Template dostał gotowego, skonfigurowanego klienta S3Client od Spring Cloud AWS, a ten pobrał dane logowania z łańcucha. Ty operujesz na czystej logice biznesowej i nie dotykasz tematu uwierzytelniania w ogóle.
Jak to wygląda w różnych środowiskach? Lokalnie, na swoim komputerze, logujesz się raz przez aws configure albo aws sso login, co zapisuje profil w ~/.aws. SDK go znajdzie i użyje. Jeśli masz kilka profili, możesz wskazać konkretny zmienną środowiskową AWS_PROFILE, na przykład AWS_PROFILE=dev.
Na produkcji, na instancji EC2, sytuacja jest jeszcze prostsza. Instancja ma przypisaną rolę (przez instance profile), a ta rola ma politykę dającą dostęp do bucketa. Aplikacja startuje, SDK odpytuje IMDS, dostaje tymczasowe klucze roli i działa. Zero zmian w kodzie czy w konfiguracji względem tego, co masz lokalnie.
A jeśli chcesz testować integrację z S3 lokalnie, w ogóle nie dotykając prawdziwego AWS? Tu przydaje się LocalStack – emulator usług AWS uruchamiany w kontenerze Dockera. Ustawiasz endpoint klienta na localhost, a aplikacja gada z atrapą S3 zamiast z prawdziwą usługą. W takim trybie zwykle podaje się dowolne testowe dane logowania wymagane przez emulator, ale nie są to prawdziwe klucze do Twojego konta AWS. To materiał na osobny wpis, ale dobrze wiedzieć, że masz taką opcję, gdy nie chcesz ćwiczyć na produkcyjnym koncie.
A jak wygląda polityka, którą trzeba przypiąć do tej roli, żeby nasz serwis działał? Dokładnie tyle, ile potrzeba i ani trochę więcej:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::moja-aplikacja-pliki/*"
}
]
}
To jest zasada najmniejszych uprawnień w akcji. Tylko GetObject i PutObject, bo tylko czytamy i zapisujemy. Tylko ten jeden bucket, a nie wszystkie. Żadnego s3:*, żadnego Resource: "*". Jakby aplikacja musiała kiedyś też listować zawartość bucketa, dorzucisz s3:ListBucket i ARN samego bucketa – ale dopiero wtedy, gdy faktycznie będzie to potrzebne.

Gdy coś nie działa – debugowanie AccessDenied
Prędzej czy później dostaniesz AccessDenied i przez chwilę będziesz się zastanawiać, o co chodzi. Dobra wiadomość jest taka, że AWS w komunikacie błędu mówi dokładnie, co poszło nie tak. Wystarczy go uważnie przeczytać. Typowy błąd wygląda mniej więcej tak:
User: arn:aws:sts::123456789012:assumed-role/app-s3-access/i-0abc... is not authorized to perform: s3:PutObject on resource: arn:aws:s3:::moja-aplikacja-pliki/raport.csv
Ten komunikat mówi wprost: principal app-s3-access próbował wykonać s3:PutObject na pliku raport.csv i nie ma do tego uprawnień. Brakuje akcji s3:PutObject w polityce roli. Naprawiasz, dokładasz akcję i jedziesz dalej. Przy debugowaniu warto sprawdzić kilka rzeczy:
- Czy principal jest tym, którego się spodziewasz? Jeśli aplikacja działa na roli, w komunikacie zobaczysz
assumed-role, a nie zwykłego użytkownika. To potwierdzenie, że provider chain faktycznie wziął rolę, a nie jakieś przypadkowe klucze. - Czy ARN zasobu się zgadza? Najczęstsza pomyłka to brak
/*dla obiektów albo odwrotnie – podanie tylko obiektów, gdy operacja dotyczy całego bucketa. Porównaj ARN z błędu z tym w polityce, znak po znaku. - Czy gdzieś nie ma jawnego Deny? Jeśli masz
Allow, a i tak leciAccessDenied, poszukaj polityki, która jawnie zabrania danej akcji. Pamiętaj, że Deny wygrywa zawsze. W większych organizacjach takie blokady potrafią siedzieć w politykach na poziomie całej organizacji (SCP), nie w samej roli. - Skorzystaj z Policy Simulator. To narzędzie w konsoli IAM pozwala odtworzyć żądanie i zobaczyć, która konkretnie reguła zadecydowała o wyniku.
Z mojego doświadczenia większość przypadków AccessDenied to literówka w ARN albo brakująca akcja – nie żadna głęboka magia. Te komunikaty czyta się wprost, więc debugowanie uprawnień szybko przestaje być zgadywanką.
Dobre praktyki i pułapki
Na koniec zbiór zasad, które wynoszę z pracy z AWS i które oszczędzą Ci sporo nerwów:
- Zaczynaj od najmniejszych uprawnień. Daj aplikacji minimum i dokładaj, gdy zgłosi
AccessDenied. To dużo zdrowsze podejście niż danie wszystkiego na zapas i obiecywanie sobie, że „później się to przejrzy”. - Zero kluczy w repozytorium. Nigdy. Jak musisz mieć dostęp lokalnie, użyj profilu w
~/.aws(jest poza repo) albo IAM Identity Center. Dorzuć katalog.awsdo.gitignorena wszelki wypadek, choć przy poprawnej konfiguracji i tak nie powinien tam trafić. - Aplikacja dostaje rolę, nie użytkownika. Na EC2, ECS czy Lambdzie zawsze rola. Klucze użytkownika IAM zostaw co najwyżej na lokalny development.
- MFA dla każdego człowieka. Każda osoba z dostępem do konsoli powinna mieć włączone MFA. To kosztuje minutę przy konfiguracji, a potrafi uratować konto.
- Nie rozdawaj
AdministratorAccess. Wygodne, bo działa zawsze i wszędzie, ale to klucz do całego konta. Twórz polityki pod konkretne potrzeby, nawet jeśli na początku zajmuje to więcej czasu. - Uważaj na symbole wieloznaczne.
"Action": "*"albo"Resource": "*"to proszenie się o kłopoty. Czasem nie da się inaczej, bo niektóre akcje nie wspierają zawężenia do konkretnego zasobu, ale rób to świadomie, a nie z lenistwa. - Korzystaj z narzędzi. IAM Access Analyzer pokaże Ci, które zasoby są wystawione zbyt szeroko. IAM Policy Simulator pozwala przetestować politykę, zanim ją wdrożysz na produkcję.
Jeśli śledzisz moje wpisy o Dockerze i myślisz o uruchamianiu aplikacji w kontenerach na ECS, te same zasady działają tam jeden do jednego – tylko zamiast roli instancji EC2 podpinasz task role do zadania.
Podsumowanie
IAM bywa traktowany jak nudna formalność, którą klika się byle szybciej. A to on decyduje, kto i co może zrobić na całym Twoim koncie AWS. Gdy rozłożysz go na cztery elementy – użytkowników, grupy, role i polityki – reszta usług robi się dużo prostsza.
Najważniejsze do zapamiętania jest proste. Aplikacje dostają role, ludzie najlepiej pracują przez IAM Identity Center, a uprawnienia trzymasz tak wąsko, jak się da. I nigdy, przenigdy nie wklejaj kluczy dostępu do kodu – od tego masz role i provider chain, który ogarnie to za Ciebie.
A jak u Ciebie wygląda praca z IAM? Walczyłeś już kiedyś z AccessDenied, którego nijak nie dało się rozgryźć, a okazało się, że brakowało jednej akcji w polityce? A może masz swój sposób na ogarnianie uprawnień w większym projekcie? Napisz w komentarzu, chętnie poczytam