## Overview

This guide provides step-by-step instructions for integrating Keycloak Single Sign-On (SSO) with the Glasswall Halo platform in an offline (air-gapped), on-premises environment.

It assumes you've already deployed the Single Node Halo platform (e.g., on VMs via Proxmox or ESXi) and now want to enable OIDC-based SSO using Keycloak.

You'll learn how to:

- Set up a Keycloak server with HTTPS in an offline environment (using a preloaded container image or manual VM installation).
- Import and customize the provided `glasswall-realm.json` Keycloak configuration.
- Configure TLS certificates and DNS/host resolution for both Keycloak and Halo.
- Deploy the Halo portal and related services:
  - `cdrplatform-portal`
  - `cdrplatform-portalaccess`
  - `cdrplatform-api-access`

## Glasswall Halo's Keycloak SSO Integration Guide

* [Prerequisites](/halo/keycloak-sso-integration#prerequisites)
* [Step 1 - Configuring keycloak for Glasswall Halo SSO](/halo/keycloak-sso-integration#step-1---configuring-keycloak-for-glasswall-halo-sso)
* [Step 2 - TLS certificates and DNS considerations](/halo/keycloak-sso-integration#step-2---tls-certificates-and-dns-considerations)
* [Step 3 - Deploying Halo services with Keycloak SSO (Helm configurations)](/halo/keycloak-sso-integration#step-3---deploying-halo-services-with-keycloak-sso-helm-configurations)
* [Step 4 - Validation and testing SSO integration](/halo/keycloak-sso-integration#step-4---validation-and-testing-sso-integration)
* [Step 5 - Troubleshooting](/halo/keycloak-sso-integration#step-5---troubleshooting)

## Prerequisites

### Environment requirements

- This setup is intended for air-gapped or strictly on-premises environments.
- The Single Node Halo cluster must already be deployed.
- Internal DNS resolution must be in place, either via a local DNS server or manually through `/etc/hosts` entries.
- Keycloak must be installed locally on a host or virtual machine.
→ [Keycloak documentation & installation](https://www.keycloak.org/downloads)

### Required resources

- A pre-configured realm file (`glasswall-realm.json`), provided by [Glasswall Support](mailto:support@glasswall.com).
- Local access to Halo Helm charts for deployment within the Single Node Halo cluster.

### DNS/hosts configuration

Ensure the hostname for your Keycloak server is resolvable by both client machines and Halo platform components.

- **With internal DNS**: create an A record for `keycloak.dev.local` pointing to the Keycloak server’s IP address.
- **Without internal DNS**: add the following entry to the `hosts` file on each machine that needs access:

## Step 1 - Configuring Keycloak for Glasswall Halo SSO

Glasswall provides a Keycloak realm configuration (as a JSON file) that defines the realm, clients, roles, and scopes needed for Halo's SSO.

You can import this configuration to avoid manual setup. In an offline setup, do this via the Keycloak Admin UI.

### 1.1 - Importing the Glasswall realm configuration

A. **Obtain the realm export**: you should have a file named `glasswall-realm.json`. This file contains the realm settings for Halo's SSO.

**Glasswall realm summary**

The Glasswall realm includes the following pre-configured items:

- **Realm ID**: Glasswall

- **Clients**
    - Halo-portal-client
    - Halo-API-access
    - Halo-portal-access

- **Key roles**
    - Realm-level: admin, user
    - Client-level: Halo client admin and user roles.
    - Client scopes (pre-configured with mappers):
    - Portaluserscope: necessary claims (roles, email) to tokens.

- **Token lifespan**
    - SSO session idle timeout: 30 mins

- **Authentication**: standard username/password.

B. **Log into Keycloak Admin**
    - Using a web browser, navigate to the Keycloak Admin Console at `https://<KEYCLOAK_HOST>:8443/` and log in with the master admin account.

![01_Keycloak](/.attachments/01_keycloak.png)

C. **Import the realm**.

- In the admin console select the dropdown that currently shows "Master" and click **Create realm**.

![02_Keycloak](/.attachments/02_keycloak.png)
   
- From the *Create realm* screen, select **Import**, and upload the `glasswall-realm.json` file.

![03_Keycloak](/.attachments/03_keycloak.png)

- Keycloak will parse the file and populate the realm name (e.g.,`glasswall`).
    - Confirm the import.

![04_Keycloak](/.attachments/04_keycloak.png)

D. **Verify realm settings**

- After import, review key settings and ensure:
    - Realm name is`glasswall`.
    - Realm is enabled.
    - SSL required: likely set to`external`.
    - Clients should include:
        - `halo-portal-client`
        - `halo-portal-access`
        - `halo-api-access`

![05_Keycloak](/.attachments/05_keycloak.png)

### 1.2 Customizing client settings (hostnames and redirect URIs)

Next, for each of the Halo clients, update the configuration to match your environment.

#### Halo-portal-client (portal frontend)

![06_Keycloak](/.attachments/06_keycloak.png)

**Valid redirect URIs**: replace the placeholder domain in the realm JSON with your actual portal domain. The sample configuration uses a test domain (for example,`https://pvetest.cdr.glasswall.dev/authentication/login-callback`). Update this value to match the URL users will use to access your Halo portal.

For instance, if your Halo portal is available at `https://halo.dev.local/` and the application’s login callback path is `/authentication/login-callback`, set:

- **Valid redirect URIs**:`https://halo.dev.local/authentication/*`
- **Web origins**:`https://halo.dev.local`
- **Root URL** (optional):`https://halo.dev.local`
- **Valid post logout redirect URIs**:`https://halo.dev.local/authentication/logout-callback`

![07_Keycloak](/.attachments/07_keycloak.png)

#### Halo-portal-access (portal backend)

  - **Valid redirect URIs**: (e.g.,`https://pvetest.cdr.glasswall.dev/*`)
      - Update it to your portal domain.
  - **Web origins**:`https://halo.dev.local`

#### Halo-API-access

- Leave redirect settings as-is, and save each client’s settings.

![08_Keycloak](/.attachments/08_keycloak.png)

### 1.3 Creating users and assigning roles

Now you'll create users and assign Halo roles.

![09_Keycloak](/.attachments/09_keycloak.png)

A. In the `glasswall` realm, navigate to **Users** → **Add user**.
B. Fill in the Username and details.
C. In **Credentials** section, set a password and uncheck "Temporary".
D. In **Role mappings**, assign:
   - Realm roles:
       - `Admin`
       - `User`
   - Client roles:
     - `halo-portal-client`[Admin, user]
     - `halo-api-access`[Admin, user]
     - `halo-portal-access`[Admin, user]

![10_Keycloak](/.attachments/10_keycloak.png)

An Admin user sees:

![11_Keycloak](/.attachments/11_keycloak.png)

And can manage:

![12_Keycloak](/.attachments/12_keycloak.png)

Including:

![13_Keycloak](/.attachments/13_keycloak.png)

---

## Step 2 - TLS certificates and DNS considerations

Ensure proper TLS trust and DNS for all components.

### 2.1 TLS trust

Halo platform services (`Portal`, `Portal-Access`, `API-Access`) must trust Keycloak’s certificate.

### 2.2 DNS and /etc/hosts

Ensure:
- Keycloak hostname resolves for all services and users
- Halo domains resolve as well

---

## Step 3 - Deploying Halo services with Keycloak SSO (Helm configurations)

Halo is deployed via Helm; offline environments use charts bundled with the system.

### Deploy Halo portal

```bash
portal_image_tag=$(k get deploy portal  -o json | jq -r '.spec.template.spec.containers[0].image' | cut -d":" -f2)
portal_domain="glasswall.example.com"
keycloak_domain="keycloak.example.com"
helm upgrade --install cdrplatform-portal ./cdrplatform-portal \
  --set image.tag="${portal_image_tag:?}" \
  --set image.pullPolicy=IfNotPresent \
  --set ingress.tls.enabled=true \
  --set ingress.tls.domain="${portal_domain:?}" \
  --set ingress.tls.secretName="tls-secret" \
  --set configuration.BackendUrl="https://${portal_domain:?}" \
  --set configuration.HaloVersion="2.12.0" \
  --set configuration.EnabledPages="SystemSettings\,PolicySettings\,ValidationSettings" \
  --set configuration.OIDC.ProviderOptions.Authority="https://${keycloak_domain:?}/realms/glasswall" \
  --set configuration.OIDC.ProviderOptions.ClientId="halo-portal-client" \
  --set configuration.OIDC.ProviderOptions.RedirectUri="https://${portal_domain:?}/authentication/login-callback" \
  --set configuration.OIDC.ProviderOptions.PostLogoutRedirectUri="https://${portal_domain:?}/authentication/logout-callback" \
  --set configuration.OIDC.ProviderOptions.Scope="openid profile api://cdrplatform-portal-access/PortalUserScope" \
  --set configuration.OIDC.ProviderOptions.SilentCheckSsoRedirectUri="https://${portal_domain:?}/silent-check-sso.html" \
  --set appenvironment.HTTP_CSP_FRAME_SRC="'self' https://${keycloak_domain:?} https://${portal_domain:?}/silent-check-sso.html" \
  --set appenvironment.HTTP_CSP_CONNECT_SRC="'self' https://${keycloak_domain:?}" \
  --set configuration.Authentication__Schemes__Bearer__RequireHttpsMetadata=true \
  --set configuration.Authentication__Schemes__Bearer__MetadataAddress="https://${keycloak_domain:?}/realms/glasswall/.well-known/openid-configuration" \
  --set configuration.Logging__LogLevel__Keycloak="Debug" \
  --atomic
```

### Deploy portal-access

```bash
image_tag=$(k get deploy portal-access  -o json | jq -r '.spec.template.spec.containers[0].image' | cut -d":" -f2)
portal_domain="glasswall.example.com"
keycloak_domain="keycloak.example.com"
helm upgrade --install cdrplatform-portal-access ./cdrplatform-portal-access \
  --set image.tag="${image_tag:?}" \
  --set image.pullPolicy=IfNotPresent \
  --set ingress.tls.domain="${portal_domain:?}" \
  --set ingress.tls.enabled=true \
  --set ingress.tls.secretName="tls-secret" \
  --set configuration.AuthenticationScheme="Bearer" \
  --set configuration.Authentication__Schemes__Bearer__ValidAudiences__0="api://cdrplatform-portal-access" \
  --set configuration.Authentication__Schemes__Bearer__ValidIssuer="https://${keycloak_domain:?}/realms/glasswall" \
  --set configuration.Authentication__Schemes__Bearer__Authority="https://${keycloak_domain:?}/realms/glasswall" \
  --set configuration.Authentication__Schemes__Bearer__RequireHttpsMetadata=true \
  --set configuration.Authentication__Schemes__Bearer__MetadataAddress="https://${keycloak_domain:?}/realms/glasswall/.well-known/openid-configuration" \
  --set configuration.Authentication__Schemes__Bearer__JwksUri="https://${keycloak_domain:?}/realms/glasswall/protocol/openid-connect/certs" \
  --atomic
```

### Deploy API-access

```bash
api_access_image_tag=$(k get deploy api-access  -o json | jq -r '.spec.template.spec.containers[0].image' | cut -d":" -f2)
portal_domain="test.cdr.glasswall.dev"
keycloak_domain="pbkc.cdr.glasswall.dev"
helm upgrade --install cdrplatform-api-access ./cdrplatform-api-access \
  --set image.tag="${api_access_image_tag:?}" \
  --set image.pullPolicy=IfNotPresent \
  --set ingress.tls.enabled=true \
  --set ingress.tls.secretName="tls-secret" \
  --set ingress.tls.domain="${portal_domain:?}" \
  --set configuration.AuthenticationScheme="Bearer" \
  --set configuration.Authentication__Schemes__Bearer__ValidAudiences__0="api://cdrplatform-api-access" \
  --set configuration.Authentication__Schemes__Bearer__ValidIssuer="https://${keycloak_domain:?}/realms/glasswall" \
  --set configuration.Authentication__Schemes__Bearer__Authority="https://${keycloak_domain:?}/realms/glasswall" \
  --set configuration.Authentication__Schemes__Bearer__RequireHttpsMetadata=true \
  --set configuration.Authentication__Schemes__Bearer__MetadataAddress="https://${keycloak_domain:?}/realms/glasswall/.well-known/openid-configuration" \
  --set configuration.Authentication__Schemes__Bearer__JwksUri="https://${keycloak_domain:?}/realms/glasswall/protocol/openid-connect/certs" \
  --atomic
```

### 3.1 Post-deployment checklist

- Ensure that the pods are running (no`CrashLoopBackOff`).
- Also check that the logs show successful Keycloak OIDC config.

![14_Keycloak](/.attachments/14_keycloak.png)

---

## Step 4 - Validation and testing SSO integration

![15_Keycloak](/.attachments/15_keycloak.png)

### 4.1 Browser login test

A. Launch the Halo portal.
B. Get redirected to Keycloak login.

![16_Keycloak](/.attachments/16_keycloak.png)

C. Sign in to your account.

![17_Keycloak](/.attachments/17_keycloak.png)

D. Return to Halo after being authenticated.

![18_Keycloak](/.attachments/18_keycloak.png)

---

## Step 5 - Troubleshooting

### Misconfigured redirect URI

Check URIs match between portal and Keycloak.

### Clock skew

Sync system clocks.

### TLS trust errors

Ensure Halo trusts Keycloak’s cert.

---

## References

- [Keycloak documentation](https://www.keycloak.org/documentation)