Reliable, Scalable and Maintainable IT Systems
Zespół (A) Scalabeusze
Marcin Bagnowski
Piotr Jabłoński
Paweł Wysocki
Maciej Płuciennik
Krzysztof Tadeusiak
W dobie częstych wycieków danych i rosnącego braku prywatności w internecie użytkownicy tracą kontrolę nad tym, kto ma dostęp do ich plików. Rozwiązaniem jest Scale Drop — bezpieczna chmura, w której dane są szyfrowane jeszcze przed opuszczeniem urządzenia użytkownika. Pliki można odszyfrować wyłącznie za pomocą hasła znanego tylko użytkownikowi, które nigdy nie jest przechowywane w systemie.
- Użytkownik - osoba korzystająca z funkcjonalności systemu, autoryzowana za pomocą Google Auth
- Administrator - zarządza infrastrukturą, którą monitoruje i konfiguruje z użyciem dedykowanego serwisu
- Operacje użytkowników na plikach:
- Przesyłanie plików na platformę
- szyfrowanie plików
- zabezpieczone hasłem przez użytkownika
- Udostępnianie plików innym użytkownikom wskazując ich login
- Przeglądanie plików
- Lista własnych plików (zarządzanie udostępnianiem)
- Lista udostępnionych plików
- Pobieranie dostępnych plików
- Konieczność podania hasła
- Audyt aktywności własnych plików
- Historia pobierania plików (kto, kiedy)
- Usuwanie własnych plików
- Przesyłanie plików na platformę
- Użytkownik ma panel do monitorowania zajętości swojej przestrzeni dyskowej
- Konto administratora
- Logowanie niezależne od logowania użytkowników, nie potrzebuje OAuth ani frontendu, zawsze może się zalogować niezależnie od awarii
- Administrator loguje się do specjalnego konsolowego modułu monitoringu
- Monitorowanie statusu serwisów za pomocą logów - każdy działający serwis przesyła tutaj cyklicznie swoje logi
- Sprawdzanie zajętości przestrzeni dyskowej każdego użytkownika
- Sprawdzanie statystyk ruchu aktywności na platformie
- Wszystkie pliki użytkowników muszą być szyfrowane po stronie klienta przed przesłaniem do systemu.
- System nie może przechowywać haseł służących do odszyfrowania plików użytkownika.
- Komunikacja pomiędzy klientem a serwisami musi odbywać się wyłącznie z użyciem protokołu HTTPS/TLS.
- Dostęp do plików musi być możliwy wyłącznie dla użytkowników posiadających odpowiednie uprawnienia oraz poprawne hasło odszyfrowujące.
- System musi rejestrować operacje wykonywane na plikach użytkowników (upload, download, share).
- System musi ograniczać możliwość ataków typu brute-force poprzez rate limiting.
- Administrator systemu nie może mieć możliwości odszyfrowania plików użytkowników.
- System powinien umożliwiać analizę incydentów bezpieczeństwa poprzez centralne logowanie zdarzeń.
- System powinien minimalizować ilość przechowywanych danych użytkownika.
- System powinien być dostępny 24/7 z minimalnym czasem niedostępności usług.
- Awaria pojedynczego mikroserwisu nie może powodować niedostępności całego systemu.
- System powinien umożliwiać automatyczne restartowanie niedziałających usług.
- Wszystkie krytyczne operacje powinny być odporne na chwilowe błędy sieciowe oraz posiadać mechanizmy retry.
- Dane użytkowników muszą być przechowywane w sposób trwały i odporny na utratę danych.
- System powinien umożliwiać odtworzenie działania po awarii infrastruktury.
- Każdy serwis powinien posiadać health check umożliwiający wykrywanie awarii.
- System powinien umożliwiać poziome skalowanie mikroserwisów.
- System powinien obsługiwać rosnącą liczbę użytkowników bez konieczności zmian architektonicznych.
- Storage plików powinien umożliwiać przechowywanie dużych ilości danych bez wpływu na wydajność systemu.
- System powinien umożliwiać równoczesne przesyłanie i pobieranie plików przez wielu użytkowników.
- Architektura systemu powinna umożliwiać niezależne skalowanie poszczególnych komponentów.
- System musi umożliwiać centralne zbieranie logów ze wszystkich serwisów.
- Administrator musi mieć możliwość monitorowania aktualnego stanu wszystkich usług systemowych.
- System powinien być wdrażany automatycznie z wykorzystaniem pipeline CI/CD.
- Infrastruktura systemu powinna być zarządzana jako kod (Infrastructure as Code).
- Frontend: React
- Serwisy: Java (Spring Boot)
- Panel admina: Python (Streamlit)
- Baza danych: PostgreSQL
- Cache: Redis
- Storage: S3
- Chmura: AWS
- Kolejki danych: SNS + SQS
Jest interfejsem użytkownika do komunikacji z aplikacją.
- Komunikuje się z BFF po HTTPS
- Przekierowuje do Google oAuth do logowania
Jest interfejsem użytkownika do komunikacji z aplikacją.
- Jest pojedynczym punktem wejścia (API Gateway) pośredniczącym pomiędzy frontendem a siecią wewnętrzną.
- Stanowi główną linię obrony aplikacji – weryfikuje tokeny JWT i dba o bezpieczną autoryzację zapytań.
- Integruje komunikację z mikro serwisami zewnętrznymi (konta, upload, download oraz udostępnianie plików).
- Optymalizuje działanie systemu, agregując odpowiedzi i wykorzystując pamięć podręczną (Redis).
Odpowiada za logowanie przez Google OAuth, zarządzanie użytkownikami oraz generowanie tokenów JWT.
- Odbiera token Google OAuth, odczytuje zaszyte informacje: adres skrzynki pocztowej, imię i nazwisko oraz awatar. Tworzy nowego użytkownika w bazie IAM_DB i ustawia jego status na aktywny
- Generuje tokeny JWT, w których zaszywa:
{
"access_token": "<inny_token>",
"scope": "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile openid",
"expires_in": 3544,
"token_type": "Bearer",
"id_token": "<wlasciwy_token>”
} - id_token jest wykorzystywany do komunikacji z sd-upload-service oraz sd-download-service przez sd-bff-service
- Umożliwa usuwanie (wyłączania) kont użytkowników lub edytowanie indywidualnych danych
Odpowiada za wrzucanie i usuwanie plików.
- Zwraca S3 Pre-signed URLs do Frontendu, na który jest przesyłany plik
- Przechowuje metadane plików w Upload_DB (lokalizacja pliku, właściciel, rozmiar)
- Wysyła metadane plików na kolejkę SNS
Odpowiada za pobieranie plików i informacji o nich oraz za udostępnianie plików.
- Odbiera z SQS informacje o plikach i aktualizuje swoją bazę danych (mirror)
- Trzyma w bazie informacje o udostępnieniach plików,
- Ściąga pliki z S3 (dostęp read-only)
Pozwala na zarządzanie infrastrukturą oraz jej monitorowanie
- Zbiera logi ze wszystkich serwisów, liczy z nich statystyki
- Zapewnia podgląd do baz danych - IAM_DB, Upload_DB (read-only)
- Wykonuje health check dla wszystkich serwisów
- Łączy się z enginem do zarządzania serwisami
| Nazwa pola | Rodzaj danych | Opis |
|---|---|---|
| id | Identyfikator | Unikalny kod przypisany do konta. |
| username | Tekst | Nazwa użytkownika (login). |
| first_name | Tekst | Imię użytkownika. |
| last_name | Tekst | Nazwisko użytkownika. |
| avatar_url | Link internetowy | Adres do zdjęcia profilowego. |
| status | Tekst | Stan konta: ACTIVE (aktywne), DISABLED (wyłączone), LOCKED (zablokowane). |
| last_login_at | Data i godzina | Kiedy użytkownik ostatnio się zalogował. |
| created_at | Data i godzina | Kiedy konto zostało założone. |
| updated_at | Data i godzina | Kiedy ostatnio zmieniono dane na tym koncie. |
| Nazwa pola | Rodzaj danych | Opis |
|---|---|---|
| id | Identyfikator | Unikalny kod tego połączenia. |
| account_id | Identyfikator | Informacja, do którego konta z tabeli powyżej należy to logowanie. |
| provider | Tekst | Nazwa zewnętrznej firmy (w tym przypadku: GOOGLE). |
| provider_subject | Tekst (Kod) | Numer identyfikacyjny użytkownika w systemie Google. |
| Adres e-mail | Adres e-mail pobrany z konta Google. | |
| email_verified | Tak / Nie | Informacja, czy e-mail został potwierdzony przez Google. |
| created_at | Data i godzina | Kiedy użytkownik połączył to logowanie ze swoim kontem. |
| updated_at | Data i godzina | Kiedy to połączenie było ostatnio aktualizowane. |
| Nazwa pola | Rodzaj danych | Opis |
|---|---|---|
| id | Identyfikator | Unikalny kod przypisany do pliku. |
| size | Liczba | Rozmiar pliku (w bajtach). |
| updated_at | Data i godzina | Kiedy plik był ostatnio modyfikowany. |
| hash | Tekst (Kod) | Techniczny znacznik wersji pliku (używany do sprawdzania, czy plik się zmienił). |
| owner_id | Identyfikator | Identyfikator właściciela pliku. |
| name | Tekst | Oryginalna nazwa pliku (np. "dokument.pdf"). |
| location | Tekst | Ścieżka, na której fizycznie zapisany jest plik. |
| content_type | Tekst | Format pliku (np. obrazek JPG, dokument tekstowy). |
| status | Tekst | Obecny stan pliku: PENDING (w trakcie przesyłania), CONFIRMED (gotowy). |
| Nazwa pola | Rodzaj danych | Opis |
|---|---|---|
| id | Identyfikator | Unikalny kod przypisany do pliku. |
| key | Tekst | Wewnętrzny klucz/ścieżka pliku w systemie. |
| size | Liczba | Rozmiar pliku (w bajtach). |
| last_modified | Data i godzina | Kiedy plik był ostatnio modyfikowany. |
| e_tag | Tekst (Kod) | Techniczny znacznik wersji pliku (używany do sprawdzania, czy plik się zmienił). |
| owner_id | Identyfikator | Identyfikator właściciela pliku. |
| name | Tekst | Oryginalna nazwa pliku (np. "dokument.pdf"). |
| location | Tekst | Ścieżka, na której fizycznie zapisany jest plik. |
| content_type | Tekst | Format pliku (np. obrazek JPG, dokument tekstowy). |
| status | Tekst | Obecny stan pliku (np. w trakcie przesyłania, gotowy). |
| Nazwa pola | Rodzaj danych | Opis |
|---|---|---|
| id | Identyfikator | Unikalny kod operacji udostępnienia. |
| file_id | Identyfikator | Informacja, który plik jest udostępniany. |
| from_id | Identyfikator | Użytkownik, który udostępnia plik (nadawca). |
| to_id | Identyfikator | Użytkownik, któremu plik jest udostępniany (odbiorca). |
| Nazwa pola | Rodzaj danych | Opis |
|---|---|---|
| id | Identyfikator | Unikalny kod operacji pobierania. |
| file_id | Identyfikator | Informacja, który plik jest pobierany. |
| requested_at | Data i godzina | Kiedy użytkownik zażądał pobrania pliku. |
| expires_at | Data i godzina | Data wygaśnięcia dostępu (do kiedy można pobrać plik). |
- Mechanizm opiera się na obiektach (ziarnach) zarządzanych bezpośrednio przez kontener Springa, w skład których wchodzi m.in. klient S3 oraz S3Presigner.
- Parametry połączenia są dynamicznie wstrzykiwane i mapowane bezpośrednio z ustawień aplikacji oraz zmiennych środowiskowych.
- S3Presigner odpowiada za generowanie bezpiecznych, tymczasowych linków (pre-signed URLs) pozwalających na autoryzowane pobieranie i wysyłanie plików.
- Wygenerowany adres URL instruuje przeglądarkę, by wysłała plik bezpośrednio do AWS S3, co całkowicie odciąża backend z konieczności procesowania ciężkich transferów.
- S3Presigner odgórnie koduje w przepustce dokładne miejsce zapisu pliku, wymuszając strukturę ownerId / fileId (wirtualny katalog użytkownika oraz identyfikator pliku).
- Z góry narzucona i kryptograficznie podpisana ścieżka gwarantuje, że dane trafiają zawsze we właściwe miejsce, całkowicie eliminując ryzyko nadpisania lub dostępu do plików innych użytkowników.
- Mechanizm opiera się na bazie Redis w pamięci operacyjnej (in-memory), co gwarantuje błyskawiczny odczyt i zapis współdzielony między wieloma instancjami BFF.
- Aplikacja definiuje główny, nie lokując RedisCacheManager, który koordynuje dostępem do poszczególnych przestrzeni cache (bucketów).
- Wartości zapisywane w cache są konwertowane na format JSON przy użyciu Jackson2JsonRedisSerializer ze wsparciem dla modułu Java Time (dla dat i czasu). Klucze zapisywane są jako proste ciągi znaków (String).
- Audience Cache: Dedykowana przestrzeń przechowująca proste typy liczbowe (Integer) z wydłużonym TTL (domyślnie 12 godzin).
- Account Details Cache: Specjalistyczna przestrzeń do przechowywania złożonych obiektów IAMAccountResponse z czasem wygaśnięcia wynoszącym 12 godzin.
- użytkownik wybiera plik (opcjonalnie zmienia nazwę) i nadaje hasło do jego zaszyfrowania, Frontend szyfruje zawartość pliku i wyciąga metadane, które przesyła do BFF
- BFF sprawdza tożsamość użytkownika i przesyła dane do Upload Service, który umieszcza metadane w Upload DB, ich kopię wysyła na kolejkę SNS oraz prosi S3 o Pre-signed-URL, który zwraca do BFF do Frontendu
- Frontend przesyła zaszyfrowany plik na otrzymany URL i przesyła potwierdzenie do BFF -> Upload o poprawnym przesłaniu pliku
- Download Service odbiera metadane z SQS i umieszcza je w swojej bazie (mirror danych)
- Frontend prosi o hasło do pliku i występuje z żądaniem pobrania pliku
- BFF sprawdza tożsamość użytkownika i prosi Download o pobranie pliku
- Download sprawdza uprawnienia użytkownika i odsyła URL do pliku do BFF do Frontendu
- Frontend pobiera i odszyfrowuje plik
- Frontend pobiera listę użytkowników według wpisanego maila i mapuje wskazany mail na user_id, dla zadanego user_id oraz file_id przesyła żądanie o udostępnienie do BFF
- BFF przekazuje prośbę do Download Service, który dodaje wpis do tabeli o udostępnieniach pliku
- Frontend wysyła informacje o usunięciu pliku do BFF
- BFF sprawdza tożsamość użytkownika i przesyła informacje do Upload Service, kasuje metadane pliku w Upload DB i przesyła stosowny komunikat na kolejkę SNS oraz usuwa plik z S3
- Download Service odbiera metadane z SQS i usuwa metadane pliku ze swojej bazy (mirror) oraz tabeli o udostępnieniach pliku
- Wykorzystujemy standardowy sposób łączenia się z serwisem Google OAath: https://console.cloud.google.com
- Używamy wspólnego konta do administracji konsoli Google: “skarabeusz.skalowalny@gmail.com”
- Skonfigurowaliśmy aplikację, a w niej klienta platformy do uwierzytelniania
- GOOGLE_CLIENT_ID oraz GOOGLE_SECRET są używane w infrastrukturze AWS do zestawiania połączenia z serwisem OAuth Google
- W kodzie serwisów używamy standardowej paczki Java Spring “org.springframework.security.oauth2”
Topologia sieciowa systemu ScaleDrop została zbudowana wewnątrz dedykowanej chmury AWS VPC (Virtual Private Cloud). Zastosowano trójwarstwową architekturę w celu wymuszenia izolacji i bezpiecznego przepływu danych.
- Warstwa brzegowa i dystrybucji: Umiejscowiona poza główną granicą VPC. Amazon CloudFront (Globalny CDN) działa jako pojedynczy, publiczny punkt wejścia dla użytkowników. Serwuje on statyczne pliki frontendu bezpośrednio z bucketa Amazon S3 oraz bezpiecznie przekazuje cały ruch zapytań do API w głąb sieci (proxy).
- Publiczna podsieć wejściowa: Zawiera publiczny Application Load Balancer (ALB1). Jest to jedyny komponent wystawiony do internetu. Otrzymuje zewnętrzne zapytania HTTPS i kieruje je do serwisu
sd-bff-servicewewnątrz podsieci aplikacyjnej. Obsługuje również bezpośredni dostęp dla administratorów korzystających z panelu zarządzania, z pominięciem warstwy CDN. - Prywatna podsieć aplikacyjna: Całkowicie odcięta od bezpośredniego dostępu do internetu. Hostuje klaster Amazon ECS (Fargate). Wewnątrz tej podsieci wdrożono wewnętrzny Application Load Balancer (ALB2). API Gateway (
sd-bff-service) odbiera żądania z publicznego ALB1, przetwarza je i przekazuje za pośrednictwem wewnętrznego ALB2 do mikroserwisów (sd-iam,sd-upload,sd-download), dzięki czemu routing wewnętrzny jest całkowicie ukryty dla świata zewnętrznego. - Prywatna podsieć bazodanowa: Najgłębsza, najbardziej restrykcyjna warstwa, w której znajduje się instancja Amazon RDS PostgreSQL. Nie posiada ona żadnego routingu do internetu. Dostęp sieciowy jest zarządzany przez rygorystyczne reguły VPC Security Groups, które ściśle ograniczają ruch przychodzący wyłącznie do portu
5432i dopuszczają go wyłącznie z poziomu prywatnej podsieci aplikacyjnej.
Warstwa obliczeniowa korzysta z orkiestracji kontenerów z wykorzystaniem Amazon ECS (Elastic Container Service) z technologią AWS Fargate.
- Silnik AWS Fargate: Eliminuje narzut operacyjny i ryzyko bezpieczeństwa związane z zarządzaniem klasycznymi instancjami EC2 czy demonami Dockera. Każde zadanie (ECS Task) działa we własnym, odizolowanym środowisku micro-VM, co zapobiega atakom typu container breakout.
- sd-bff-service (Backend-For-Frontend): Pełni funkcję głównego API Gateway. W celu bezpiecznego zarządzania sesjami użytkowników i bardzo szybkim buforowaniem, wykorzystuje kontener Redis wdrożony we wzorcu Sidecar. Współdzielą one tę samą przestrzeń sieciową, co pozwala na komunikację wyłącznie przez interfejs
localhost(127.0.0.1), całkowicie izolując pamięć podręczną od ruchu w sieci VPC. - sd-iam-service: Zarządza tożsamością i kontami. Integruje się z zewnętrznym Google OAuth API, aby bezpiecznie weryfikować przychodzące tokeny tożsamości (ID tokens) podczas procesu logowania.
- sd-upload-service & sd-download-service: Dedykowane, lekkie mikroserwisy obsługujące biznesową logikę przesyłu plików i autoryzacji dostępu.
- sd-admin-service: Wewnętrzny panel zarządzania napisany w Pythonie (Streamlit). Wykorzystuje bibliotekę AWS SDK (boto3) do bezpośredniej interakcji z architekturą chmurową. Poza odpytywaniem schematów baz danych przez zastrzeżone połączenie, aplikacja pełni rolę aktywnego kontrolera klastra – potrafi zarządzać usługami ECS, wymuszając ich restart, włączanie, wyłączanie oraz ponowne wdrożenia (redeploy). Dodatkowo integruje się z usługą Amazon CloudWatch w celu agregacji i odczytu logów systemowych w czasie rzeczywistym.
System ScaleDrop rozdziela zarządzanie stanem aplikacji od przechowywania surowych danych, używając w tym celu kombinacji relacyjnych baz danych oraz magazynu obiektowego, ściśle przestrzegając praktyk bezpieczeństwa architektury cloud-native.
- Amazon S3 (Magazyn plików użytkowników): Pliki przesłane przez użytkowników są przechowywane w dedykowanym buckecie (
sd-fileserver), do którego dostęp publiczny jest całkowicie zablokowany. System odciąża kontenery ECS z konieczności procesowania ciężkich danych binarnych dzięki zastosowaniu Pre-signed URLs. Usługisd-uploadisd-downloadwalidują uprawnienia i generują krótkoterminowe, kryptograficznie podpisane linki URL. Aplikacja frontendowa (przeglądarka) odbiera/wysyła pliki bezpośrednio do S3 z wykorzystaniem tych wygenerowanych kluczy. Ponadto wdrożono szyfrowanie po stronie klienta – pliki są szyfrowane jeszcze przed opuszczeniem komputera użytkownika, co czyni je nieczytelnymi nawet dla administratorów chmury AWS. - Amazon RDS PostgreSQL: Zarządza danymi relacyjnymi. Zaimplementowano wzorzec Database per Service poprzez podział bazy na logicznie izolowane schematy bazodanowe:
sd_iam(konta i logowania),sd_upload(metadane przesłanych plików) orazsd_download(widok aktywnych plików gotowych do pobrania).
Granice bezpieczeństwa w całym środowisku chmurowym są egzekwowane poprzez bezwzględne stosowanie Zasady Najmniejszych Przywilejów przy pomocy usługi AWS IAM.
- Uprawnienia zadań (ECS Task Roles): Każdy mikroserwis otrzymał unikalną rolę IAM. Przykładowo, jedynie
sd-upload-serviceisd-download-serviceposiadają jawne uprawnienia do interakcji z API usługi Amazon S3 w celu podpisywania linków URL, podczas gdy główny węzeł API (sd-bff-service) nie posiada żadnego dostępu ani do S3, ani do bazy relacyjnej RDS. - Uprawnienia na poziomie bazy danych: W warstwie danych serwisy dziedzinowe mają uprawnienia zapisu/odczytu wyłącznie do swoich schematów, natomiast panel
sd-admin-serviceuwierzytelnia się przy pomocy odrębnego użytkownika bazy danych, mającego uprawnienia zawężone wyłącznie do odczytu. Mechanizm ten zapobiega wszelkim próbom destrukcyjnego SQL Injection. - Uprawnienia CI/CD: Zautomatyzowany potok GitHub Actions loguje się do platformy AWS z wykorzystaniem technicznego użytkownika IAM (Machine User), którego prawa są restrykcyjnie ograniczone do operacji wypychania obrazów Dockera do Amazon ECR oraz aktualizacji definicji zadań (Task Definitions) w ECS. Nie ma on możliwości modyfikowania ani kasowania infrastruktury sieciowej lub danych.
System wdraża politykę zerowego zaufania i całkowicie eliminuje luki w postaci twardego kodowania sekretów, haseł do baz danych czy kluczy do zewnętrznych API w kodzie źródłowym lub jawnych zmiennych środowiskowych.
- AWS Systems Manager (SSM) Parameter Store: Działa jako główny, szyfrowany sejf chmurowy dla konfiguracji.
- Zautomatyzowane generowanie przez IaC: W fazie wdrażania infrastruktury (Infrastructure as Code), skrypt Terraform automatycznie losuje mocne, wysokiej entropii hasła. Wartości te są przekazywane i trwale zapisywane bezpośrednio w usłudze SSM jako szyfrowane ciągi
SecureString. - Wstrzykiwanie w czasie uruchamiania (Runtime Injection): Demon AWS Fargate odpowiedzialny za start kontenerów kontaktuje się z usługą SSM, odszyfrowuje klucze w locie i wstrzykuje je natychmiast do pamięci RAM poszczególnych kontenerów jako zabezpieczone zmienne środowiskowe, minimalizując ryzyko ich wycieku.
Ze względu na edukacyjny format projektu, a także pulę darmowych kredytów z AWS Free Tier / AWS Educate, architektura celowo integruje elementy optymalizacji oraz formalną Akceptację Ryzyka (Risk Acceptance). Rozwiązania, które normalnie są uznawane za rygorystyczne standardy Enterprise, zostały w tych obszarach uproszczone:
- Pominięcie usługi AWS WAF: Wdrożenie i utrzymanie zapory aplikacyjnej warstwy 7 (WAF) generuje znaczące opłaty stałe i opłaty za każde zrealizowane żądanie HTTP. System w obecnej fazie akceptuje ryzyko ataków na poziomie warstwy aplikacji, całkowicie delegując obronę zewnętrzną darmowym i wbudowanym w usługę Amazon CloudFront mechanizmom absorpcji ataków wolumetrycznych L3/L4 (AWS Shield Standard).
- Zarządzane, domyślne certyfikaty SSL/TLS: Wykupienie niestandardowej nazwy domenowej oraz utrzymanie dedykowanej strefy publicznej w usłudze Amazon Route 53 wiąże się ze stałymi miesięcznymi opłatami. By uchronić projekt przed szybszym wyczerpaniem kredytów, system bazuje na domyślnych, wieloznacznych certyfikatach HTTPS, które AWS za darmo przypisuje do swoich usług dystrybucji (
*.cloudfront.netlub domeny Load Balancerów). Zapewnia to identyczny, wymagany poziom bezpieczeństwa kryptograficznego w locie, kosztem rezygnacji z indywidualnego brandingu adresu URL. - Ograniczenie do jednej Strefy Dostępności: Utworzenie rozszerzonego, w pełni redundantnego systemu oznacza w chmurze dublowanie liczby uruchomionych instancji Compute/Storage w rezerwowych centrach danych oraz konieczność pokrywania kosztów ciągłej synchronizacji wewnątrz-sieciowej. Na obecnym etapie zrezygnowano z tej warstwy tolerancji na awarię.
- Wyłączenie retencji danych bazy i obiektów: Ponieważ celem infrastruktury wykonanej w modelu Infrastructure-as-Code jest bycie "ulotną" i możliwą do zniszczenia/wskrzeszenia w każdym momencie, baza RDS w definicji wdrożeniowej otrzymała bezwzględną flagę pomijania płatnych kopii końcowych (
skip_final_snapshot = true), co połączono z niewersjonowaniem plików w S3.
- Dostęp do aplikacji oraz jej możliwości wyłącznie przy zalogowaniu się przez aktywne konto Google
- Używamy OAuth 2.0 oraz minimalnego zbioru uprawnień wymaganych do zestawienia połączenia z tym serwisem Googlowskim
- Zaciągany OAuth token uwierzytelnia użytkownika i nadaje dostęp do aplikacji
- Aplikacja jest dostępna publicznie tzn. nie wymagane jest połączenie przez prywatną sieć VPN
- Google token z OAuth 2.0 jest przekierowany do serwisu sd-iam, aby umożliwić generowania własnych tokenów JWT
- Używamy własnych tokenów JWT do komunikacji z serwisami sd-upload oraz sd-download
- Użytkownik A ma dostęp tylko do swoich plików oraz do tych, które znajdują się w tabeli file-shares z toId użytkownika A
- Adres mailowy
- Imię i nazwisko
- Zdjęcie profilowe (awatar)
Wykorzystuje standardowy i wbudowany interfejs Web Crypto API.
Kod używa algorytmu PBKDF2 (Password-Based Key Derivation Function 2). Algorytm ten łączy hasło z losowym ciągiem znaków (solenie) i "miesza" je aż 100 000 razy przy użyciu funkcji skrótu SHA-256. Taka duża liczba iteracji celowo spowalnia proces, co sprawia, że ataki typu brute-force stają się nieopłacalne czasowo. Wynikiem tego procesu jest silny, 256-bitowy klucz AES-GCM.
Proces szyfrowania składa się z kilku etapów:
- Generowanie losowości: Tworzona jest unikalna dla każdego pliku, 16-bajtowa sól oraz 12-bajtowy Wektor Inicjujący (IV). Nawet jeśli zaszyfruje się ten sam plik dwa razy tym samym hasłem, wynik (szyfrogram) będzie za każdym razem wyglądał zupełnie inaczej.
- Generowanie klucza: Na podstawie hasła od użytkownika i wygenerowanej soli, funkcja deriveKey tworzy klucz szyfrujący.
- Właściwe szyfrowanie: Plik jest szyfrowany algorytmem AES-GCM. Jest to nowoczesny standard (używany m.in. w bankowości), który nie tylko szyfruje dane, ale też zapewnia ich autentyczność. Oznacza to, że jeśli ktokolwiek spróbuje zmienić choćby jeden bajt w zaszyfrowanym pliku, proces deszyfrowania zwróci błąd, co chroni przed manipulacją danymi.
- Pakowanie: Aby plik można było później odszyfrować, aplikacja musi pamiętać użytą sól oraz wektor IV (nie są one tajne, tajne jest tylko hasło). Skrypt "skleja" te informacje w jeden bufor:
- Pierwsze 16 bajtów to Sól.
- Kolejne 12 bajtów to wektor IV.
- Cała reszta to właściwe zaszyfrowane dane pliku.
Odszyfrowywanie działa w analogiczny sposób tylko kolejność operacji jest odwrócona oraz sprawdzany jest rozmiar pliku (czy plik ma co najmniej 28 bajtów = 16 bajtów soli + 12 bajtów IV).
- Mechanizm opiera się na koncepcji Token Bucket, zaimplementowanej przy użyciu biblioteki Bucket4j
- Użytkownicy są rozróżniani na podstawie kontekstu (z tokena JWT lub oznaczani jako anonimowi).
- Limity są precyzyjnie rozliczane dla każdego użytkownika oraz każdego endpointu z osobna, dzięki generowaniu unikalnych kluczy.
- Aspekt w Springu przechwytuje żądania przed kontrolerem i decyduje o ich przepuszczeniu na podstawie dostępności żetonów w wirtualnym "wiaderku".
- Brak żetonów skutkuje natychmiastowym przerwaniem operacji i zwróceniem wyjątku, bez obciążania dalszych warstw aplikacji.
- Wiaderka utrzymywane są w pamięci RAM, a żetony uzupełniane są w sposób płynny.
- Limity są zarządzane za pomocą dedykowanej adnotacji na poziomie metod kontrolera.
W niniejszym rozdziale przeprowadzono analizę bezpieczeństwa systemu ScaleDrop, identyfikując potencjalne ataki oraz zaimplementowane mechanizmy mitygacji (ochrony). Analiza opiera się na docelowej architekturze wdrożeniowej w chmurze AWS, wykorzystującej konteneryzację (ECS Fargate), separację sieciową (VPC) oraz usługi zarządzane.
Do najważniejszych zasobów systemu, które wymagają najwyższego poziomu ochrony, należą:
- Dane użytkowników (PII) – przechowywane w bazie relacyjnej PostgreSQL (schemat
sd_iam). - Pliki użytkowników – zlokalizowane w dedykowanym buckecie Amazon S3.
- Sekrety i poświadczenia infrastrukturalne – hasła do bazy danych, klucze API, hasło administratora.
- Dostępność usług (Uptime) – kluczowa dla działania aplikacji i panelu administracyjnego.
- Wektor ataku: Zalanie publicznych punktów końcowych (API lub frontendu) fałszywym ruchem w celu zablokowania dostępu prawdziwym użytkownikom.
- Mitygacja w systemie:
- Warstwa brzegowa: Aplikacja kliencka serwowana jest z Amazon S3 za pośrednictwem Amazon CloudFront (CDN), który natywnie absorbuje ataki DDoS w warstwie 3 i 4 (sieciowej i transportowej).
- Elastyczne skalowanie: Użycie AWS Application Auto Scaling dla usług ECS zapewnia, że w przypadku nagłego wzrostu obciążenia, system automatycznie powoła nowe kontenery (skalowanie horyzontalne), zapobiegając padom pojedynczych instancji.
- Wektor ataku: Przejęcie konta użytkownika, podszycie się pod administratora lub bezpośrednia próba wywołania wewnętrznych mikroserwisów z pominięciem API Gateway.
- Mitygacja w systemie:
- Federacja Tożsamości (OAuth): System nie przechowuje haseł użytkowników (co eliminuje ataki typu credential stuffing). Logowanie odbywa się przez zewnętrznego dostawcę (Google OAuth). Za weryfikację i bezpieczeństwo tokenów OAuth odpowiada wyłącznie dedykowany
sd-iam-service. - Ukrycie topologii wewnętrznej: Mikroserwisy dziedzinowe (
sd-iam,sd-upload,sd-download) znajdują się w prywatnej podsieci (Private Subnet) bez dostępu z Internetu. Ruch kierowany jest wyłącznie przez wewnętrzny Load Balancer (Internal ALB), do którego dostęp ma tylko warstwa wejściowa (dostęp zsd-bff-service). - Ochrona sesji (Wzorzec Sidecar): Baza Redis obsługująca stan i sesje dla BFF została wdrożona jako kontener typu Sidecar. Komunikacja odbywa się wyłącznie wewnątrz pojedynczego zadania ECS przez interfejs
localhost, całkowicie odcinając bazę od ruchu w sieci VPC. - Zasada Najmniejszych Przywilejów (Least Privilege): Potok CI/CD (GitHub Actions) operuje na dedykowanej roli IAM z uprawnieniami ograniczonymi wyłącznie do aktualizacji specyfikacji ECS. Z kolei Panel Administracyjny łączy się z bazą RDS przy użyciu unikalnego użytkownika z prawami wyłącznie do odczytu (Read-Only), co całkowicie eliminuje ryzyko destrukcyjnych ataków SQL Injection.
- Federacja Tożsamości (OAuth): System nie przechowuje haseł użytkowników (co eliminuje ataki typu credential stuffing). Logowanie odbywa się przez zewnętrznego dostawcę (Google OAuth). Za weryfikację i bezpieczeństwo tokenów OAuth odpowiada wyłącznie dedykowany
- Wektor ataku: Odczytanie prywatnych plików z S3, przechwycenie danych z bazy RDS przez niepowołane podmioty lub błąd konfiguracji uprawnień w AWS.
- Mitygacja w systemie:
- Szyfrowanie po stronie klienta (Client-Side Encryption): System realizuje podejście zbliżone do End-to-End Encryption. Pliki są szyfrowane po stronie przeglądarki klienta jeszcze przed ich wysłaniem do chmury. Przechowywane w S3 obiekty są kryptograficznie nieczytelne dla infrastruktury AWS oraz administratorów systemu.
- Izolacja sieciowa bazy (Network Isolation): Instancja RDS znajduje się w odseparowanej, prywatnej podsieci. Dostęp do niej posiadają wyłącznie wybrane kontenery aplikacyjne poprzez rygorystyczne reguły Security Groups.
- Ochrona dostępu do plików (Pre-signed URLs): Buckety S3 posiadają aktywną blokadę
Block Public Access. Mikroserwisy ECS po zweryfikowaniu uprawnień w bazie generują tymczasowe, kryptograficznie podpisane linki (Pre-signed URLs) ważne tylko przez krótki okres, co uniemożliwia bezpośrednie lub stałe linkowanie do plików (hotlinkowanie).
- Wektor ataku: Podsłuchanie ruchu (Man-in-the-Middle), modyfikacja przesyłanych danych lub kradzież sekretów konfiguracyjnych z repozytorium kodu.
- Mitygacja w systemie:
- Szyfrowanie w locie (In-Transit): Komunikacja pomiędzy użytkownikiem a punktami wejścia do infrastruktury (CloudFront, Load Balancer) wymusza użycie protokołu HTTPS, korzystając z domyślnych certyfikatów wystawionych przez AWS.
- Zarządzanie sekretami (Secrets Management): Żadne hasła (np. do bazy danych, dla administratora) ani klucze nie są wpisywane w kod źródłowy ani jawne zmienne Terraform. Są one losowo generowane podczas budowania infrastruktury, zapisywane jako obiekty
SecureStringw AWS SSM Parameter Store, a usługa ECS wstrzykuje je do pamięci RAM kontenerów dopiero w momencie startu aplikacji.
Biorąc pod uwagę konieczność optymalizacji kosztów pod budżet ograniczony pakietami AWS Free Tier / AWS Educate, architektura posiada świadomie zaakceptowane kompromisy. W przypadku wdrożenia w pełni produkcyjnego wymagałyby one rozbudowy:
- Brak zaawansowanej zapory aplikacyjnej (AWS WAF): AWS WAF nie wchodzi w skład standardowego darmowego pakietu, a jego utrzymanie generuje stałe koszty (opłaty za samą definicję WebACL, zestaw reguł oraz każdy milion przetworzonych żądań). Ze względu na bardzo ograniczoną pulę kredytów chmurowych, wdrożenie WAF skutkowałoby zbyt szybkim wyczerpaniem budżetu projektu. Akceptujemy ryzyko ataków aplikacyjnych w warstwie 7 (np. HTTP floods, botnety), opierając bezpieczeństwo na darmowej, wbudowanej ochronie warstwy 3/4 w usłudze CloudFront.
- Brak dedykowanych certyfikatów SSL/TLS na niestandardowych domenach: Pozyskanie własnej domeny, zarządzanie strefami DNS (np. Amazon Route 53) w celu wystawienia i podpięcia certyfikatów z AWS Certificate Manager wiąże się z wysokimi opłatami, na które nie możemy sobie pozwolić ze względu na ograniczone kredyty. Aby uniknąć generowania zbędnych kosztów, system wykorzystuje darmowe certyfikaty wildcard domyślnie dostarczane przez AWS dla zarządzanych usług (takich jak
*.cloudfront.netczy domeny Load Balancerów). Zapewnia to pełne, wymagane bezpieczeństwo kryptograficzne całego ruchu sieciowego, kosztem rezygnacji z warstwy wizerunkowej (własnego brandingu w pasku przeglądarki) - Uproszczona polityka kopii zapasowych (Brak Disaster Recovery): Ze względu na koszty przechowywania danych w AWS (Storage Costs), zrezygnowano z rozbudowanych mechanizmów ciągłego backupu. Baza danych RDS posiada wyłączoną funkcję tworzenia końcowych migawek przed usunięciem, a buckety S3 nie mają włączonego wersjonowania obiektów (Object Versioning). Akceptujemy ryzyko całkowitej i nieodwracalnej utraty danych w przypadku celowego, złośliwego ataku typu Ransomware (usunięcie/nadpisanie danych) lub katastrofalnego błędu ludzkiego (tzw. "Fat-finger error" przez administratora). W środowisku produkcyjnym wymagałoby to wdrożenia AWS Backup oraz reguł zapobiegających przypadkowemu usunięciu (np. S3 Object Lock).
Założono organizację na GitHub, która zawiera repozytorium dla każdego z serwisów oraz do dodatkowych komponentów na potrzebę lokalnego developmentu (baza danych, symulacje usług AWS) oraz do konfiguracji Terraform.

Lokalne środowisko developmentu opiera się na dwóch trybach uruchamiania aplikacji oraz osobnym repozytorium wspierającym infrastrukturę AWS.
Serwisy mogą być uruchamiane bezpośrednio przez Gradle, co zapewnia szybki development i debugowanie na lokalnej maszynie, lub w pełnym środowisku kontenerowym, które odzwierciedla środowisko produkcyjne i pozwala testować integracje między usługami.
Dodatkowo wykorzystywane jest osobne repozytorium sd-docs, które dostarcza lokalne środowisko symulujące usługi AWS oraz zależności systemowe.
W jego skład wchodzą:
- PostgreSQL – baza danych aplikacji
- Redis – cache i mechanizmy pomocnicze
- LocalStack – emulacja usług AWS (S3, SNS, SQS)
- setup-resources – automatyczna inicjalizacja zasobów (bucket S3, kolejki, topic SNS)
Dzięki temu możliwe jest uruchomienie pełnego środowiska lokalnego bez potrzeby korzystania z realnych usług AWS, przy zachowaniu kompatybilności z produkcyjną architekturą systemu.
System wykorzystuje podejście Infrastructure as Code (IaC) oparte o Terraform, dzięki czemu cała infrastruktura AWS jest definiowana w sposób deklaratywny i wersjonowany w repozytorium Git. AWS pełni wyłącznie rolę warstwy wykonawczej, a źródłem prawdy jest kod infrastruktury.
W repozytorium sd-infrastructure znajdują się osobne pliki odpowiadające poszczególnym komponentom systemu, takim jak sieć (VPC), klastry ECS, load balancery, baza danych, storage S3 oraz mechanizmy komunikacji asynchronicznej (SNS/SQS). Dodatkowo każdy mikroserwis posiada własne definicje infrastruktury obejmujące m.in. ECS oraz ALB, co pozwala na ich niezależne zarządzanie i skalowanie.
Zmiany w infrastrukturze są wdrażane poprzez pipeline CI/CD w GitHub Actions, który automatycznie wykonuje plan zmian (terraform plan), a następnie – po ręcznej akceptacji – stosuje je w AWS (terraform apply). Takie podejście zapewnia powtarzalność środowisk, pełną kontrolę nad zmianami oraz eliminację ręcznych modyfikacji w konsoli AWS.
Do automatycznego deploy’u nowej wersji usługi zostało skonfigurowane GitHub Actions. Przy wrzucaniu kodu na gałąź main kod jest budowany i testowany, następnie ładowane są odpowiednie sekrety AWS, budowany jest obraz kontenera i publikowany do Amazon ECR.
Po opublikowaniu obrazu pipeline pobiera aktualną definicję zadania ECS, podmienia w niej wersję obrazu na nowo zbudowaną (oznaczoną unikalnym identyfikatorem commita), a następnie wdraża nową wersję usługi do klastra Amazon ECS. Proces deploy’u odbywa się w trybie rolling update, co zapewnia brak przestojów aplikacji.
Dodatkowo pipeline oczekuje na osiągnięcie stabilności usługi w ECS, co pozwala automatycznie zweryfikować poprawność wdrożenia nowej wersji przed zakończeniem procesu CI/CD.
Dokumentacja API serwisów BFF, IAM, Upload, Download została wykonana narzędziem Swagger.
Do panelu admina należy najpierw zalogować się podając login i hasło.
Po lewej stronie mamy opcję wyboru wybranej zakładki - serwisy, logi i monitoring, bazy danych oraz możliwość wylogowania się.
W tym miejscu możemy sprawdzić stan oraz liczbę instancji każdego z serwisów, a także zatrzymać, uruchomić lub wykonać świeży Deploy wybranego serwisu.
Wybieramy interesujący nas serwis oraz maksymalną liczbę logów z ostatniego zadanego okresu czasu. Admin pobiera stosowne logi z Amazon CloudWatch.
W zakładce “Database” widzimy tabelę użytkowników wraz z mailem, datą rejestracji konta oraz datą ostatniej aktywności w systemie.
Po wejściu w wybranego użytkownika widać wszystkie informacje na temat konta oraz listę jego wszystkich plików.

Architektura Scale Drop została zaprojektowana w modelu mikro serwisowym, co pozwala na niezależne skalowanie poszczególnych komponentów systemu w zależności od obciążenia.
Frontend oraz BFF mogą być skalowane poziomo za pomocą load balancera, co zapewnia obsługę rosnącej liczby użytkowników bez wpływu na pozostałe warstwy systemu.
Kluczowe serwisy (IAM, Upload, Download) działają jako niezależne usługi w ECS, dzięki czemu mogą być skalowane oddzielnie w zależności od liczby operacji takich jak logowanie, upload czy pobieranie plików.
Warstwa storage oparta o Amazon S3 zapewnia praktycznie nieograniczoną skalowalność bez potrzeby ręcznej konfiguracji. Metadane przechowywane w bazach danych mogą być skalowane poprzez replikację i niezależne skalowanie instancji.
Komunikacja asynchroniczna oparta o SNS/SQS pozwala na buforowanie ruchu i odciążenie serwisów, co dodatkowo zwiększa odporność systemu na skoki obciążenia.
Całość infrastruktury uruchamiana w ECS wspiera autoskalowanie, co umożliwia dynamiczne dostosowanie liczby instancji do aktualnego ruchu bez przestojów.
Zebrane zostały metryki (rozmiar pliku, czas wysyłki/pobrania) serwisów dla odpowiednio sd-upload oraz sd-download pośrednicząc przez sd-bff. Celem testów jest pokazanie, że wąskim gardłem w aplikacji jest łącze internetowe użytkownika, a nie problemy wydajnościowe aplikacji webowej, czy serwisów. Komputer testowy posiada łącze 70 MB/s (down) / 30 MB/s (up) mierzone w megabajtach na sekundę.
Przygotowany wykres przedstawia czas przesyłu w skali logarytmicznej, aby czytelniej zobrazować różnice w wartościach między plikami.
Wysłanie - 114ms, 245ms, 557ms, 18.82s
Pobranie - 128ms, 339ms, 332ms, 5.45s
Widoczne jest, że im większy jest rozmiar pliku, tym dłuższy jest czas wysyłki/pobrania. Typowy czas wykonania zapytania do serwisów to ok. 200~300ms, także nawet dla chudych plików jest on na tyle mały, że użytkownik nie czeka nadmiernie długo, aż wyśle/pobierze plik.



