|
1 | 1 | # Spring Boot Security & Observability Lab |
2 | 2 |
|
3 | | -This repository is a hands-on lab designed to demonstrate the architectural evolution of a modern Java application. We |
4 | | -will build a system from the ground up, starting with a secure monolith and progressively refactoring it into a fully |
5 | | -observable, distributed system using cloud-native best practices. |
| 3 | +This repository is an advanced, hands-on lab demonstrating the architectural evolution of a modern Java application. We will build a system from the ground up, starting with a secure monolith and progressively refactoring it into a fully observable, distributed system using cloud-native best practices. |
6 | 4 |
|
7 | 5 | --- |
8 | 6 |
|
9 | | -## Lab Progress: Phase 3 - Federated Identity & Multi-Service Architecture |
| 7 | +## Workshop Guide: The Evolutionary Phases |
10 | 8 |
|
11 | | -The `main` branch currently represents the completed state of **Phase 3**. |
| 9 | +This lab is structured in distinct, self-contained phases. The `main` branch always represents the latest completed phase. To explore a previous phase's code and detailed documentation, use the links below. |
12 | 10 |
|
13 | | -* **Git Tag for this Phase:** `v3.0-federated-identity` |
14 | | - |
15 | | -### Objective |
16 | | - |
17 | | -The goal of this phase was to perform a major architectural evolution: moving from a self-contained security model to a |
18 | | -modern, **federated identity** pattern. We have introduced an external Identity Provider (Keycloak) and refactored our |
19 | | -system into two distinct services: a pure backend **Resource Server** and a new **Web Client** that handles user-facing |
20 | | -login flows. This is the standard architecture for secure, distributed systems. |
21 | | - |
22 | | -### Key Concepts Demonstrated |
23 | | - |
24 | | -* **Federated Identity (OAuth2/OIDC):** Delegating user authentication to a dedicated, centralized Identity Provider ( |
25 | | - Keycloak). |
26 | | -* **OAuth2 Resource Server:** Refactoring the backend API to a pure resource server whose only job is to validate JWTs |
27 | | - issued by an external authority. |
28 | | -* **OAuth2 Client:** Building a new web application that implements the OIDC **Authorization Code Grant Flow** for |
29 | | - secure, user-facing login. |
30 | | -* **Service-to-Service Security:** Implementing the **Client Credentials Grant** flow for secure, authenticated |
31 | | - communication between the `web-client` and the `resource-server`. |
32 | | -* **Role-Based Access Control (RBAC):** Using Keycloak to manage user and service account roles, and enforcing them in |
33 | | - the `resource-server` with Spring Security's `@PreAuthorize`. |
34 | | -* **Reverse Proxy Gateway:** Introducing a Traefik reverse proxy to provide a single, clean entry point for our identity |
35 | | - provider, solving complex container networking issues for browser-based flows. |
36 | | -* **Infrastructure as Code (IaC):** All services, including Keycloak, PostgreSQL, and Traefik, are defined in |
37 | | - `docker-compose.yml`. Keycloak's entire realm configuration is captured in a version-controlled JSON file for 100% |
38 | | - automated, reproducible setup. |
39 | | -* **Robust Startup Orchestration:** Using Docker health checks to guarantee a stable startup order, eliminating race |
40 | | - conditions between dependent services. |
41 | | - |
42 | | -### Architecture Overview |
43 | | - |
44 | | -Phase 3 introduces a distributed, multi-service architecture orchestrated by Docker Compose and fronted by a reverse |
45 | | -proxy. |
46 | | - |
47 | | -```mermaid |
48 | | -graph TD |
49 | | - subgraph "User's Machine" |
50 | | - B[Browser] |
51 | | - end |
52 | | -
|
53 | | - subgraph "Docker Compose Network (lab-net)" |
54 | | - P[Traefik Proxy] |
55 | | -
|
56 | | - subgraph "Services" |
57 | | - WC[Web Client] |
58 | | - RS[Resource Server] |
59 | | - KC[Keycloak] |
60 | | - DB[(PostgreSQL)] |
61 | | - Prom[Prometheus] |
62 | | - Graf[Grafana] |
63 | | - end |
64 | | - end |
65 | | -
|
66 | | - B -- "1. GET /dashboard (app.local)" --> P; |
67 | | - P -- "2. Forwards to" --> WC; |
68 | | - WC -- "3. Redirects to login" --> B; |
69 | | - B -- "4. GET /auth (keycloak.local)" --> P; |
70 | | - P -- "5. Forwards to" --> KC; |
71 | | - KC -- "6. User authenticates" --> B; |
72 | | - B -- "7. GET /dashboard (app.local) with session" --> P; |
73 | | - P -- "8. Forwards to" --> WC; |
74 | | - |
75 | | - subgraph "Service-to-Service Call" |
76 | | - WC -- "9. GET /api/secure/admin (via resource-server:8081)" --> RS; |
77 | | - end |
78 | | - |
79 | | - RS -- "10. Validates token with Keycloak" --> KC; |
80 | | - KC -- "11. Uses" --> DB; |
81 | | - RS -- "12. Exposes metrics" --> Prom; |
82 | | - Prom -- "13. Provides data" --> Graf; |
83 | | -
|
84 | | -``` |
85 | | - |
86 | | -1. **[Traefik](docker-compose.yml):** Our reverse proxy. It listens on port 80 and routes traffic based on hostname. It |
87 | | - directs `keycloak.local` traffic to Keycloak. |
88 | | -2. **[Keycloak](docker-compose.yml):** Our external Identity Provider, backed by a PostgreSQL database. It is **fully |
89 | | - pre-configured on startup** using a realm export file. See the "Keycloak Configuration" section below for details. |
90 | | -3. **[Web Client](web-client):** The new user-facing application. It handles the OIDC login flow and makes secure, |
91 | | - backend API calls. |
92 | | -4. **[Resource Server](resource-server):** The original application, now refactored to only protect API endpoints by |
93 | | - validating JWTs issued by Keycloak. |
94 | | - |
95 | | ---- |
96 | | - |
97 | | -### Keycloak Configuration Details |
98 | | - |
99 | | -A core principle of this lab is Infrastructure as Code. The entire Keycloak environment is configured automatically on |
100 | | -startup, requiring zero manual setup. This is achieved through three key mechanisms in our `docker-compose.yml`: |
101 | | - |
102 | | -1. **Reverse Proxy with Traefik:** A Traefik container acts as a reverse proxy. This is the cornerstone of our |
103 | | - networking solution, designed to solve the "split-horizon" problem where our browser (on the host) and our backend |
104 | | - services (in containers) have different views of the network. |
105 | | - * **The Problem:** When the `web-client` needs to log a user in, it must redirect the user's browser to Keycloak. If |
106 | | - it redirects to the internal Docker address (`http://keycloak:8080`), the browser will fail as it cannot resolve |
107 | | - that hostname. If Keycloak is configured to use `localhost`, then the inter-service JWT validation fails. |
108 | | - * **The Solution:** We use a custom hostname, `keycloak.local`. Traefik is configured via Docker labels to listen |
109 | | - for traffic directed to `keycloak.local` and route it to the `keycloak` container. This provides a single, |
110 | | - consistent address. To make this hostname reachable from our host machine, a one-time update to the `/etc/hosts` |
111 | | - file is required, as detailed in the "Local Development" section. |
112 | | -2. **Network Alias:** We use Docker's `networks.aliases` feature to make the Traefik proxy reachable at the |
113 | | - `keycloak.local` hostname from any other container on our shared `lab-net` network. This provides a single, |
114 | | - consistent URL for all services. |
115 | | -3. **Realm Export File:** We provide a complete realm definition in |
116 | | - the [observability-lab-realm.json](config/keycloak/observability-lab-realm.json) file. When the Keycloak container |
117 | | - starts, its `--import-realm` command automatically creates and configures our entire environment based on this file. |
118 | | - |
119 | | -The realm export file defines: |
120 | | - |
121 | | -* The **`observability-lab` Realm**. |
122 | | -* Two **Clients**: |
123 | | - * `resource-server-api`: A "bearer-only" client representing our backend API. |
124 | | - * `web-app-client`: A "confidential" client representing our user-facing web application, with the correct redirect |
125 | | - URIs configured. |
126 | | -* Two **Users**: |
127 | | - * `lab-admin` (password: `lab-admin`) |
128 | | - * `lab-user` (password: `lab-user`) |
129 | | -* One **Realm Role**: `ADMIN`, which is assigned to the `lab-admin` user. |
130 | | -* **Service-to-Service Authorization**: A set of mappers on the `web-app-client` that ensure its service account token |
131 | | - is correctly configured to be accepted by the `resource-server` and to contain the `ADMIN` role. |
132 | | - |
133 | | -This automated setup ensures a perfectly consistent and reproducible environment for every developer, every time. |
| 11 | +| Phase | Description & Key Concepts | Code & Docs (at tag) | Key Pull Requests | |
| 12 | +|:-----------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |
| 13 | +| **1. The Secure Monolith** | A standalone service that issues and validates its own JWTs. Concepts: `AuthenticationManager`, custom `JwtAuthenticationFilter`, `jjwt` library, and a foundational CI pipeline. | [`v1.0-secure-monolith`](https://github.com/apenlor/spring-boot-security-observability-lab/blob/v1.0-secure-monolith/README.md) | [#2](https://github.com/apenlor/spring-boot-security-observability-lab/pull/2), [#3](https://github.com/apenlor/spring-boot-security-observability-lab/pull/3), [#4](https://github.com/apenlor/spring-boot-security-observability-lab/pull/4) | |
| 14 | +| **2. Observing the Monolith** | The service is containerized and orchestrated via `docker-compose`. Concepts: Micrometer, Prometheus, Grafana, custom metrics, and automated dashboard provisioning. | [`v2.0-observable-monolith`](https://github.com/apenlor/spring-boot-security-observability-lab/blob/v2.0-observable-monolith/README.md) | [#6](https://github.com/apenlor/spring-boot-security-observability-lab/pull/6) | |
| 15 | +| **3. Evolving to Federated Identity** | The system is refactored into a multi-service architecture with an external IdP. Concepts: Keycloak, OIDC, OAuth2 Client (`web-client`) vs. Resource Server, Traefik reverse proxy, service-to-service security. | [`v3.0-federated-identity`](https://github.com/apenlor/spring-boot-security-observability-lab/blob/v3.0-federated-identity/README.md) | [#8](https://github.com/apenlor/spring-boot-security-observability-lab/pull/8) | |
| 16 | +| **4. Tracing a Distributed System** | _Upcoming..._ | - | - | |
| 17 | +| **5. Correlated Logs & Access Auditing** | _Upcoming..._ | - | - | |
| 18 | +| **6. Proactive Alerting** | _Upcoming..._ | - | - | |
| 19 | +| **7. Continuous Security Integration** | _Upcoming..._ | - | - | |
| 20 | +| **8. Advanced Secret Management** | _Upcoming..._ | - | - | |
134 | 21 |
|
135 | 22 | --- |
136 | 23 |
|
137 | | -## Local Development & Quick Start |
138 | | - |
139 | | -**Prerequisites:** Docker and Docker Compose must be installed. A **one-time setup** is required to configure local |
140 | | -domain names. |
141 | | - |
142 | | -1. **Configure Local Hostnames (One-Time Setup):** |
143 | | - You must edit your local `hosts` file to map our service domains to your local machine. This is required for your |
144 | | - browser to correctly interact with the containerized services via the reverse proxy. |
145 | | - * **On macOS / Linux:** Edit `/etc/hosts` |
146 | | - * **On Windows:** Edit `C:\Windows\System32\drivers\etc\hosts` (with an administrator editor) |
147 | | - |
148 | | - Add the following line to the end of the file: |
149 | | - ``` |
150 | | - 127.0.0.1 keycloak.local |
151 | | - ``` |
152 | | - |
153 | | -2. **Create and Configure Your Environment File:** |
154 | | - Copy the provided template to create your local `.env` file. |
155 | | - ```bash |
156 | | - cp .env.example .env |
157 | | - ``` |
158 | | - This file contains secrets that your Docker Compose stack will use. You must now add the `WEB_CLIENT_SECRET`. |
159 | | - |
160 | | - **How to get the `WEB_CLIENT_SECRET`:** |
161 | | - 1. After starting the stack for the first time (`docker-compose up -d`), navigate to the **Keycloak Admin Console** |
162 | | - at [http://keycloak.local](http://keycloak.local). |
163 | | - 2. Log in with the admin credentials (`admin`/`admin`). |
164 | | - 3. Ensure you are in the `observability-lab` realm (use the dropdown in the top-left corner). |
165 | | - 4. Navigate to **Clients** in the left-hand menu. |
166 | | - 5. Click on the `web-app-client`. |
167 | | - 6. Go to the **Credentials** tab. |
168 | | - 7. You will see a "Client secret" field. Click the "copy" icon to copy the secret value. |
169 | | - 8. Paste this value into your `.env` file: |
170 | | - ```dotenv |
171 | | - # .env |
172 | | - # ... other variables |
173 | | - WEB_CLIENT_SECRET=PASTE_YOUR_COPIED_SECRET_HERE |
174 | | - ``` |
175 | | - 9. You may need to restart the `web-client` for it to pick up the new secret: `docker-compose restart web-client`. |
176 | | -
|
177 | | -3. **Build and run the entire stack:** |
178 | | - ```bash |
179 | | - docker-compose up --build -d |
180 | | - ``` |
181 | | - This will build all application images and start all services. The `--build` flag ensures your latest code changes |
182 | | - are included. The `-d` flag runs them in the background. |
| 24 | +## How to Follow This Lab |
183 | 25 |
|
184 | | -4. **Access the Services:** |
185 | | - * **Web Client Application:** [http://localhost:8082](http://localhost:8082) (Login with `lab-user`/`lab-user` or |
186 | | - `lab-admin`/`lab-admin`) |
187 | | - * **Keycloak Admin Console:** [http://keycloak.local](http://keycloak.local) (Login with `admin`/`admin`) |
188 | | - * **Traefik Dashboard:** [http://localhost:8080](http://localhost:8080) |
189 | | - * **Prometheus UI:** [http://localhost:9090](http://localhost:9090) |
190 | | - * **Grafana UI:** [http://localhost:3000](http://localhost:3000) (Login with `admin`/`admin`) |
| 26 | +1. **Start with the `main` branch** to see the latest state of the project. |
| 27 | +2. To go back in time, use the **"Code & Docs" link** for a specific phase. This will show you the `README.md` for that phase, which contains the specific instructions and examples for that version of the code. |
| 28 | +3. To understand the *"why"* behind the changes, review the **Key Pull Requests** for each phase. |
191 | 29 |
|
192 | 30 | --- |
193 | 31 |
|
194 | | -## Usage Example: The Full OIDC Flow |
| 32 | +## Running the Project |
195 | 33 |
|
196 | | -1. **Navigate to the Web Client:** Open your browser to `http://localhost:8082`. |
197 | | -2. **Log In:** Click the "Go to Dashboard" button. You will be redirected to the `keycloak.local` login page. |
198 | | - * Log in as the admin user: `lab-admin` / `lab-admin` |
199 | | - * Or log in as the regular user: `lab-user` / `lab-user` |
200 | | -3. **Access the Dashboard and Test Permissions:** You will be redirected back to the `web-client` dashboard. Both |
201 | | - buttons will be visible for both users. This allows you to test the API-level security: |
202 | | - * **As `lab-admin`:** Clicking **both** "Fetch Secure Data" and "Fetch Admin Data" will succeed. |
203 | | - * **As `lab-user`:** Clicking "Fetch Secure Data" will succeed. Clicking "Fetch Admin Data" will correctly fail and |
204 | | - display a **403 FORBIDDEN** error. This proves that our backend `resource-server` is correctly enforcing |
205 | | - role-based security. |
206 | | -4. **Log Out:** Click the "Logout" button. This will perform a full OIDC federated logout, terminating both your |
207 | | - application session and your Keycloak session. |
| 34 | +To run the application and see usage examples for the **current phase**, please refer to the detailed instructions in its tagged `README.md` file. |
208 | 35 |
|
209 | | -#### Stop the Environment |
| 36 | +**[>> Go to instructions for the current phase: `v3.0-federated-identity` <<](https://github.com/apenlor/spring-boot-security-observability-lab/blob/v3.0-federated-identity/README.md#local-development--quick-start)** |
210 | 37 |
|
211 | | -```bash |
212 | | -docker-compose down -v |
213 | | -``` |
| 38 | +As the lab progresses, this link will always be updated to point to the latest completed phase. |
0 commit comments