Red Hat Identity header authentication for deployments behind Red Hat Hybrid
Cloud Console infrastructure (e.g., console.redhat.com, Insights). This module
validates the x-rh-identity header provided by Red Hat's authentication proxy.
The rh-identity module:
- Extracts the
x-rh-identityheader from incoming requests - Base64 decodes and parses the JSON payload
- Validates the identity structure based on type (User or System)
- Optionally validates service entitlements
- Extracts user identity for downstream use
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────────┐
│ Client/RHEL │──────▶ Red Hat Auth │──────▶ Lightspeed Stack │
│ System │ │ Proxy │ │ (rh-identity) │
└─────────────────┘ └──────────────────┘ └─────────────────────┘
│ │
│ Adds x-rh-identity │ Validates header
│ header to request │ Extracts identity
▼ ▼
The authentication proxy (part of Red Hat's infrastructure) authenticates users
via SSO or systems via certificate authentication, then injects the
x-rh-identity header containing the verified identity information.
authentication:
module: rh-identityauthentication:
module: rh-identity
rh_identity_config:
required_entitlements:
- rhel
- insights| Option | Required | Default | Description |
|---|---|---|---|
required_entitlements |
No | [] |
List of service entitlements to require |
When required_entitlements is configured, ALL listed entitlements must be
present and entitled in the identity header. Omit this field to disable
entitlement validation entirely.
The x-rh-identity header supports two identity types, each with different
structure and use cases.
Console users authenticated via Red Hat SSO. Used when humans access services through the Hybrid Cloud Console.
Identity extraction:
user_id: Fromidentity.user.user_idusername: Fromidentity.user.username
Header structure:
{
"identity": {
"account_number": "123456",
"org_id": "654321",
"type": "User",
"user": {
"user_id": "abc123",
"username": "user@example.com",
"is_org_admin": false,
"is_internal": false,
"locale": "en_US"
}
},
"entitlements": {
"rhel": {"is_entitled": true, "is_trial": false},
"insights": {"is_entitled": true, "is_trial": false},
"ansible": {"is_entitled": false, "is_trial": false}
}
}Available User fields:
| Field | Type | Description |
|---|---|---|
user_id |
string | Unique user identifier |
username |
string | User's email or username |
is_org_admin |
boolean | Whether user is an organization admin |
is_internal |
boolean | Whether user is a Red Hat internal user |
locale |
string | User's locale preference |
Certificate-authenticated RHEL systems. Used when RHEL hosts access services directly (e.g., Insights client, subscription-manager).
Identity extraction:
user_id: Fromidentity.system.cn(certificate Common Name)username: Fromidentity.account_number
Header structure:
{
"identity": {
"account_number": "123456",
"org_id": "654321",
"type": "System",
"system": {
"cn": "c87dcb4c-8af1-40dd-878e-60c744edddd0",
"cert_type": "system"
}
},
"entitlements": {
"rhel": {"is_entitled": true, "is_trial": false}
}
}Available System fields:
| Field | Type | Description |
|---|---|---|
cn |
string | Certificate Common Name (system UUID) |
cert_type |
string | Certificate type (usually "system") |
Both identity types share these top-level fields:
| Field | Type | Description |
|---|---|---|
account_number |
string | Red Hat account number |
org_id |
string | Organization ID |
type |
string | Identity type: "User" or "System" |
Entitlements represent service subscriptions. Each entitlement has:
| Field | Type | Description |
|---|---|---|
is_entitled |
boolean | Whether the account has this entitlement |
is_trial |
boolean | Whether this is a trial entitlement |
Common entitlement names:
rhel- Red Hat Enterprise Linuxinsights- Red Hat Insightsansible- Ansible Automation Platformopenshift- OpenShift Container Platform
The module stores the parsed identity data in request.state.rh_identity_data
for downstream access:
from fastapi import Request
async def my_endpoint(request: Request):
rh_identity = request.state.rh_identity_data
# Get organization ID
org_id = rh_identity.get_org_id()
# Check specific entitlement
has_insights = rh_identity.has_entitlement("insights")
# Get user info
user_id = rh_identity.get_user_id()
username = rh_identity.get_username()| Method | Returns | Description |
|---|---|---|
get_user_id() |
str |
User ID or system CN |
get_username() |
str |
Username or account number |
get_org_id() |
str |
Organization ID |
has_entitlement(service) |
bool |
Check single entitlement |
has_entitlements(services) |
bool |
Check ALL entitlements in list |
# User identity
IDENTITY='{
"identity": {
"account_number": "123456",
"org_id": "654321",
"type": "User",
"user": {
"user_id": "test-user-id",
"username": "testuser@example.com"
}
},
"entitlements": {
"rhel": {"is_entitled": true, "is_trial": false}
}
}'
# Base64 encode
HEADER=$(echo -n "$IDENTITY" | base64)
# Make request
curl http://localhost:8080/v1/query \
-H "Content-Type: application/json" \
-H "x-rh-identity: $HEADER" \
-d '{"query": "Hello"}'# System identity
IDENTITY='{
"identity": {
"account_number": "123456",
"org_id": "654321",
"type": "System",
"system": {
"cn": "c87dcb4c-8af1-40dd-878e-60c744edddd0"
}
},
"entitlements": {
"rhel": {"is_entitled": true, "is_trial": false}
}
}'
HEADER=$(echo -n "$IDENTITY" | base64)
curl http://localhost:8080/v1/query \
-H "Content-Type: application/json" \
-H "x-rh-identity: $HEADER" \
-d '{"query": "Hello"}'| Status | Condition | Response |
|---|---|---|
| 401 | Missing x-rh-identity header |
{"detail": "Missing x-rh-identity header"} |
| 400 | Invalid base64 encoding | {"detail": "Invalid base64 encoding in x-rh-identity header"} |
| 400 | Invalid JSON | {"detail": "Invalid JSON in x-rh-identity header"} |
| 400 | Missing identity field |
{"detail": "Missing 'identity' field"} |
| 400 | Missing identity type |
{"detail": "Missing identity 'type' field"} |
| 400 | Missing user for User type |
{"detail": "Missing 'user' field for User type"} |
| 400 | Missing user_id in user |
{"detail": "Missing 'user_id' in user data"} |
| 400 | Missing username in user |
{"detail": "Missing 'username' in user data"} |
| 400 | Missing system for System type |
{"detail": "Missing 'system' field for System type"} |
| 400 | Missing cn in system |
{"detail": "Missing 'cn' in system data"} |
| 400 | Missing account_number for System |
{"detail": "Missing 'account_number' for System type"} |
| 400 | Unsupported identity type | {"detail": "Unsupported identity type: X"} |
| 403 | Missing required entitlements | {"detail": "Missing required entitlement: rhel"} |
When deployed behind console.redhat.com or similar Red Hat infrastructure:
- The authentication proxy handles all authentication
- The
x-rh-identityheader is automatically injected - Configure Lightspeed Stack to trust this header
- Never expose directly to the internet - The
x-rh-identityheader can be forged. Always deploy behind Red Hat's authentication proxy. - Validate entitlements - Use
required_entitlementsto ensure only authorized accounts can access the service. - Log audit events - The extracted user/system identity should be logged for audit purposes.
For local testing without the authentication proxy, you can manually create
headers using the examples above. Consider using noop module instead for
simpler local development.
| Issue | Cause | Solution |
|---|---|---|
| 401 errors | Header not being forwarded | Check proxy/ingress configuration |
| 400 errors | Malformed header | Validate JSON structure, check base64 encoding |
| 403 errors | Missing entitlements | Verify account has required subscriptions |
| Wrong user ID | Using System identity | Check identity type in header |
Enable debug logging to see identity extraction:
service:
color_log: true
log_level: DEBUGLook for log entries like:
RH Identity authenticated: user_id=abc123, username=user@example.com