Files
pangolin/.tasks/artifacts/guacamole-research.md
Olaf b428721b07 Initial commit: cleaned project structure
- Consolidated documentation from Ralph Loop iterations
- Archived 20+ outdated/superseded files to .archive/
- Kept essential docs: OIDC integration, mobile setup, quick start
- Added operational scripts for health monitoring and backup
- Research artifacts preserved in .tasks/artifacts/

Current state:
- 3 VPS sites (fry, proton, photon) ONLINE in Pangolin
- brn-home site pending for local services (Jellyfin, etc.)
- Mobile access configuration pending

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 06:15:04 +00:00

445 lines
16 KiB
Markdown

# Apache Guacamole OIDC Integration with Authentik - Research Report
**Research Task:** RESEARCH-003
**Date:** 2026-01-20
**Objective:** Comprehensive research on Apache Guacamole OIDC integration with Authentik
---
## 1. Complete List of OPENID_ Environment Variables
### Required Environment Variables
For Docker installations of Apache Guacamole, the following environment variables are **required**:
| Variable | Description | Example Value |
|----------|-------------|---------------|
| `OPENID_AUTHORIZATION_ENDPOINT` | Authorization endpoint URI from IdP | `https://sso.obr.sh/application/o/authorize/` |
| `OPENID_JWKS_ENDPOINT` | JWKS endpoint for validating ID tokens (JWTs) | `https://sso.obr.sh/application/o/<slug>/jwks/` |
| `OPENID_ISSUER` | Expected issuer for all received ID tokens | `https://sso.obr.sh/application/o/<slug>/` |
| `OPENID_CLIENT_ID` | OpenID client ID for your application | From Authentik provider configuration |
| `OPENID_REDIRECT_URI` | Full callback URL for Guacamole | `https://remote.obr.sh/guacamole/api/auth/openid-connect/callback` |
### Optional Environment Variables
| Variable | Description | Default Value |
|----------|-------------|---------------|
| `OPENID_ENABLED` | Explicitly enable the extension | `true` (or set any OPENID_ variable) |
| `OPENID_USERNAME_CLAIM_TYPE` | JWT claim for username | `email` |
| `OPENID_GROUPS_CLAIM_TYPE` | JWT claim for groups | `groups` |
| `OPENID_ATTRIBUTES_CLAIM_TYPE` | Comma-separated list of claims to expose as `OIDC_*` attributes | (empty) |
| `OPENID_SCOPE` | OpenID scopes to request | `openid email profile` |
| `OPENID_ALLOWED_CLOCK_SKEW` | Clock skew tolerance for token validation | `30` (seconds) |
| `OPENID_MAX_TOKEN_VALIDITY` | Maximum token validity period | `300` (minutes/5 hours) |
| `OPENID_MAX_NONCE_VALIDITY` | Maximum nonce validity period | `10` (minutes) |
### Extension Loading Control
| Variable | Description | Effect |
|----------|-------------|--------|
| `EXTENSION_PRIORITY` | Controls authentication flow | `openid` = auto-redirect to IdP |
| | | `*,openid` = show login screen first |
### Additional Configuration Requirement
For standalone installations (non-Docker), you must enable environment property evaluation:
```properties
# In /etc/guacamole/guacamole.properties
enable-environment-properties: true
```
---
## 2. Connection-Level Authentication Flow
### User Authentication vs. Connection Authentication
**Critical Finding:** OIDC in Guacamole handles **user authentication** to the Guacamole web interface, **NOT connection-level authentication** to RDP/VNC/SSH hosts.
### How It Works
1. **User logs into Guacamole** via OIDC (SSO to Authentik)
2. **Guacamole creates a session** for the authenticated user
3. **Connections are accessed** based on permissions stored in the database extension (JDBC)
4. **Target system authentication** happens separately using configured credentials
### The Credential Passthrough Problem
#### With LDAP Authentication (Works)
```
User → LDAP login → Guacamole stores credentials → RDP connection
${GUAC_USERNAME} and ${GUAC_PASSWORD} available
```
#### With OIDC Authentication (Does NOT Work)
```
User → OIDC login → Guacamole gets token only → RDP connection
${GUAC_PASSWORD} is NOT available (password never sent to Guacamole)
```
### Parameter Tokens Available
| Token | LDAP Auth | OIDC Auth | Notes |
|-------|-----------|-----------|-------|
| `${GUAC_USERNAME}` | ✓ Available | ✓ Available | Username from claim |
| `${GUAC_PASSWORD}` | ✓ Available | ✗ NOT Available | Password never leaves IdP |
### OIDC Attribute Passthrough
The `OPENID_ATTRIBUTES_CLAIM_TYPE` variable allows exposing JWT claims as `OIDC_*` parameters:
```bash
# Example: Expose custom claims
OPENID_ATTRIBUTES_CLAIM_TYPE=workstationName,department,employeeId
```
This creates connection parameters:
- `${OIDC_workstationName}`
- `${OIDC_department}`
- `${OIDC_employeeId}`
**Use Case:** Dynamic connection routing based on user attributes, NOT credential passthrough.
---
## 3. MFA Flow with TOTP Passthrough
### Guacamole's TOTP Support
Apache Guacamole supports **TOTP (Time-based One-Time Password)** as a second authentication factor.
### TOTP Authentication Flow
```
┌─────────────────────────────────────────────────────────────┐
│ 1. User attempts to log in │
│ ↓ │
│ 2. Primary authentication (OIDC/LDAP/Database) │
│ ↓ │
│ 3. If successful, TOTP extension checks for enrolled device │
│ ├─ Device enrolled: Prompt for TOTP code │
│ └─ No device: Force enrollment (QR code scan) │
│ ↓ │
│ 4. User enters 6-digit code from authenticator app │
│ ↓ │
│ 5. If code validates: Grant access │
│ └─ If code fails: Deny access │
└─────────────────────────────────────────────────────────────┘
```
### TOTP Extension Configuration
**Extension File:** `guacamole-auth-totp-*.jar`
**Environment Variables (Docker):**
| Variable | Description | Default |
|----------|-------------|---------|
| `TOTP_ISSUER` | Issuer name shown in authenticator app | `Apache Guacamole` |
| `TOTP_DIGITS` | Number of digits in TOTP code | `6` |
| `TOTP_PERIOD` | Time period for code validity | `30` (seconds) |
| `TOTP_MODE` | TOTP generation mode | `sha1` |
### Layered Authentication: OIDC + TOTP
**Prerequisites:**
- Database authentication extension (JDBC) installed for key storage
- OIDC extension for primary authentication
- TOTP extension for second factor
**Flow:**
1. User → Authentik (OIDC with MFA at IdP level) → Guacamole login
2. Guacamole → TOTP challenge (separate from Authentik MFA)
3. User → Enters TOTP code → Access granted
### Important Limitation: Duo vs. TOTP with SSO
**From official documentation:**
> "Guacamole's Duo support cannot currently be used alongside single sign-on (SSO). If you need both MFA and SSO, you must use your SSO provider's own Duo integration or use TOTP instead of Duo."
### MFA Architecture Options
#### Option A: MFA at IdP Level (Authentik)
```
User → Authentik (with TOTP/WebAuthn) → Guacamole
Pros: Single MFA enforcement point, better UX
Cons: No Guacamole-level MFA control
```
#### Option B: MFA at Guacamole Level
```
User → Authentik (no MFA) → Guacamole TOTP → Access
Pros: Guacamole controls second factor
Cons: Users authenticate twice (SSO + TOTP)
```
#### Option C: Dual MFA (Most Secure)
```
User → Authentik (with MFA) → Guacamole TOTP → Access
Pros: Defense in depth, two independent factors
Cons: Potential user friction
```
---
## 4. RDP NLA Compatibility with SSO
### What is RDP NLA?
**Network Level Authentication (NLA)** is a security feature in RDP that requires authentication **before** establishing a full remote desktop session.
### The Fundamental Incompatibility
**Critical Finding:** OIDC SSO and RDP NLA have a credential passthrough problem.
#### Why It Doesn't Work
```
┌──────────────────────────────────────────────────────────┐
│ OIDC Flow (No Password to Guacamole) │
├──────────────────────────────────────────────────────────┤
│ User → Authentik → JWT Token → Guacamole │
│ │
│ RDP NLA Requirement (Needs Actual Password) │
│ │
│ Guacamole → Windows Server (NLA) → ??? No password ??? │
└──────────────────────────────────────────────────────────┘
```
**The Problem:**
- OIDC authentication means the user's password **never reaches Guacamole**
- RDP NLA requires a username/password for NTLM or Kerberos authentication
- `${GUAC_PASSWORD}` token is **empty** when using OIDC
### Confirmed by Community Evidence
**From mailing list discussions:**
> "When using OpenID Connect (or SAML, or CAS without the ClearPass extension) for Guacamole login, the user's password is not shared between the Identity Provider (IdP) and Guacamole, meaning the `${GUAC_PASSWORD}` variable will not be available for use in RDP connections."
### Current Status of FreeRDP NLA Support
Guacamole uses **FreeRDP** for RDP connections. Key limitations:
1. **NTLM-only NLA:** FreeRDP currently only supports NTLM variant of NLA
2. **No Kerberos NLA:** Kerberos NLA support is in development
3. **Protected Users Group:** Users in AD "Protected Users" group cannot authenticate via NTLM NLA
### Workarounds and Solutions
#### Solution 1: Disable NLA on Target Systems
```
Pros: Simple, works immediately
Cons: Reduced security, Microsoft discourages this
```
#### Solution 2: Credential Prompting
```
Leave RDP username/password blank → User prompted during connection
Pros: Works with OIDC
Cons: Users log in twice (SSO + RDP prompt)
```
#### Solution 3: Pre-configured Shared Accounts
```
Store RDP credentials in Guacamole connection config
Pros: Single sign-on UX maintained
Cons: Shared accounts, loses per-user audit trail
```
#### Solution 4: Use LDAP Instead of OIDC (Not Recommended)
```
LDAP auth → ${GUAC_PASSWORD} available → RDP NLA works
Pros: Credential passthrough works
Cons: Loses SSO benefits, security regression
```
#### Solution 5: Custom Authentication Extension
**Community Solution:** `guacamole-auth-header-password`
```bash
# Example: Pass credentials via HTTP headers
http-username-header=OIDC_REMOTE_USER
http-password-header=OIDC_access_token # Not a real password
http-groups-header=OIDC_CLAIM_sdDesktopProjects
```
**Note:** This doesn't solve the password problem but shows extension architecture.
#### Solution 6: CAS with ClearPass (Alternative SSO)
```
Apache Guacamole 0.9.14+ includes CAS credential pass-through
using "ClearPass" which allows ${GUAC_USERNAME} and ${GUAC_PASSWORD}
tokens with SSO
```
**Requires:** CAS server instead of OIDC
### Azure AD / Entra Joined Systems
**Additional Complexity:**
- Azure AD Joined (Entra-joined) systems may require web-based authentication
- Guacamole cannot pass through the web sign-in mechanism
- **Hybrid joined** (Azure AD + Domain joined) systems work better
**From community reports:**
> "Hybrid joined systems with NLA enabled are working fine with Guacamole/RDP"
### Recommended Approach for SSO + RDP NLA
**Best Practice Architecture:**
1. **Use OIDC for Guacamole authentication** (SSO to Authentik)
2. **Configure RDP connections** with one of:
- **Option A:** Dedicated service accounts (stored in connection config)
- **Option B:** Prompt users for RDP credentials (UX compromise)
- **Option C:** Disable NLA on internal trusted network segments only
3. **Implement connection-level access control** via Authentik groups
4. **Enable audit logging** to track connection access (even with shared accounts)
---
## 5. Authentik-Specific Configuration Notes
### Authentik Provider Setup
**Provider Type:** OAuth2/OpenID Connect
**Client Type:** Confidential
**Scopes:** `openid`, `email`, `profile`
### Critical Authentik Settings
1. **Redirect URI:**
```
https://remote.obr.sh/guacamole/api/auth/oidc/callback
```
2. **Token Validity:**
```
Max 300 minutes (5 hours) - Guacamole limitation
```
3. **Signing Key:**
```
authentik Self-signed Certificate
```
### Username Claim Configuration
**Two Options:**
#### Option A: Use `sub` (Recommended)
```bash
OPENID_USERNAME_CLAIM_TYPE=sub
```
- Pros: Immutable, unique identifier
- Cons: Not human-readable (UUIDs)
- **Important:** Disable "Allow users to change username" in Authentik
#### Option B: Use `preferred_username`
```bash
OPENID_USERNAME_CLAIM_TYPE=preferred_username
```
- Pros: Human-readable usernames
- Cons: May change if user updates profile
- **Requires:** Custom property mapping in Authentik
### Custom Property Mapping for Usernames
**In Authentik:** Create Scope Mapping to expose custom username attribute
```python
# Example: Map custom field to preferred_username claim
{
"preferred_username": user.attributes.get("guacamole_username", user.username)
}
```
### Pre-creating Admin User
**Best Practice:** Create admin user in Guacamole database **before** enabling OIDC
```sql
-- Insert user matching Authentik username
INSERT INTO guacamole_entity (name, type)
VALUES ('admin@example.com', 'USER');
-- Grant admin permissions
INSERT INTO guacamole_user_permission (entity_id, permission)
VALUES ((SELECT entity_id FROM guacamole_entity WHERE name='admin@example.com'), 'ADMINISTER');
```
---
## 6. Key Takeaways and Recommendations
### Confirmed Capabilities
✓ OIDC authentication to Guacamole web interface works well with Authentik
✓ Username passthrough (`${GUAC_USERNAME}`) available with OIDC
✓ Custom JWT claims can be exposed as `${OIDC_*}` parameters
✓ TOTP MFA can be layered on top of OIDC authentication
✓ Groups from Authentik can control connection access
### Critical Limitations
✗ Password passthrough (`${GUAC_PASSWORD}`) does NOT work with OIDC
✗ RDP NLA requires passwords, creating incompatibility with OIDC SSO
✗ No per-connection SSO (OIDC only authenticates to Guacamole, not target systems)
✗ Duo MFA cannot be used with SSO (use TOTP instead)
### Recommended Architecture
**For Pangolin/Authentik/Guacamole Integration:**
1. **Primary Authentication:** OIDC to Authentik (SSO)
2. **MFA Strategy:** MFA at Authentik level (TOTP/WebAuthn)
3. **RDP Authentication:**
- Service accounts for automated access
- User prompting for per-user audit requirements
- NLA disabled on trusted internal segments (if acceptable)
4. **Database Extension:** Required for connection storage and TOTP keys
5. **Group Mapping:** Use `OPENID_GROUPS_CLAIM_TYPE=groups` to control access
### Environment Variable Template for Pangolin
```bash
# Authentik OIDC Integration
OPENID_AUTHORIZATION_ENDPOINT=https://sso.obr.sh/application/o/authorize/
OPENID_JWKS_ENDPOINT=https://sso.obr.sh/application/o/guacamole/jwks/
OPENID_ISSUER=https://sso.obr.sh/application/o/guacamole/
OPENID_CLIENT_ID=<from_authentik_provider>
OPENID_REDIRECT_URI=https://remote.obr.sh/guacamole/api/auth/oidc/callback
# Claim Configuration
OPENID_USERNAME_CLAIM_TYPE=preferred_username
OPENID_GROUPS_CLAIM_TYPE=groups
OPENID_SCOPE=openid email profile
# Token Settings
OPENID_MAX_TOKEN_VALIDITY=300
OPENID_ALLOWED_CLOCK_SKEW=30
# Extension Loading
EXTENSION_PRIORITY=openid
OPENID_ENABLED=true
# Enable environment variables in guacamole.properties
enable-environment-properties: true
```
---
## References
- Apache Guacamole Official Documentation: https://guacamole.apache.org/doc/gug/openid-auth.html
- Authentik Integration Guide: https://docs.goauthentik.io/integrations/services/apache-guacamole/
- Guacamole Mailing List Archives: https://www.mail-archive.com/user@guacamole.apache.org/
- FreeRDP NLA Limitations: Community reports on NTLM vs Kerberos support
- TOTP Extension Guide: https://guacamole.apache.org/doc/gug/totp-auth.html
---
**Research Completed:** 2026-01-20
**Task:** RESEARCH-003
**Status:** Complete