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>
This commit is contained in:
444
.tasks/artifacts/guacamole-research.md
Normal file
444
.tasks/artifacts/guacamole-research.md
Normal file
@@ -0,0 +1,444 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user