Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 145 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,9 @@ The application uses Keycloak for authentication and authorization. Follow these

4. Verify that Keycloak is running:
```bash
curl -k https://localhost:8443/health
curl -k https://localhost:8443/realms/master --cert client.crt --key client.key
```
Note: The client certificate files (client.crt and client.key) must be in your current directory or provide the full path. If you haven't generated these yet, see the [Certificate Setup](#certificate-setup) section.

5. Access the Keycloak admin console at https://localhost:8443/admin with the following credentials:
- Username: `admin`
Expand Down Expand Up @@ -328,35 +329,133 @@ After starting Keycloak, you need to set up a realm for the Management Node. You
5. Navigate to and select the `docker/keycloak/management-node-realm.json` file from your project directory
6. Click "Create" or "Import"
7. After the import is complete, verify that the `management-node` realm has been created with all the necessary configurations
8. Note the client secret for the `ztf-client` from the Credentials tab (Clients → ztf-client → Credentials) and update it in your application.yml if needed
8. Note the client secret for the `management-node` client from the Credentials tab (Clients → management-node → Credentials) and update it in your application.yml if needed

### Option 2: Manual Configuration

If you prefer to set up the realm manually:
If you prefer to set up the realm manually (updated for Keycloak 26.x):

1. Log in to the Keycloak admin console at https://localhost:8443/admin
2. Create a new realm named `management-node`
3. Create a client with the following settings:
- Client ID: `ztf-client`
- Client Protocol: `openid-connect`
- Access Type: `confidential`
- Valid Redirect URIs: `https://localhost:8090/*`
- Web Origins: `+`
4. Note the client secret from the Credentials tab and update it in your application.yml if needed
1. Log in to the Keycloak admin console at https://localhost:8443/admin (you must first import your `client.p12` certificate into your browser)

2. Create a new realm named `management-node` by clicking the dropdown in the top-left and selecting "Create Realm"

3. Create the **management-node** client:
- In the `management-node` realm, navigate to **Clients** and click **Create client**

**General Settings:**
- Client type: `OpenID Connect`
- Client ID: `management-node`
- Click **Next**

**Capability config:**
- Client authentication: **ON** (this enables the Credentials tab)
- Authorization: **OFF**
- Authentication flow: Enable **Service accounts roles**
- Click **Next**

**Login settings:**
- Valid redirect URIs: `https://localhost:8090/*`
- Valid post logout redirect URIs: `+`
- Web origins: `+`
- Click **Save**

4. After saving, click on the **Credentials** tab to view the **Client Secret**. Copy this secret.

5. Add required roles to the client:
- Go to **Clients** → **management-node** → **Roles** tab
- Click **Create role** and add the following roles:
- `access_producer_configurations`
- `access_consumer_configurations`

6. Assign roles to the service account:
- Go to **Clients** → **management-node** → **Service accounts roles** tab
- Click **Assign role**
- Filter by **Filter by clients** and select **management-node**
- Check both roles (`access_producer_configurations` and `access_consumer_configurations`)
- Click **Assign**

7. Update your `application.yml` with the client configuration:
```yaml
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://localhost:8443/realms/management-node
jwk-set-uri: https://localhost:8443/realms/management-node/protocol/openid-connect/certs
audiences: account
opaquetoken:
introspection-uri: https://localhost:8443/realms/management-node/protocol/openid-connect/token/introspect
client-secret: YOUR_CLIENT_SECRET_FROM_STEP_4
client-id: management-node

application:
client:
key-store: keystore.jks
key-store-password: changeit
keyStoreType: JKS
```

### Testing mTLS connectivity:

Once KeyCloak is running and configured, you can test mTLS connectivity using the below command:
Once Keycloak is running and configured, you can test mTLS connectivity using the command below. Replace `YOUR_CLIENT_SECRET` with the actual client secret obtained from the Keycloak Credentials tab (step 4 in the manual configuration above):

```bash
curl --location 'https://localhost:8443/realms/management-node/protocol/openid-connect/token' \
--cert client.crt --key client.key \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=ztf-client' \
--data-urlencode 'grant_type=client_credentials'
```
```bash
curl -k --location 'https://localhost:8443/realms/management-node/protocol/openid-connect/token' \
--cert client.crt --key client.key \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=management-node' \
--data-urlencode 'client_secret=YOUR_CLIENT_SECRET' \
--data-urlencode 'grant_type=client_credentials'
```

This tests the mTLS setup by attempting to obtain a token from Keycloak using client certificate authentication.
**Note:** The `client_secret` parameter is required for confidential clients. Make sure to:
1. Copy the client secret from Keycloak admin console: **Clients** → **management-node** → **Credentials** tab
2. Replace `YOUR_CLIENT_SECRET` in the command above with your actual client secret
3. The `-k` flag is used to allow insecure connections (self-signed certificates) for development

If successful, you will receive a JSON response containing an `access_token` with the assigned roles in the `resource_access.management-node.roles` claim. This confirms that:
- ✅ mTLS authentication is working (client certificates validated)
- ✅ Client credentials are correct
- ✅ Keycloak is properly configured
- ✅ Service account has the required roles assigned

### Testing API Endpoints:

Once you have a valid token, you can test the protected API endpoints:

```bash
# Get a token and save it
TOKEN=$(curl -k https://localhost:8443/realms/management-node/protocol/openid-connect/token \
--cert client.crt --key client.key \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'client_id=management-node' \
--data-urlencode 'client_secret=YOUR_CLIENT_SECRET' \
-s | jq -r '.access_token')

# Test the producer endpoint
curl -k https://localhost:8090/api/v1/configuration/producer \
--cert client.crt --key client.key \
-H "Authorization: Bearer $TOKEN" | jq .

# Test the consumer endpoint
curl -k https://localhost:8090/api/v1/configuration/consumer \
--cert client.crt --key client.key \
-H "Authorization: Bearer $TOKEN" | jq .
```

Expected response (if no configuration data exists yet):
```json
{
"clientId": "management-node",
"producers": []
}
```

If successful, you will receive a JSON response containing an `access_token`. This confirms that:
- ✅ mTLS authentication is working (client certificates validated)
- ✅ Client credentials are correct
- ✅ Keycloak is properly configured

## Building and Running with Maven

Expand Down Expand Up @@ -557,12 +656,32 @@ How Springdoc OpenAPI works in this project
## Authentication Requirements

All protected endpoints require JWT bearer tokens. Tokens must:
- Include the audience (aud) "management-node".
- Contain a `resource_access` claim with client roles used for authorization.
- Include the audience (aud) claim with value `account` (default Keycloak audience for service accounts).
- Contain a `resource_access` claim with client-specific roles under `resource_access.management-node.roles`.

**Required Client Roles:**
- `access_producer_configurations` - Required to access `/api/v1/configuration/producer` endpoint
- `access_consumer_configurations` - Required to access `/api/v1/configuration/consumer` endpoint

**Token Structure Example:**
```json
{
"aud": "account",
"resource_access": {
"management-node": {
"roles": [
"access_producer_configurations",
"access_consumer_configurations"
]
}
},
"client_id": "management-node"
}
```

Role-specific access:
- Producer Federator: must have role `access_producer_configurations` to access `/api/v1/configuration/producer`.
- Consumer Federator: must have role `access_consumer_configurations` to access `/api/v1/configuration/consumer`.
These roles must be:
1. Created as client roles in the Keycloak `management-node` client
2. Assigned to the service account of the `management-node` client

Read the full details, examples, and Keycloak mapping guidance in [Authentication Requirements](docs/AUTHENTICATION_REQUIREMENTS.md).

Expand Down
2 changes: 1 addition & 1 deletion docker/keycloak/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ Import client key and crt in keystore to create the "certificate" to be used in
curl --location 'https://localhost:8443/realms/management-node/protocol/openid-connect/token' \
--cert client.crt --key client.key \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=ztf-client' \
--data-urlencode 'client_id=management-node' \
--data-urlencode 'grant_type=client_credentials'

---
Expand Down
62 changes: 62 additions & 0 deletions docker/keycloak/management-node.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{
"clientId": "management-node",
"name": "management-node",
"description": "",
"rootUrl": "",
"adminUrl": "",
"baseUrl": "",
"surrogateAuthRequired": false,
"enabled": true,
"alwaysDisplayInConsole": false,
"clientAuthenticatorType": "client-secret",
"secret": "BD6fxpz3TJoD1sRdKo1sKK5X4BtuGSqi",
"redirectUris": [
"https://localhost:8090/*"
],
"webOrigins": [
"+"
],
"notBefore": 0,
"bearerOnly": false,
"consentRequired": false,
"standardFlowEnabled": true,
"implicitFlowEnabled": false,
"directAccessGrantsEnabled": false,
"serviceAccountsEnabled": true,
"publicClient": false,
"frontchannelLogout": true,
"protocol": "openid-connect",
"attributes": {
"realm_client": "false",
"oidc.ciba.grant.enabled": "false",
"client.secret.creation.time": "1760976518",
"backchannel.logout.session.required": "true",
"standard.token.exchange.enabled": "false",
"oauth2.device.authorization.grant.enabled": "false",
"backchannel.logout.revoke.offline.tokens": "false"
},
"authenticationFlowBindingOverrides": {},
"fullScopeAllowed": true,
"nodeReRegistrationTimeout": -1,
"defaultClientScopes": [
"service_account",
"web-origins",
"acr",
"roles",
"profile",
"basic",
"email"
],
"optionalClientScopes": [
"address",
"phone",
"offline_access",
"organization",
"microprofile-jwt"
],
"access": {
"view": true,
"configure": true,
"manage": true
}
}
29 changes: 14 additions & 15 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,23 @@ spring:
oauth2:
resourceserver:
jwt:
issuer-uri: https://localhost:8443/realms/mng-node
jwk-set-uri: https://localhost:8443/realms/mng-node/protocol/openid-connect/certs
audiences: management-node
authorities-claim-name: resource_access
issuer-uri: https://localhost:8443/realms/management-node
jwk-set-uri: https://localhost:8443/realms/management-node/protocol/openid-connect/certs
audiences: account
opaquetoken:
introspection-uri: https://localhost:8443/realms/mng-node/protocol/openid-connect/token/introspect
client-secret:
client-id: MANAGEMENT_NODE_CLIENT # required client id for introspect endpoint
introspection-uri: https://localhost:8443/realms/management-node/protocol/openid-connect/token/introspect
client-secret: # Client secret from keycloak
client-id: management-node # MANAGEMENT_NODE_CLIENT required client id for introspect endpoint
flyway:
create-schemas: on
default-schema: mn
locations: classpath:db/migration,classpath:db/samples # samples are only for local development
enabled: true
baseline-on-migrate: true
datasource:
url: jdbc:postgresql://localhost:5433/postgres
username: # required postgress username
password: # required postgress username
url: jdbc:postgresql://localhost:5433/keycloak_db # this is setup to get you going easily using the Keycloak postgress so change this !
username: keycloak_db_user
password: keycloak_db_user_password
jpa:
properties:
hibernate:
Expand All @@ -37,16 +36,16 @@ server:
key-alias: localhost
key-store: keystore.jks #path to ssl keystore
key-store-type: JKS
key-store-password: #keystore password
trust-store: #path to ssl truststore
trust-store-password: #truststore password
key-store-password: changeit #keystore password
trust-store: truststore.jks #path to ssl truststore
trust-store-password: changeit #truststore password
trust-store-type: JKS
client-auth: need
enabled: true # disable for local development Only
application:
client:
key-store: keystore.jks # path to MTLS client keystore
keyStorePassword: # MTLS client keystore password
key-store-password: changeit # MTLS client keystore password
keyStoreType: JKS

# Actuator Configuration
Expand All @@ -69,4 +68,4 @@ logging:
uk.gov.dbt.ndtp.ia.node.management: DEBUG
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%X{clientId}] - %msg%n"
file: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%X{clientId}] - %msg%n"
file: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%X{clientId}] - %msg%n"
Loading