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:
525
.tasks/artifacts/architecture-validation.md
Normal file
525
.tasks/artifacts/architecture-validation.md
Normal file
@@ -0,0 +1,525 @@
|
||||
# Architecture Validation: Authentik + Pangolin + Guacamole
|
||||
|
||||
**Validation Date:** 2026-01-20
|
||||
**Purpose:** Review proposed SSO infrastructure architecture for multi-site deployment
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
**VERDICT:** ✅ **APPROVED WITH CRITICAL MODIFICATIONS**
|
||||
|
||||
The proposed architecture (Authentik + Pangolin + Guacamole) is sound for your use case with **one critical exception**: the Guacamole/RDP integration has fundamental limitations that require architectural workarounds.
|
||||
|
||||
### Key Findings
|
||||
|
||||
| Component | Status | Confidence | Notes |
|
||||
|-----------|--------|------------|-------|
|
||||
| **Authentik** | ✅ RECOMMENDED | High | Best choice for self-hosted SSO in 2026 |
|
||||
| **Pangolin** | ✅ RECOMMENDED | High | Superior to Cloudflare Tunnel for self-hosted |
|
||||
| **Guacamole + OIDC** | ⚠️ APPROVED WITH CAVEATS | Medium | RDP NLA incompatibility requires workarounds |
|
||||
|
||||
---
|
||||
|
||||
## 1. Authentik Validation
|
||||
|
||||
### Research Findings
|
||||
|
||||
**Market Position (2026):**
|
||||
- Authentik has emerged as the **leading modern SSO solution** for self-hosted environments
|
||||
- Superior to Keycloak for small/medium deployments (lower complexity, better UX)
|
||||
- Superior to Authelia (full IdP vs just forward auth)
|
||||
- MIT licensed, active development, 19.6k GitHub stars
|
||||
|
||||
**Key Strengths:**
|
||||
- **Modern architecture:** Written in Python (Django), not Java like Keycloak
|
||||
- **Lower resource requirements:** Documented to run well with 2GB RAM total
|
||||
- **Better UX:** Admin interface significantly easier than Keycloak
|
||||
- **Full protocol support:** OIDC, OAuth2, SAML2, LDAP, RADIUS
|
||||
- **Native MFA:** TOTP, WebAuthn, Duo, all built-in
|
||||
- **Expression policies:** Powerful Python-based policy engine
|
||||
|
||||
**For Your Use Case:**
|
||||
- ✅ Single-user deployment supported (minimal resource config documented)
|
||||
- ✅ Service account support for API tokens (Jellyfin mobile apps)
|
||||
- ✅ MFA enforcement per-application (can require for Guacamole only)
|
||||
- ✅ Proven integration with Guacamole, Jellyfin SSO plugin, OpenWebUI
|
||||
- ✅ Active documentation for Pangolin integration
|
||||
|
||||
**Alternatives Considered:**
|
||||
- **Keycloak:** Overkill for single-user, 4GB+ RAM, steeper learning curve
|
||||
- **Authelia:** Limited to forward auth, no full OIDC provider capabilities
|
||||
- **Zitadel:** Newer, less proven integrations
|
||||
|
||||
**RECOMMENDATION:** ✅ **Use Authentik as proposed**
|
||||
|
||||
---
|
||||
|
||||
## 2. Pangolin Validation
|
||||
|
||||
### Research Findings
|
||||
|
||||
**Market Position (2026):**
|
||||
- Pangolin is the **leading self-hosted alternative to Cloudflare Tunnel**
|
||||
- Open-source (fosrl/pangolin, 18.2k GitHub stars)
|
||||
- Built on proven tech: WireGuard + Traefik reverse proxy
|
||||
- Active community, recently featured in major tech channels (Christian Lempa, NetworkChuck)
|
||||
|
||||
**Key Strengths:**
|
||||
- **Self-hosted control plane:** You own all infrastructure, no third-party dependencies
|
||||
- **Identity-aware access control:** Native OIDC integration with Authentik
|
||||
- **Dual mode:** Tunneled reverse proxy + VPN-style private resource access
|
||||
- **No inbound ports required:** WireGuard outbound tunnels from private networks
|
||||
- **Automatic SSL:** Let's Encrypt integration via Traefik
|
||||
- **Mobile support:** Native apps + WireGuard config export
|
||||
|
||||
**Architecture Components:**
|
||||
1. **Pangolin (Control Plane):** Dashboard, API, WebSocket server, auth system
|
||||
2. **Gerbil (Tunnel Manager):** WireGuard interface management
|
||||
3. **Newt (Edge Client):** Runs on private networks (brn, VPS hosts)
|
||||
4. **Traefik (Reverse Proxy):** TLS termination, routing, load balancing
|
||||
5. **Badger (Auth Middleware):** OIDC authentication enforcement
|
||||
|
||||
**For Your Use Case:**
|
||||
- ✅ **Replaces WireGuard mesh:** Current 10.51.0.0/24 network becomes Pangolin sites
|
||||
- ✅ **Centralized on brn:** Control plane on physically secure host
|
||||
- ✅ **VPS integration:** Newt clients on fry, proton, photon for site-to-site routing
|
||||
- ✅ **Mobile access:** Apps for pixel9pro, pixel6pro
|
||||
- ✅ **Granular ACLs:** Per-service, per-user access control via Authentik
|
||||
|
||||
**Comparison to Alternatives:**
|
||||
|
||||
| Solution | Ownership | Cost | Mobile | OIDC | Complexity |
|
||||
|----------|-----------|------|--------|------|------------|
|
||||
| **Pangolin** | Self-hosted | Free | ✅ | ✅ | Medium |
|
||||
| Cloudflare Tunnel | Cloudflare | Free | ⚠️ Limited | ✅ | Low |
|
||||
| Tailscale | Tailscale | $5/user | ✅ | ⚠️ Enterprise | Low |
|
||||
| Headscale | Self-hosted | Free | ✅ | ❌ | Medium |
|
||||
|
||||
**Critical Findings:**
|
||||
- ✅ **OIDC redirect URI:** `https://tunnel.obr.sh/api/v1/auth/callback`
|
||||
- ✅ **Required scopes:** openid, profile, email, groups
|
||||
- ✅ **Site architecture:** Each location (brn LAN, fry, proton) becomes a "Site"
|
||||
- ✅ **Resource types:** Public (HTTPS with domains) + Private (TCP/UDP for VPN access)
|
||||
|
||||
**RECOMMENDATION:** ✅ **Use Pangolin as proposed**
|
||||
|
||||
---
|
||||
|
||||
## 3. Guacamole Validation
|
||||
|
||||
### Research Findings
|
||||
|
||||
**Market Position (2026):**
|
||||
- Apache Guacamole remains the **leading open-source clientless RDP gateway**
|
||||
- No viable open-source alternatives with equivalent feature set
|
||||
- Active Apache project, version 1.6.0 current
|
||||
|
||||
**OIDC Support:**
|
||||
- ✅ Native OIDC extension available
|
||||
- ✅ Documented Authentik integration guide
|
||||
- ✅ Works well for **authentication to Guacamole dashboard**
|
||||
|
||||
### ⚠️ CRITICAL LIMITATION DISCOVERED
|
||||
|
||||
**RDP NLA + OIDC Incompatibility:**
|
||||
|
||||
The research uncovered a **fundamental architectural limitation**:
|
||||
|
||||
**Problem:**
|
||||
1. **RDP Network Level Authentication (NLA)** requires username/password for NTLM/Kerberos authentication
|
||||
2. **OIDC authentication** never provides the user's password to Guacamole
|
||||
3. Variables available: `${GUAC_USERNAME}` ✅, `${GUAC_PASSWORD}` ❌
|
||||
4. **Result:** Cannot use NLA with OIDC authentication
|
||||
|
||||
**Security Implications:**
|
||||
- **NLA is recommended security best practice** for RDP (encrypts credentials before RDP connection)
|
||||
- **Disabling NLA** exposes credentials during connection handshake
|
||||
- **Windows 11** (argon) defaults to requiring NLA
|
||||
|
||||
**Workarounds Available:**
|
||||
|
||||
| Option | Security | User Experience | Implementation |
|
||||
|--------|----------|-----------------|----------------|
|
||||
| **1. Disable NLA** | ⚠️ Lower | Seamless SSO | Easy - disable in Guacamole connection config |
|
||||
| **2. Prompt for credentials** | ✅ High | Double login | Medium - configure in Guacamole |
|
||||
| **3. Service account** | ⚠️ Medium | Seamless SSO | Easy - hardcode credentials, lose audit trail |
|
||||
| **4. Use CAS instead of OIDC** | ✅ High | Seamless SSO | Hard - requires ClearPass Receiver on Windows |
|
||||
|
||||
### Recommended Approach for Your Deployment
|
||||
|
||||
**Since this is single-user (you) accessing your own workstation (argon):**
|
||||
|
||||
**RECOMMENDED:** **Option 1 - Disable NLA**
|
||||
|
||||
**Rationale:**
|
||||
- Low risk: You're the only user, accessing your own machine
|
||||
- Network already secured: Guacamole only accessible via Pangolin tunnel + Authentik SSO + MFA
|
||||
- User experience: Best (seamless SSO with TOTP)
|
||||
- Defense in depth: Multiple layers (MFA on Authentik, network isolation via Pangolin)
|
||||
|
||||
**Implementation:**
|
||||
```yaml
|
||||
# In Guacamole connection config for argon-rdp:
|
||||
security: rdp # Use standard RDP security instead of NLA
|
||||
ignore-cert: true # Accept self-signed certs
|
||||
```
|
||||
|
||||
**Additional Security Mitigations:**
|
||||
1. ✅ Enforce MFA on Guacamole application in Authentik (TOTP required)
|
||||
2. ✅ Restrict Guacamole to Pangolin tunnel only (no public WAN access)
|
||||
3. ✅ Enable Guacamole session recording for audit trail
|
||||
4. ✅ Configure Windows Firewall on argon to only allow RDP from brn (10.50.0.74)
|
||||
|
||||
**Alternative for Future Multi-User:**
|
||||
If you later add users, switch to **Option 2 (prompt for credentials)** to maintain per-user accountability.
|
||||
|
||||
**RECOMMENDATION:** ✅ **Use Guacamole with NLA disabled, compensated by MFA + Pangolin isolation**
|
||||
|
||||
---
|
||||
|
||||
## 4. Service Integration Validation
|
||||
|
||||
### Jellyfin SSO
|
||||
|
||||
**Status:** ✅ **FULLY SUPPORTED**
|
||||
|
||||
**Plugin:** SSO-Auth plugin from Jellyfin catalog
|
||||
|
||||
**Key Findings:**
|
||||
- ✅ Authentik integration well-documented
|
||||
- ⚠️ **Critical:** Mobile apps (Android/iOS) have limited OIDC support
|
||||
- ✅ **Solution:** Use "Quick Connect" feature for mobile (6-digit code pairing)
|
||||
- ✅ Alternative: API tokens for dedicated devices
|
||||
|
||||
**Configuration:**
|
||||
- Provider type: Generic OpenID
|
||||
- Client auth: `client_secret_post` (NOT `client_secret_basic`)
|
||||
- Claims: roles via `groups` claim
|
||||
- Scopes: openid, profile, email, groups
|
||||
|
||||
**Mobile App Strategy:**
|
||||
1. **Primary:** Quick Connect (user logs in via web SSO, enters code in app)
|
||||
2. **Secondary:** API tokens per device (generated in Jellyfin dashboard)
|
||||
|
||||
---
|
||||
|
||||
### OpenWebUI SSO
|
||||
|
||||
**Status:** ✅ **FULLY SUPPORTED**
|
||||
|
||||
**Native OIDC:** No plugin required
|
||||
|
||||
**Key Findings:**
|
||||
- ✅ Robust OIDC implementation since v0.7.1+
|
||||
- ✅ **Role-based admin designation** via `OAUTH_ADMIN_ROLES`
|
||||
- ✅ JIT group provisioning with `ENABLE_OAUTH_GROUP_CREATION`
|
||||
- ✅ Automatic role synchronization on every login
|
||||
|
||||
**Configuration Variables:**
|
||||
```bash
|
||||
OPENID_PROVIDER_URL=https://sso.obr.sh/application/o/openwebui/.well-known/openid-configuration
|
||||
OAUTH_CLIENT_ID=<from_authentik>
|
||||
OAUTH_CLIENT_SECRET=<from_authentik>
|
||||
ENABLE_OAUTH_ROLE_MANAGEMENT=true
|
||||
OAUTH_ROLES_CLAIM=groups
|
||||
OAUTH_ADMIN_ROLES=openwebui-admins
|
||||
```
|
||||
|
||||
**Redirect URI:** `https://ll.obr.sh/oauth/oidc/callback`
|
||||
|
||||
---
|
||||
|
||||
### Gitea SSO (fry + proton)
|
||||
|
||||
**Status:** ✅ **FULLY SUPPORTED**
|
||||
|
||||
**Native OIDC:** Built-in authentication source
|
||||
|
||||
**Configuration:**
|
||||
- Type: OAuth2
|
||||
- Provider: OpenID Connect
|
||||
- Auto Discovery URL: `https://sso.obr.sh/application/o/gitea/.well-known/openid-configuration`
|
||||
- Admin role mapping: Via Authentik groups
|
||||
|
||||
**Note:** Gitea instances remain **publicly accessible** (federated nature), SSO is optional login method
|
||||
|
||||
---
|
||||
|
||||
### Transmission
|
||||
|
||||
**Status:** ⚠️ **NO SSO SUPPORT**
|
||||
|
||||
**Current:** HTTP Basic Authentication
|
||||
|
||||
**Recommendation:**
|
||||
- Keep existing basic auth
|
||||
- Protect behind Pangolin tunnel only (no public WAN access)
|
||||
- Consider forward auth middleware via Traefik if SSO required
|
||||
|
||||
---
|
||||
|
||||
### Mastodon (bern.social)
|
||||
|
||||
**Status:** ✅ **NO CHANGES NEEDED**
|
||||
|
||||
**Reason:** Public federated service, should remain publicly accessible
|
||||
|
||||
**Recommendation:** Do not integrate with SSO, keep existing authentication
|
||||
|
||||
---
|
||||
|
||||
## 5. Architectural Risks & Mitigations
|
||||
|
||||
### Risk Matrix
|
||||
|
||||
| Risk | Severity | Probability | Mitigation |
|
||||
|------|----------|-------------|------------|
|
||||
| **Authentik failure = total auth outage** | High | Low | Backup recovery codes, PostgreSQL backups, consider HA |
|
||||
| **Pangolin control plane failure** | Medium | Low | Services still accessible via LAN, failover to WireGuard |
|
||||
| **RDP NLA disabled security concern** | Medium | Medium | Compensate with MFA + network isolation |
|
||||
| **Mobile app SSO limitations (Jellyfin)** | Low | High | Use Quick Connect, document for users |
|
||||
| **DNS failure (sso.obr.sh unreachable)** | High | Low | Local /etc/hosts entries as backup |
|
||||
|
||||
### Single Points of Failure
|
||||
|
||||
**Authentik (sso.obr.sh):**
|
||||
- **Impact:** All SSO authentication fails
|
||||
- **Mitigation:**
|
||||
- Regular PostgreSQL backups (`pg_dump`)
|
||||
- Store recovery codes offline
|
||||
- Document emergency admin access procedure
|
||||
- Consider Docker volume backups
|
||||
|
||||
**Pangolin (tunnel.obr.sh):**
|
||||
- **Impact:** Mobile/remote access fails, VPS sites unreachable
|
||||
- **Mitigation:**
|
||||
- Services still accessible from LAN (Traefik routes remain)
|
||||
- Keep existing WireGuard as emergency fallback
|
||||
- Document manual WireGuard reconnection procedure
|
||||
|
||||
**brn Host (10.50.0.74):**
|
||||
- **Impact:** Total control plane failure (Authentik, Pangolin, Guacamole)
|
||||
- **Mitigation:**
|
||||
- Physical host security (already planned)
|
||||
- UPS for power stability
|
||||
- Backup restore procedure documented
|
||||
- Consider VM snapshots before changes
|
||||
|
||||
### Backup Strategy
|
||||
|
||||
**Critical Data:**
|
||||
1. **Authentik PostgreSQL database** - `pg_dump` daily, keep 7 days
|
||||
2. **Authentik media files** - `/srv/docker/authentik/media/`
|
||||
3. **Pangolin configuration** - `/srv/docker/pangolin/` database and config
|
||||
4. **Guacamole PostgreSQL database** - connection definitions
|
||||
5. **Traefik dynamic config** - `/srv/docker/traefik/traefik_dynamic.yaml`
|
||||
|
||||
**Backup Script:** See `/home/olaf/pangolin/.tasks/artifacts/backup-strategy.md` (TODO: create)
|
||||
|
||||
---
|
||||
|
||||
## 6. Alternative Architectures Considered
|
||||
|
||||
### Alternative A: Keycloak instead of Authentik
|
||||
|
||||
**Pros:**
|
||||
- More mature (13 years vs 6 years)
|
||||
- Enterprise-grade features
|
||||
- Larger community (32k stars vs 19k)
|
||||
|
||||
**Cons:**
|
||||
- Higher resource requirements (4GB+ RAM)
|
||||
- Steeper learning curve
|
||||
- Overkill for single-user deployment
|
||||
- Java-based (vs Python for Authentik)
|
||||
|
||||
**Verdict:** ❌ Rejected - unnecessary complexity for use case
|
||||
|
||||
---
|
||||
|
||||
### Alternative B: Cloudflare Tunnel instead of Pangolin
|
||||
|
||||
**Pros:**
|
||||
- Lower operational burden (managed service)
|
||||
- Global edge network
|
||||
- Built-in DDoS protection
|
||||
- Simpler setup
|
||||
|
||||
**Cons:**
|
||||
- Third-party dependency (Cloudflare controls routing)
|
||||
- Limited customization
|
||||
- No VPN-style private resource access
|
||||
- Privacy concerns (traffic visibility)
|
||||
|
||||
**Verdict:** ❌ Rejected - plan specifies self-hosted control
|
||||
|
||||
---
|
||||
|
||||
### Alternative C: Tailscale instead of Pangolin
|
||||
|
||||
**Pros:**
|
||||
- Easier setup
|
||||
- Better mobile apps
|
||||
- NAT traversal superior (DERP relays)
|
||||
|
||||
**Cons:**
|
||||
- Pricing: $5/user/month after 3 devices
|
||||
- Control plane dependency on Tailscale servers
|
||||
- Limited reverse proxy features
|
||||
- No identity-aware access control without ACL tags
|
||||
|
||||
**Verdict:** ❌ Rejected - cost and third-party dependency
|
||||
|
||||
---
|
||||
|
||||
### Alternative D: No RDP Gateway (Direct RDP)
|
||||
|
||||
**Pros:**
|
||||
- Simpler architecture
|
||||
- No NLA compatibility issues
|
||||
|
||||
**Cons:**
|
||||
- Requires RDP client installation on devices
|
||||
- No web-based access (can't use from Chromebook, iPad browser)
|
||||
- No session recording capability
|
||||
- Less secure (direct exposure vs gateway)
|
||||
|
||||
**Verdict:** ❌ Rejected - Guacamole provides superior UX and security
|
||||
|
||||
---
|
||||
|
||||
## 7. Final Recommendations
|
||||
|
||||
### ✅ APPROVED Architecture
|
||||
|
||||
**Core Components:**
|
||||
1. **Authentik** at `sso.obr.sh` - SSO/IdP
|
||||
2. **Pangolin** at `tunnel.obr.sh` - Tunneled reverse proxy
|
||||
3. **Guacamole** at `remote.obr.sh` - RDP gateway (NLA disabled)
|
||||
|
||||
### 🔧 Required Modifications to Original Plan
|
||||
|
||||
1. **Guacamole RDP Connection:**
|
||||
- Change from "NLA security" to "Standard RDP security"
|
||||
- Enable session recording for audit trail
|
||||
- Configure Windows Firewall on argon to only allow brn
|
||||
|
||||
2. **Authentik MFA Policy:**
|
||||
- Create separate policy for Guacamole application (TOTP required)
|
||||
- Optional for other services (Jellyfin, OpenWebUI) based on preference
|
||||
|
||||
3. **Jellyfin Mobile Strategy:**
|
||||
- Document Quick Connect procedure for mobile apps
|
||||
- Create API tokens for persistent devices (TV apps)
|
||||
|
||||
4. **Transmission:**
|
||||
- Keep HTTP basic auth (no OIDC support)
|
||||
- Access via Pangolin tunnel only
|
||||
|
||||
### 📋 Implementation Order Validation
|
||||
|
||||
The plan's phased approach is sound:
|
||||
|
||||
**Phase 1: Authentik** ✅
|
||||
- Foundation for all SSO
|
||||
|
||||
**Phase 2: Pangolin** ✅
|
||||
- Requires Authentik for OIDC
|
||||
|
||||
**Phase 3: Guacamole** ✅
|
||||
- Requires Authentik for OIDC
|
||||
|
||||
**Phase 4: Service Integration** ✅
|
||||
- Requires Authentik + Pangolin operational
|
||||
|
||||
**Phase 5: Traefik Restriction** ✅
|
||||
- Only after Pangolin sites verified working
|
||||
|
||||
**Phase 6: Mobile Setup** ✅
|
||||
- Final verification step
|
||||
|
||||
**Order is correct:** Sequential dependencies respected
|
||||
|
||||
### 🎯 Success Criteria
|
||||
|
||||
**Deployment successful when:**
|
||||
1. ✅ Can login to Authentik admin via `sso.obr.sh`
|
||||
2. ✅ Can login to Pangolin dashboard via `tunnel.obr.sh` (SSO redirect)
|
||||
3. ✅ Can access Guacamole via `remote.obr.sh` (SSO + MFA)
|
||||
4. ✅ Can connect to argon RDP via Guacamole web interface
|
||||
5. ✅ Can access Jellyfin via Pangolin mobile app (with Quick Connect)
|
||||
6. ✅ Can access OpenWebUI via Pangolin tunnel (SSO login)
|
||||
7. ✅ Jellyfin/OpenWebUI/Transmission return 404 from public WAN
|
||||
8. ✅ VPS hosts (fry, proton) show connected in Pangolin dashboard
|
||||
|
||||
### ⚠️ Rollback Plan
|
||||
|
||||
**Critical checkpoints:**
|
||||
1. After TASK-005 (Authentik deploy): Services still work without SSO
|
||||
2. After TASK-009 (Pangolin sites): Traefik routes still public
|
||||
3. **After TASK-024 (Traefik restriction): CRITICAL CHECKPOINT**
|
||||
|
||||
**Rollback procedure:**
|
||||
```bash
|
||||
# Emergency: restore public access
|
||||
sudo cp /home/olaf/pangolin/.tasks/artifacts/traefik_dynamic.yaml.backup \
|
||||
/srv/docker/traefik/traefik_dynamic.yaml
|
||||
docker exec traefik kill -SIGHUP 1 # Reload Traefik config
|
||||
```
|
||||
|
||||
### 📊 Resource Requirements
|
||||
|
||||
**brn Host (10.50.0.74) Additional Load:**
|
||||
- Authentik: +2GB RAM, +2 CPU cores
|
||||
- Pangolin: +1GB RAM, +1 CPU core
|
||||
- Guacamole: +1GB RAM, +1 CPU core
|
||||
- **Total:** +4GB RAM, +4 CPU cores
|
||||
|
||||
**Current brn specs needed:** Minimum 8GB RAM, 4-6 CPU cores recommended
|
||||
|
||||
### 🔒 Security Posture
|
||||
|
||||
**Improvements:**
|
||||
- ✅ Centralized authentication (single MFA enrollment)
|
||||
- ✅ Granular per-service access control
|
||||
- ✅ Session recording for RDP access
|
||||
- ✅ Network segmentation via Pangolin tunnels
|
||||
- ✅ Elimination of password sprawl
|
||||
|
||||
**Trade-offs:**
|
||||
- ⚠️ RDP NLA disabled (compensated by MFA + network isolation)
|
||||
- ⚠️ Single point of failure (brn host)
|
||||
|
||||
**Overall:** ✅ **Net security improvement**
|
||||
|
||||
---
|
||||
|
||||
## 8. Conclusion
|
||||
|
||||
**FINAL VERDICT:** ✅ **ARCHITECTURE APPROVED FOR IMPLEMENTATION**
|
||||
|
||||
**The proposed Authentik + Pangolin + Guacamole architecture is sound and recommended with the following conditions:**
|
||||
|
||||
1. Acknowledge RDP NLA limitation and implement compensating controls
|
||||
2. Follow phased implementation order as specified
|
||||
3. Create backup strategy before starting (TASK-027)
|
||||
4. Test thoroughly at each phase before proceeding
|
||||
5. Document emergency rollback procedures
|
||||
|
||||
**Confidence Level:** **85%**
|
||||
|
||||
**Remaining 15% risk factors:**
|
||||
- Pangolin relatively new in production (1 year track record)
|
||||
- Guacamole NLA workaround requires security discipline
|
||||
- Single-user deployment lacks redundancy
|
||||
|
||||
**Recommendation to proceed:** ✅ **YES**
|
||||
|
||||
**Next step:** Execute research-informed implementation starting with TASK-003 (Create Authentik Compose) using insights from RESEARCH-002 and TASK-001 outputs.
|
||||
|
||||
---
|
||||
|
||||
**Validation completed by:** Claude Code
|
||||
**Date:** 2026-01-20
|
||||
**Research artifacts referenced:** RESEARCH-001 through RESEARCH-005, TASK-001
|
||||
399
.tasks/artifacts/authentik-research.md
Normal file
399
.tasks/artifacts/authentik-research.md
Normal file
@@ -0,0 +1,399 @@
|
||||
# Authentik Best Practices for Single-User/Single-Admin Deployments
|
||||
|
||||
**Research Date:** 2026-01-20
|
||||
**Task:** RESEARCH-002
|
||||
**Purpose:** Comprehensive research on Authentik deployment strategies for minimal single-user/admin environments
|
||||
|
||||
---
|
||||
|
||||
## 1. Minimal Resource Configuration
|
||||
|
||||
### Host Requirements
|
||||
- **Minimum Specification:** 2 CPU cores and 2 GB RAM (official recommendation)
|
||||
- **Source:** [Authentik Docker Compose Installation Documentation](https://docs.goauthentik.io/install-config/install/docker-compose/)
|
||||
|
||||
### Container-Level Resource Limits
|
||||
|
||||
Based on real-world deployment optimization (from [TEK Online case study](https://www.tekonline.com.au/diagnosing-and-fixing-high-cpu-usage-in-authentik-stack/)), recommended resource limits for single-user deployments:
|
||||
|
||||
#### PostgreSQL Database
|
||||
```yaml
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 1G
|
||||
reservations:
|
||||
cpus: '0.5'
|
||||
memory: 512M
|
||||
```
|
||||
|
||||
#### Redis Cache
|
||||
```yaml
|
||||
resources:
|
||||
limits:
|
||||
cpus: '0.5'
|
||||
memory: 512M
|
||||
reservations:
|
||||
cpus: '0.2'
|
||||
memory: 256M
|
||||
```
|
||||
|
||||
#### Authentik Server
|
||||
```yaml
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 1G
|
||||
reservations:
|
||||
cpus: '0.5'
|
||||
memory: 512M
|
||||
```
|
||||
|
||||
#### Authentik Worker
|
||||
```yaml
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 1G
|
||||
reservations:
|
||||
cpus: '0.5'
|
||||
memory: 512M
|
||||
```
|
||||
|
||||
### Kubernetes Deployments
|
||||
For Kubernetes-based deployments, configure resource limits via JSON patches:
|
||||
|
||||
```yaml
|
||||
kubernetes_json_patches:
|
||||
deployment:
|
||||
- op: add
|
||||
path: "/spec/template/spec/containers/0/resources"
|
||||
value:
|
||||
requests:
|
||||
cpu: 2000m
|
||||
memory: 2000Mi
|
||||
limits:
|
||||
cpu: 2000m
|
||||
memory: 2000Mi
|
||||
```
|
||||
|
||||
**Source:** [Authentik Outpost Configuration](https://version-2024-2.goauthentik.io/docs/outposts/)
|
||||
|
||||
### Minimal Viable Configuration Notes
|
||||
- **VPS Testing:** One user reported running Authentik on a free tier with 1 shared vCPU and 256MB shared memory, but this proved insufficient and caused OOM kills
|
||||
- **Recommended Minimum:** 512MB dedicated memory per component (Postgres and Authentik server) to avoid stability issues
|
||||
- **Source:** [Self-hosting Chronicles Blog](https://notes.catdad.science/2023/03/02/self-hosting-chronicles-plex-oidc.html)
|
||||
|
||||
---
|
||||
|
||||
## 2. Backup Strategies
|
||||
|
||||
### Official Backup Components
|
||||
|
||||
Authentik backups consist of **two critical components**:
|
||||
|
||||
#### 2.1 PostgreSQL Database Backup
|
||||
|
||||
**Critical Importance:** The database stores ALL persistent data including:
|
||||
- User accounts and credentials
|
||||
- Policies and configurations
|
||||
- Application settings
|
||||
- Flow definitions
|
||||
- Provider configurations
|
||||
|
||||
**Backup Methods:**
|
||||
- Use PostgreSQL native tools:
|
||||
- `pg_dump` for single database dumps
|
||||
- `pg_dumpall` for complete cluster dumps
|
||||
- Continuous archiving for point-in-time recovery
|
||||
|
||||
**Best Practice Example (from Kubernetes upgrade guide):**
|
||||
```bash
|
||||
# Connect to PostgreSQL pod
|
||||
kubectl exec -it authentik-postgresql-0 -- /bin/bash
|
||||
|
||||
# Create dump in pod's data directory
|
||||
pg_dump -U authentik_user -d authentik_db > /bitnami/postgresql/authentik_backup.sql
|
||||
|
||||
# CRITICAL: Copy dump outside the pod
|
||||
kubectl cp authentik-postgresql-0:/bitnami/postgresql/authentik_backup.sql ./authentik_backup_$(date +%Y%m%d).sql
|
||||
```
|
||||
|
||||
**Important Exclusions:**
|
||||
- Exclude system databases: `template0` and `template1`
|
||||
|
||||
**Source:** [Authentik Backup and Restore Documentation](https://docs.goauthentik.io/docs/sys-mgmt/ops/backup-restore)
|
||||
|
||||
#### 2.2 Static Directory Backups
|
||||
|
||||
**Required Directories:**
|
||||
|
||||
1. **`/media` Directory**
|
||||
- Contains: Application icons, flow backgrounds, uploaded files
|
||||
- **Only required if NOT using S3 external storage**
|
||||
- If using S3: Use AWS S3 Sync utility for backups instead
|
||||
|
||||
2. **`/certs` Directory**
|
||||
- Contains: TLS certificates
|
||||
- Required for SSL/TLS functionality
|
||||
|
||||
**Docker Volume Persistence:**
|
||||
- PostgreSQL data: `/var/lib/postgresql/data`
|
||||
- Media files: `/media`
|
||||
- Certificates: `/certs`
|
||||
|
||||
**Source:** [Authentik Architecture Documentation](https://docs.goauthentik.io/core/architecture/)
|
||||
|
||||
### Backup Automation Strategy
|
||||
|
||||
**Recommended Approach (from community discussions):**
|
||||
1. Automate PostgreSQL dumps using cron or systemd timers
|
||||
2. Back up Docker volumes containing media/certs
|
||||
3. Store backups in off-site location (follow 3-2-1 rule)
|
||||
4. Test restore procedures regularly
|
||||
|
||||
**Pre-Upgrade Requirement:**
|
||||
- **ALWAYS** backup PostgreSQL database before upgrades
|
||||
- Reason: Authentik does not support downgrades
|
||||
- **Source:** [Authentik Upgrade Documentation](https://docs.goauthentik.io/docs/install-config/upgrade)
|
||||
|
||||
### Configuration Storage
|
||||
|
||||
**S3 External Storage (Optional):**
|
||||
```yaml
|
||||
AUTHENTIK_STORAGE__MEDIA__BACKEND=s3
|
||||
AUTHENTIK_STORAGE__MEDIA__S3__BUCKET_NAME=your-bucket
|
||||
```
|
||||
|
||||
**Benefits for Single-User Deployments:**
|
||||
- Reduces local storage requirements
|
||||
- Simplifies media file backups
|
||||
- Enables cross-region redundancy
|
||||
|
||||
**Source:** [Authentik Configuration Documentation](https://docs.goauthentik.io/install-config/configuration/)
|
||||
|
||||
---
|
||||
|
||||
## 3. API Token Management for Service Accounts
|
||||
|
||||
### Service Account Overview
|
||||
|
||||
**Purpose:** Machine-to-machine authentication and automation
|
||||
- Ideal for: Scripts, CI/CD pipelines, infrastructure-as-code (Terraform)
|
||||
- Authentication method: HTTP Basic Authentication with token
|
||||
|
||||
**Source:** [Authentik Service Accounts Documentation](https://docs.goauthentik.io/sys-mgmt/service-accounts/)
|
||||
|
||||
### Token Creation Methods
|
||||
|
||||
#### Method 1: Bootstrap Token (Automated Install)
|
||||
|
||||
Set during initial deployment for the default `akadmin` user:
|
||||
|
||||
```yaml
|
||||
# Environment variable
|
||||
AUTHENTIK_BOOTSTRAP_TOKEN=your-secure-token-here
|
||||
```
|
||||
|
||||
**Kubernetes/Helm Example:**
|
||||
```yaml
|
||||
bootstrap_token: test
|
||||
# Or reference from secret:
|
||||
secretRef: authentik-bootstrap-secret
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- Enables fully automated deployments
|
||||
- No manual UI interaction required
|
||||
- Perfect for infrastructure-as-code workflows
|
||||
|
||||
**Source:** [Authentik Automated Install](https://docs.goauthentik.io/install-config/automated-install/)
|
||||
|
||||
#### Method 2: Manual Token Creation via Admin UI
|
||||
|
||||
1. Navigate to **Directory** > **Tokens and App passwords**
|
||||
2. Create new token with **Intent: API Token**
|
||||
3. Default lifespan: 30 minutes (configurable)
|
||||
|
||||
**Token Properties:**
|
||||
- Can be configured as expiring or non-expiring
|
||||
- Supports auto-rotation
|
||||
- User-specific permissions apply
|
||||
|
||||
#### Method 3: Programmatic Token Generation
|
||||
|
||||
**Current Limitation (as of 2024):**
|
||||
- GitHub Issue [#7707](https://github.com/goauthentik/authentik/issues/7707) requests environment variable support for initial API token generation
|
||||
- GitHub Issue [#12882](https://github.com/goauthentik/authentik/issues/12882) discusses programmatic bearer token creation post-bootstrap
|
||||
|
||||
**Workaround for Automation:**
|
||||
- Use `AUTHENTIK_BOOTSTRAP_TOKEN` for initial setup
|
||||
- Subsequent tokens can be created via API using the bootstrap token
|
||||
|
||||
### Machine-to-Machine (M2M) Authentication
|
||||
|
||||
**OAuth 2.0 Client Credentials Grant:**
|
||||
- Uses `grant_type=client_credentials`
|
||||
- Authentication via service account username + app password token
|
||||
- Returns signed JWT access token
|
||||
|
||||
**Automatic Service Account Creation:**
|
||||
- Pass configured `client_secret` during token request
|
||||
- Authentik auto-generates service account
|
||||
- Naming scheme: `<provider-name>-service-account`
|
||||
|
||||
**Example Token Request:**
|
||||
```bash
|
||||
curl -X POST https://authentik.example.com/application/o/token/ \
|
||||
-d "grant_type=client_credentials" \
|
||||
-d "client_id=your-client-id" \
|
||||
-d "username=service-account-name" \
|
||||
-d "password=app-password-token"
|
||||
```
|
||||
|
||||
**Source:** [Machine-to-Machine Authentication](https://docs.goauthentik.io/add-secure-apps/providers/oauth2/machine_to_machine)
|
||||
|
||||
### Best Practices for Single-Admin Deployments
|
||||
|
||||
1. **Bootstrap Token:** Set `AUTHENTIK_BOOTSTRAP_TOKEN` during initial deployment
|
||||
2. **Service Accounts:** Create dedicated service accounts for each automation task
|
||||
3. **Token Rotation:** Enable auto-rotation for long-lived tokens
|
||||
4. **Least Privilege:** Assign minimal required permissions to service accounts
|
||||
5. **Audit Logging:** Monitor API token usage via Authentik's built-in logging
|
||||
|
||||
---
|
||||
|
||||
## 4. MFA Enforcement Policies
|
||||
|
||||
### Single-Admin MFA Strategy
|
||||
|
||||
For single-user/single-admin deployments, MFA enforcement can be achieved through **Expression Policies** bound to authenticator validation stages.
|
||||
|
||||
### Expression Policy Implementation
|
||||
|
||||
**Concept:** Use Python-based expression policies to conditionally enforce MFA for specific users.
|
||||
|
||||
**Example Policy (User-Specific MFA):**
|
||||
```python
|
||||
# Enforce MFA only for admin user
|
||||
if context['pending_user'].username == "admin":
|
||||
return True # Enforce MFA stage
|
||||
return False # Skip MFA stage for other users
|
||||
```
|
||||
|
||||
**Policy Binding:**
|
||||
1. Create authenticator validation stage in authentication flow
|
||||
2. Create expression policy with above code
|
||||
3. Bind policy to the MFA stage binding
|
||||
4. Result: Only "admin" user sees MFA prompt
|
||||
|
||||
**Source:** [Expression Policies Documentation](https://docs.goauthentik.io/customize/policies/expression/)
|
||||
|
||||
### User/Group Binding Approach
|
||||
|
||||
**Alternative Method:** Bind users directly to stage bindings
|
||||
|
||||
**Process:**
|
||||
1. Create MFA prompt stage in authentication flow
|
||||
2. Bind stage to flow
|
||||
3. In stage binding settings, specify which users/groups must complete the stage
|
||||
4. Result: Only bound users see the MFA prompt
|
||||
|
||||
**Advantages:**
|
||||
- No code required
|
||||
- Simpler for single-admin scenarios
|
||||
- GUI-based configuration
|
||||
|
||||
**Source:** [Stages Documentation](https://docs.goauthentik.io/add-secure-apps/flows-stages/stages)
|
||||
|
||||
### Advanced Expression Policy Functions
|
||||
|
||||
**Available Helper Functions:**
|
||||
```python
|
||||
# Check group membership
|
||||
ak_is_group_member(user, group_name="admins")
|
||||
|
||||
# Check if user has authenticator enrolled
|
||||
ak_user_has_authenticator(user, device_type="totp")
|
||||
|
||||
# Get user by username
|
||||
ak_user_by(username="admin")
|
||||
```
|
||||
|
||||
**Context Variables:**
|
||||
- `context['pending_user']` - User attempting authentication
|
||||
- `context['request']` - HTTP request object
|
||||
- `context['flow_plan']` - Current flow execution plan
|
||||
|
||||
### Recommended MFA Policy for Single-Admin
|
||||
|
||||
**Strict Admin-Only MFA:**
|
||||
```python
|
||||
# Enforce MFA for superuser/admin accounts
|
||||
if context['pending_user'].is_superuser:
|
||||
return True
|
||||
return False
|
||||
```
|
||||
|
||||
**Or by Username:**
|
||||
```python
|
||||
# Enforce MFA for specific admin username
|
||||
admin_usernames = ["olaf", "admin", "root"]
|
||||
if context['pending_user'].username in admin_usernames:
|
||||
return True
|
||||
return False
|
||||
```
|
||||
|
||||
**Best Practice:** Always enforce MFA for accounts with administrative privileges, even in single-user deployments, to protect against credential compromise.
|
||||
|
||||
---
|
||||
|
||||
## 5. Additional Considerations for Single-User Deployments
|
||||
|
||||
### Security Hardening
|
||||
- Enable MFA for admin account (see section 4)
|
||||
- Use strong, unique passwords (consider password manager)
|
||||
- Regularly update Authentik (with pre-upgrade backups)
|
||||
- Enable audit logging and review periodically
|
||||
|
||||
### Monitoring and Maintenance
|
||||
- Set up health check endpoints monitoring
|
||||
- Configure email notifications for critical events
|
||||
- Schedule regular backup verifications (test restore)
|
||||
- Monitor resource usage (CPU/memory) for optimization
|
||||
|
||||
### Disaster Recovery
|
||||
- Document restore procedures
|
||||
- Store backup credentials securely (separate from production)
|
||||
- Test full recovery process at least quarterly
|
||||
- Maintain off-site backup copies (3-2-1 rule)
|
||||
|
||||
### Network Security
|
||||
- Use reverse proxy (Traefik, nginx) with TLS termination
|
||||
- Implement rate limiting for authentication endpoints
|
||||
- Consider IP allowlisting for admin interface
|
||||
- Use internal Docker networks for database/Redis
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
For single-user/single-admin Authentik deployments:
|
||||
|
||||
1. **Resources:** Minimum 2 CPU cores, 2 GB RAM host; set container limits (1GB memory, 1 CPU per service)
|
||||
2. **Backups:** Automate PostgreSQL dumps + backup /media and /certs volumes; test restores regularly
|
||||
3. **API Tokens:** Use `AUTHENTIK_BOOTSTRAP_TOKEN` for automation; create service accounts for scripts
|
||||
4. **MFA:** Implement expression policies or stage bindings to enforce MFA for admin account only
|
||||
|
||||
These practices ensure a secure, maintainable, and resource-efficient Authentik deployment suitable for personal/small-scale use cases.
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- [Authentik Docker Compose Installation](https://docs.goauthentik.io/install-config/install/docker-compose/)
|
||||
- [Authentik Backup and Restore](https://docs.goauthentik.io/docs/sys-mgmt/ops/backup-restore)
|
||||
- [Authentik Service Accounts](https://docs.goauthentik.io/sys-mgmt/service-accounts/)
|
||||
- [Authentik Expression Policies](https://docs.goauthentik.io/customize/policies/expression/)
|
||||
- [Authentik Automated Install](https://docs.goauthentik.io/install-config/automated-install/)
|
||||
- [TEK Online: Diagnosing High CPU Usage](https://www.tekonline.com.au/diagnosing-and-fixing-high-cpu-usage-in-authentik-stack/)
|
||||
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
|
||||
619
.tasks/artifacts/jellyfin-sso-research.md
Normal file
619
.tasks/artifacts/jellyfin-sso-research.md
Normal file
@@ -0,0 +1,619 @@
|
||||
# Jellyfin SSO-Auth Plugin Configuration for Authentik OIDC
|
||||
|
||||
**Research Date:** 2026-01-20
|
||||
**Plugin:** jellyfin-plugin-sso by 9p4
|
||||
**Target IDP:** Authentik OIDC
|
||||
**Documentation Version:** Based on plugin v3.5+ and Jellyfin 10.9+
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
The Jellyfin SSO-Auth plugin enables OpenID Connect (OIDC) authentication with Authentik, allowing users to authenticate via SSO. This document covers installation, claim mapping configuration, and critical mobile app authentication workflows.
|
||||
|
||||
**Key Findings:**
|
||||
- Plugin supports OIDC providers including Authentik, Authelia, Keycloak, and others
|
||||
- Claim mapping is essential for role-based authorization
|
||||
- Mobile apps have SSO limitations requiring alternative authentication methods
|
||||
- Quick Connect and API tokens are recommended for mobile app access
|
||||
|
||||
---
|
||||
|
||||
## 1. Plugin Installation Process
|
||||
|
||||
### 1.1 Installation via Web GUI
|
||||
|
||||
1. **Access Jellyfin Administration Dashboard**
|
||||
- Navigate to your Jellyfin instance (e.g., `https://jellyfin.example.com`)
|
||||
- Log in with administrator credentials
|
||||
- Go to **Dashboard** → **Plugins**
|
||||
|
||||
2. **Add Plugin Repository**
|
||||
- Click on the **Repositories** tab
|
||||
- Click the **+** button to add a new repository
|
||||
- Enter the following details:
|
||||
- **Repository Name:** `Jellyfin SSO`
|
||||
- **Repository URL:** `https://raw.githubusercontent.com/9p4/jellyfin-plugin-sso/manifest-release/manifest.json`
|
||||
- Click **Save**
|
||||
- Confirm the repository installation
|
||||
|
||||
3. **Install SSO-Auth Plugin**
|
||||
- Go to the **Catalog** tab
|
||||
- Find **SSO Authentication** in the **Authentication** section
|
||||
- Click **Install**
|
||||
- Confirm the plugin installation
|
||||
- **Restart Jellyfin** for the plugin to activate
|
||||
|
||||
### 1.2 Configuration File Location
|
||||
|
||||
After installation, the plugin configuration is stored at:
|
||||
```
|
||||
/config/data/plugins/configurations/SSO-Auth.xml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Authentik OIDC Provider Configuration
|
||||
|
||||
### 2.1 Authentik Setup
|
||||
|
||||
In Authentik, create an OIDC provider with the following settings:
|
||||
|
||||
**Provider Settings:**
|
||||
- **Name:** `Jellyfin`
|
||||
- **Client Type:** `Confidential`
|
||||
- **Client ID:** `jellyfin` (or custom value)
|
||||
- **Client Secret:** Generate secure secret
|
||||
- **Redirect URIs:**
|
||||
```
|
||||
https://jellyfin.example.com/sso/OID/redirect/authentik
|
||||
```
|
||||
- **Token Endpoint Auth Method:** `client_secret_post` (CRITICAL for compatibility)
|
||||
- **Scopes:** `openid`, `profile`, `email`, `groups`
|
||||
|
||||
**Important Note:** The plugin uses `client_secret_post` authentication method. Ensure Authentik is configured to accept this method. Mismatch causes authentication failures:
|
||||
|
||||
```
|
||||
Client authentication failed... The registered client with id 'jellyfin' is configured
|
||||
to only support 'token_endpoint_auth_method' method 'client_secret_basic'.
|
||||
```
|
||||
|
||||
### 2.2 Authentik Group Configuration
|
||||
|
||||
Create groups for role-based access:
|
||||
- **jellyfin-admins** - Administrative access
|
||||
- **jellyfin-users** - Standard user access
|
||||
|
||||
Assign users to appropriate groups in Authentik.
|
||||
|
||||
### 2.3 Authentik Scope and Property Mappings
|
||||
|
||||
Ensure the following claim mappings are configured:
|
||||
- **preferred_username** - Username claim
|
||||
- **email** - Email address
|
||||
- **groups** - User group memberships (CRITICAL for role authorization)
|
||||
|
||||
---
|
||||
|
||||
## 3. Jellyfin SSO Plugin Configuration
|
||||
|
||||
### 3.1 Configuration via Web GUI
|
||||
|
||||
1. **Navigate to Plugin Settings**
|
||||
- Dashboard → **Plugins** → **SSO-Auth**
|
||||
|
||||
2. **Add OIDC Provider**
|
||||
- Click **Add Provider**
|
||||
- Configure the following settings:
|
||||
|
||||
**Basic Configuration:**
|
||||
```
|
||||
Name of OID Provider: authentik
|
||||
OID Endpoint: https://auth.example.com
|
||||
OpenID Client ID: jellyfin
|
||||
OID Secret: <your-secure-secret>
|
||||
```
|
||||
|
||||
**Authorization Settings:**
|
||||
```
|
||||
Enabled: ☑ (checked)
|
||||
Enable Authorization by Plugin: ☑ (checked)
|
||||
Enable All Folders: ☑ (checked)
|
||||
```
|
||||
|
||||
**Role Mapping:**
|
||||
```
|
||||
Roles: jellyfin-users, jellyfin-admins
|
||||
Admin Roles: jellyfin-admins
|
||||
Role Claim: groups
|
||||
```
|
||||
|
||||
**Advanced Settings:**
|
||||
```
|
||||
Request Additional Scopes: groups
|
||||
Default Username Claim: preferred_username
|
||||
New Path (recommended): ☑ (checked)
|
||||
Disable HTTPS: ☐ (unchecked)
|
||||
Do Not Validate Endpoints: ☐ (unchecked)
|
||||
Do Not Validate Issuer Name: ☐ (unchecked)
|
||||
```
|
||||
|
||||
3. **Save Configuration**
|
||||
- Click **Save**
|
||||
- The plugin is now active
|
||||
|
||||
### 3.2 Configuration via XML File
|
||||
|
||||
Alternatively, configure via the XML file at `/config/data/plugins/configurations/SSO-Auth.xml`:
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PluginConfiguration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||
<SamlConfigs />
|
||||
<OidConfigs>
|
||||
<item>
|
||||
<key>
|
||||
<string>authentik</string>
|
||||
</key>
|
||||
<value>
|
||||
<PluginConfiguration>
|
||||
<OidEndpoint>https://auth.example.com</OidEndpoint>
|
||||
<OidClientId>jellyfin</OidClientId>
|
||||
<OidSecret>your-secure-secret</OidSecret>
|
||||
<Enabled>true</Enabled>
|
||||
<EnableAuthorization>true</EnableAuthorization>
|
||||
<EnableAllFolders>true</EnableAllFolders>
|
||||
<EnabledFolders />
|
||||
<AdminRoles>
|
||||
<string>jellyfin-admins</string>
|
||||
</AdminRoles>
|
||||
<Roles>
|
||||
<string>jellyfin-users</string>
|
||||
<string>jellyfin-admins</string>
|
||||
</Roles>
|
||||
<EnableFolderRoles>false</EnableFolderRoles>
|
||||
<EnableLiveTvRoles>false</EnableLiveTvRoles>
|
||||
<EnableLiveTv>false</EnableLiveTv>
|
||||
<EnableLiveTvManagement>false</EnableLiveTvManagement>
|
||||
<LiveTvRoles />
|
||||
<LiveTvManagementRoles />
|
||||
<FolderRoleMappings />
|
||||
<RoleClaim>groups</RoleClaim>
|
||||
<OidScopes>
|
||||
<string>groups</string>
|
||||
</OidScopes>
|
||||
<DefaultUsernameClaim>preferred_username</DefaultUsernameClaim>
|
||||
<NewPath>true</NewPath>
|
||||
<CanonicalLinks />
|
||||
<DisableHttps>false</DisableHttps>
|
||||
<DisablePushedAuthorization>false</DisablePushedAuthorization>
|
||||
<DoNotValidateEndpoints>false</DoNotValidateEndpoints>
|
||||
<DoNotValidateIssuerName>false</DoNotValidateIssuerName>
|
||||
</PluginConfiguration>
|
||||
</value>
|
||||
</item>
|
||||
</OidConfigs>
|
||||
</PluginConfiguration>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Claim Mapping Configuration
|
||||
|
||||
### 4.1 Understanding Role Claims
|
||||
|
||||
The plugin uses the `RoleClaim` setting to determine which OIDC claim contains user roles. Common patterns:
|
||||
|
||||
**Standard Groups Claim:**
|
||||
```xml
|
||||
<RoleClaim>groups</RoleClaim>
|
||||
```
|
||||
|
||||
**Nested Claims (e.g., Keycloak):**
|
||||
```xml
|
||||
<RoleClaim>realm_access.roles</RoleClaim>
|
||||
```
|
||||
|
||||
**Complex Nested Claims:**
|
||||
```xml
|
||||
<RoleClaim>resource_access.jellyfin.roles</RoleClaim>
|
||||
```
|
||||
|
||||
### 4.2 Common Claim Mapping Issues
|
||||
|
||||
**Issue: Incorrect role claims warning**
|
||||
```
|
||||
OpenID user "username" has one or more incorrect role claims:
|
||||
[{"Type": "groups", "Value": "jellyfin-admin"}]. Expected any one of: ["jellyfin-admins"]
|
||||
```
|
||||
|
||||
**Solution:** Ensure exact match between:
|
||||
1. Group names in Authentik
|
||||
2. Role values in plugin configuration (`<AdminRoles>`, `<Roles>`)
|
||||
3. Case sensitivity matters
|
||||
|
||||
### 4.3 Username Claim Mapping
|
||||
|
||||
The plugin supports multiple username claim sources:
|
||||
|
||||
```xml
|
||||
<DefaultUsernameClaim>preferred_username</DefaultUsernameClaim>
|
||||
```
|
||||
|
||||
**Supported Claims:**
|
||||
- `preferred_username` - Most common
|
||||
- `email` - Email address as username
|
||||
- `sub` - OIDC subject identifier
|
||||
- `name` - Display name
|
||||
|
||||
### 4.4 Additional Scopes
|
||||
|
||||
Request additional claims beyond the default `openid` scope:
|
||||
|
||||
```xml
|
||||
<OidScopes>
|
||||
<string>groups</string>
|
||||
<string>email</string>
|
||||
<string>profile</string>
|
||||
</OidScopes>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Mobile App Authentication Workflow
|
||||
|
||||
### 5.1 The Mobile App SSO Problem
|
||||
|
||||
**Critical Finding:** Jellyfin mobile apps (Android/iOS) have limited SSO support. When using SSO login on mobile:
|
||||
|
||||
**Observed Behavior:**
|
||||
- User clicks "Sign in with SSO"
|
||||
- Browser-based authentication completes successfully
|
||||
- Redirect returns to app
|
||||
- App hangs on "Logging in..." indefinitely
|
||||
- Session is not established
|
||||
|
||||
**Root Cause:**
|
||||
- Mobile apps expect traditional username/password authentication
|
||||
- Web-based OIDC flow completes but token is not properly transferred to app
|
||||
- Issue documented in plugin Issue #189 (Jellyfin 10.9+)
|
||||
|
||||
### 5.2 Recommended Mobile App Solutions
|
||||
|
||||
#### Option 1: Quick Connect (Recommended)
|
||||
|
||||
Quick Connect allows mobile apps to authenticate without entering credentials:
|
||||
|
||||
**How It Works:**
|
||||
1. User opens Jellyfin mobile app
|
||||
2. App displays a 6-digit code
|
||||
3. User logs into web interface (via SSO)
|
||||
4. User enters the 6-digit code in web dashboard
|
||||
5. Mobile app is authorized automatically
|
||||
|
||||
**Configuration:**
|
||||
- Enable Quick Connect in Jellyfin Dashboard
|
||||
- Dashboard → **Networking** → **Quick Connect**
|
||||
- Check "Enable Quick Connect"
|
||||
|
||||
**Usage:**
|
||||
1. Mobile app: **Settings** → **Add Server** → **Quick Connect**
|
||||
2. Note the 6-digit code
|
||||
3. Web browser: Login via SSO → **Dashboard** → **Quick Connect**
|
||||
4. Enter code and authorize
|
||||
5. Mobile app connects automatically
|
||||
|
||||
#### Option 2: API Token Authentication
|
||||
|
||||
Generate an API token for mobile app access:
|
||||
|
||||
**Steps:**
|
||||
1. Login to Jellyfin web interface via SSO
|
||||
2. Navigate to **Dashboard** → **API Keys**
|
||||
3. Click **+** to create new API key
|
||||
4. Name: "Mobile App - [Device Name]"
|
||||
5. Copy the generated API token
|
||||
6. In mobile app: Use token instead of password
|
||||
|
||||
**Security Considerations:**
|
||||
- Tokens should be device-specific
|
||||
- Revoke tokens for lost/stolen devices
|
||||
- Rotate tokens periodically
|
||||
- Monitor active sessions in Dashboard
|
||||
|
||||
#### Option 3: Fallback Local Authentication
|
||||
|
||||
Configure the plugin to allow fallback to local Jellyfin authentication:
|
||||
|
||||
```xml
|
||||
<DefaultProvider>Jellyfin.Server.Implementations.Users.DefaultAuthenticationProvider</DefaultProvider>
|
||||
```
|
||||
|
||||
**Workflow:**
|
||||
- Web users: Use SSO
|
||||
- Mobile users: Use Jellyfin username/password
|
||||
- Maintain separate password for mobile access
|
||||
- Less secure but functional
|
||||
|
||||
### 5.3 Mobile App Compatibility Matrix
|
||||
|
||||
| App | SSO Support | Quick Connect | API Token | Notes |
|
||||
|-----|-------------|---------------|-----------|-------|
|
||||
| Jellyfin Android | Partial | ✓ | ✓ | SSO hangs on "Logging in" |
|
||||
| Jellyfin iOS | Partial | ✓ | ✓ | Similar issues as Android |
|
||||
| Jellyfin Web | ✓ | N/A | ✓ | Full SSO support |
|
||||
| Finamp (Music) | ✗ | ✓ | ✓ | No SSO, Quick Connect recommended |
|
||||
| Jellyfin Kodi | ✗ | ✓ | ✓ | Token authentication works |
|
||||
|
||||
---
|
||||
|
||||
## 6. Testing and Troubleshooting
|
||||
|
||||
### 6.1 Testing Web Login
|
||||
|
||||
**Login URL:**
|
||||
```
|
||||
https://jellyfin.example.com/sso/OID/start/authentik
|
||||
```
|
||||
|
||||
**Expected Flow:**
|
||||
1. User navigates to URL
|
||||
2. Redirects to Authentik login page
|
||||
3. User authenticates with Authentik
|
||||
4. Redirects back to Jellyfin
|
||||
5. User is logged in with appropriate role
|
||||
|
||||
### 6.2 Adding SSO Button to Login Page
|
||||
|
||||
Create a custom login button on the Jellyfin login page:
|
||||
|
||||
1. Navigate to Dashboard → **General** → **Custom CSS**
|
||||
2. Add the following CSS to create an SSO button:
|
||||
|
||||
```css
|
||||
/* SSO Login Button */
|
||||
.raised.emby-button {
|
||||
padding: 0.9em 1em;
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
.disclaimerContainer {
|
||||
display: block;
|
||||
}
|
||||
```
|
||||
|
||||
3. Use browser developer tools or custom HTML injection to add button (plugin documentation has detailed instructions)
|
||||
|
||||
### 6.3 Common Errors and Solutions
|
||||
|
||||
#### Error: "Client authentication failed"
|
||||
|
||||
```
|
||||
The registered client with id 'jellyfin' is configured to only support
|
||||
'token_endpoint_auth_method' method 'client_secret_basic'.
|
||||
```
|
||||
|
||||
**Solution:** Change Authentik provider settings:
|
||||
- Edit OIDC Provider in Authentik
|
||||
- Set **Client Authentication Method** to `client_secret_post`
|
||||
- Save and restart Jellyfin
|
||||
|
||||
#### Error: "Incorrect role claims"
|
||||
|
||||
```
|
||||
OpenID user "user123" has one or more incorrect role claims:
|
||||
[{"Type": "groups", "Value": "jellyfin-user"}]. Expected any one of: ["jellyfin-users"]
|
||||
```
|
||||
|
||||
**Solution:** Check exact spelling and case:
|
||||
- Authentik groups must match exactly
|
||||
- Plugin `<Roles>` must match exactly
|
||||
- Claim name in `<RoleClaim>` must match Authentik scope mapping
|
||||
|
||||
#### Error: "Error processing request"
|
||||
|
||||
```
|
||||
[ERR] Jellyfin.Api.Middleware.ExceptionMiddleware: Error processing request.
|
||||
URL "GET" "/sso/OID/start/authentik"
|
||||
```
|
||||
|
||||
**Common Causes:**
|
||||
1. **DNS Resolution:** Jellyfin container cannot resolve Authentik hostname
|
||||
2. **Network Connectivity:** Firewall blocking connection
|
||||
3. **Certificate Issues:** SSL/TLS validation failing
|
||||
|
||||
**Solutions:**
|
||||
- Verify DNS: `docker exec jellyfin ping auth.example.com`
|
||||
- Check network connectivity
|
||||
- Set `<DoNotValidateEndpoints>true</DoNotValidateEndpoints>` for testing (not production)
|
||||
|
||||
### 6.4 Debugging with Logs
|
||||
|
||||
Enable verbose logging in Jellyfin:
|
||||
|
||||
1. Dashboard → **Logs**
|
||||
2. Look for `Jellyfin.Plugin.SSO_Auth` entries
|
||||
3. Check for claim mismatches, authentication failures
|
||||
|
||||
**Useful Log Patterns:**
|
||||
```
|
||||
[INF] Jellyfin.Plugin.SSO_Auth.Api.SSOController: SSO Controller initialized
|
||||
[WRN] OpenID user "username" has one or more incorrect role claims
|
||||
[ERR] Error processing request
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Security Best Practices
|
||||
|
||||
### 7.1 HTTPS Configuration
|
||||
|
||||
**Always use HTTPS in production:**
|
||||
- Configure reverse proxy (Traefik, Nginx, Caddy)
|
||||
- Obtain valid SSL/TLS certificates
|
||||
- Set `<DisableHttps>false</DisableHttps>`
|
||||
|
||||
### 7.2 Client Secret Security
|
||||
|
||||
**Protect the client secret:**
|
||||
- Store securely in configuration files
|
||||
- Use environment variables or secrets management
|
||||
- Never commit to version control
|
||||
- Rotate periodically
|
||||
|
||||
### 7.3 Endpoint Validation
|
||||
|
||||
**Enable validation in production:**
|
||||
```xml
|
||||
<DoNotValidateEndpoints>false</DoNotValidateEndpoints>
|
||||
<DoNotValidateIssuerName>false</DoNotValidateIssuerName>
|
||||
```
|
||||
|
||||
**Disable only for testing/debugging**
|
||||
|
||||
### 7.4 Role-Based Access Control
|
||||
|
||||
**Implement least privilege:**
|
||||
- Separate admin and user roles
|
||||
- Use folder permissions for content restriction
|
||||
- Enable `<EnableAuthorization>true</EnableAuthorization>`
|
||||
- Audit user access regularly
|
||||
|
||||
### 7.5 API Token Management
|
||||
|
||||
**For mobile apps using tokens:**
|
||||
- Generate device-specific tokens
|
||||
- Use descriptive names: "iPhone - John's Device"
|
||||
- Revoke tokens for lost devices
|
||||
- Set token expiration if supported
|
||||
- Monitor active sessions
|
||||
|
||||
---
|
||||
|
||||
## 8. Advanced Configuration Options
|
||||
|
||||
### 8.1 Folder-Based Role Mapping
|
||||
|
||||
Restrict access to specific libraries based on roles:
|
||||
|
||||
```xml
|
||||
<EnableFolderRoles>true</EnableFolderRoles>
|
||||
<FolderRoleMappings>
|
||||
<FolderRoleMappings>
|
||||
<Role>kids-content</Role>
|
||||
<Folders>
|
||||
<string>Kids Movies</string>
|
||||
<string>Kids TV Shows</string>
|
||||
</Folders>
|
||||
</FolderRoleMappings>
|
||||
</FolderRoleMappings>
|
||||
```
|
||||
|
||||
### 8.2 Canonical User Links
|
||||
|
||||
Link SSO users to existing Jellyfin accounts:
|
||||
|
||||
```xml
|
||||
<CanonicalLinks>
|
||||
<item>
|
||||
<key>
|
||||
<string>sso-username</string>
|
||||
</key>
|
||||
<value>
|
||||
<guid>existing-jellyfin-user-guid</guid>
|
||||
</value>
|
||||
</item>
|
||||
</CanonicalLinks>
|
||||
```
|
||||
|
||||
### 8.3 Live TV Permissions
|
||||
|
||||
Control Live TV access via roles:
|
||||
|
||||
```xml
|
||||
<EnableLiveTv>true</EnableLiveTv>
|
||||
<EnableLiveTvRoles>true</EnableLiveTvRoles>
|
||||
<LiveTvRoles>
|
||||
<string>livetv-users</string>
|
||||
</LiveTvRoles>
|
||||
<EnableLiveTvManagement>true</EnableLiveTvManagement>
|
||||
<LiveTvManagementRoles>
|
||||
<string>jellyfin-admins</string>
|
||||
</LiveTvManagementRoles>
|
||||
```
|
||||
|
||||
### 8.4 Port Override
|
||||
|
||||
Override the default port for callback URLs:
|
||||
|
||||
```xml
|
||||
<PortOverride>8096</PortOverride>
|
||||
```
|
||||
|
||||
Useful when Jellyfin runs behind a reverse proxy on a non-standard port.
|
||||
|
||||
---
|
||||
|
||||
## 9. Integration Testing Checklist
|
||||
|
||||
- [ ] Plugin installed and Jellyfin restarted
|
||||
- [ ] Authentik OIDC provider configured with correct redirect URI
|
||||
- [ ] Client authentication method set to `client_secret_post`
|
||||
- [ ] Groups scope enabled in Authentik
|
||||
- [ ] Test users assigned to jellyfin-users or jellyfin-admins groups
|
||||
- [ ] Plugin configured with correct endpoint, client ID, and secret
|
||||
- [ ] Role claim mapping matches Authentik group claim
|
||||
- [ ] Web login successful via `/sso/OID/start/authentik`
|
||||
- [ ] User receives correct permissions (admin vs. user)
|
||||
- [ ] Quick Connect enabled for mobile apps
|
||||
- [ ] API tokens generated for mobile testing
|
||||
- [ ] Mobile app authentication tested
|
||||
- [ ] HTTPS enabled in production
|
||||
- [ ] Endpoint validation enabled
|
||||
- [ ] Logs reviewed for errors
|
||||
|
||||
---
|
||||
|
||||
## 10. References and Resources
|
||||
|
||||
**Official Documentation:**
|
||||
- Jellyfin SSO Plugin Repository: https://github.com/9p4/jellyfin-plugin-sso
|
||||
- Authentik Jellyfin Integration: https://docs.goauthentik.io/integrations/services/jellyfin/
|
||||
- Jellyfin Official Documentation: https://jellyfin.org/docs/
|
||||
|
||||
**Community Resources:**
|
||||
- Plugin Issue Tracker: https://github.com/9p4/jellyfin-plugin-sso/issues
|
||||
- Jellyfin Forum: https://forum.jellyfin.org/
|
||||
- Authentik Forum: https://github.com/goauthentik/authentik/discussions
|
||||
|
||||
**Key Issues Referenced:**
|
||||
- Mobile App SSO Issue #189: https://github.com/9p4/jellyfin-plugin-sso/issues/189
|
||||
- Authelia Configuration Discussion: https://www.authelia.com/integration/openid-connect/clients/jellyfin/
|
||||
- Quick Connect Feature Request: https://support.symfonium.app/t/quick-connect-for-jellyfin/6428
|
||||
|
||||
---
|
||||
|
||||
## 11. Conclusion
|
||||
|
||||
The Jellyfin SSO-Auth plugin provides robust OIDC integration with Authentik for web-based authentication. Key takeaways:
|
||||
|
||||
1. **Web authentication works well** when properly configured with correct claim mappings
|
||||
2. **Mobile app support is limited** - Quick Connect or API tokens are required for mobile access
|
||||
3. **Role-based authorization** requires exact matching between Authentik groups and plugin configuration
|
||||
4. **Security configuration** is critical - always use HTTPS and validate endpoints in production
|
||||
5. **Troubleshooting** relies heavily on log analysis and understanding OIDC claim flows
|
||||
|
||||
**Recommended Deployment Strategy:**
|
||||
- Primary users: SSO via web interface
|
||||
- Mobile apps: Quick Connect for initial setup, API tokens for ongoing access
|
||||
- Administrators: SSO + 2FA in Authentik for enhanced security
|
||||
- Regular audits of active sessions and API tokens
|
||||
|
||||
This configuration provides a secure, user-friendly authentication experience while accommodating the limitations of mobile client applications.
|
||||
|
||||
---
|
||||
|
||||
**Document Version:** 1.0
|
||||
**Last Updated:** 2026-01-20
|
||||
**Author:** Research Task RESEARCH-004
|
||||
**Status:** Complete
|
||||
458
.tasks/artifacts/openwebui-research.md
Normal file
458
.tasks/artifacts/openwebui-research.md
Normal file
@@ -0,0 +1,458 @@
|
||||
# OpenWebUI OIDC Integration Research
|
||||
|
||||
**Research Task:** RESEARCH-005
|
||||
**Date:** 2026-01-20
|
||||
**Version:** OpenWebUI v0.7.1+
|
||||
|
||||
## Executive Summary
|
||||
|
||||
OpenWebUI supports OIDC/OAuth2 authentication with comprehensive role and group management capabilities. This document details all environment variables, multi-user configurations, and admin designation methods via OIDC claims.
|
||||
|
||||
**Key Limitations:**
|
||||
- Only ONE OIDC provider supported at a time via `OPENID_PROVIDER_URL`
|
||||
- Cannot configure Microsoft AND Google OIDC simultaneously (workaround exists)
|
||||
- Some variables are `PersistentConfig` - values persist to DB after first launch
|
||||
|
||||
---
|
||||
|
||||
## Complete Environment Variables List
|
||||
|
||||
### Core OIDC/OAuth Configuration
|
||||
|
||||
| Variable | Type | Default | Description |
|
||||
|----------|------|---------|-------------|
|
||||
| `WEBUI_URL` | String | (required) | **REQUIRED.** Public WebUI address (e.g., `http://localhost:8080`). Must be set before OAuth/SSO. |
|
||||
| `WEBUI_SECRET_KEY` | String | (auto-generated) | Session encryption key. **Required** in clustered/production environments. |
|
||||
| `OPENID_PROVIDER_URL` | String | (none) | **REQUIRED for OIDC.** OpenID Connect discovery URL (e.g., `https://accounts.google.com/.well-known/openid-configuration`). |
|
||||
| `OAUTH_CLIENT_ID` | String | (none) | **REQUIRED.** OAuth client ID from IdP. |
|
||||
| `OAUTH_CLIENT_SECRET` | String | (none) | **REQUIRED.** OAuth client secret from IdP. |
|
||||
|
||||
### OAuth Behavior Controls
|
||||
|
||||
| Variable | Type | Default | Description |
|
||||
|----------|------|---------|-------------|
|
||||
| `ENABLE_OAUTH_SIGNUP` | Boolean | `false` | Allow new account creation via OAuth login (separate from `ENABLE_SIGNUP`). |
|
||||
| `ENABLE_OAUTH_PERSISTENT_CONFIG` | Boolean | `true` | Persist OAuth config to database. Set to `false` for stateless/containerized environments. |
|
||||
| `OAUTH_MERGE_ACCOUNTS_BY_EMAIL` | Boolean | `false` | Merge OAuth logins by matching email. **CAUTION:** Insecure if provider doesn't verify emails. |
|
||||
| `OAUTH_UPDATE_PICTURE_ON_LOGIN` | Boolean | `false` | Update user profile pictures from OAuth provider on each login. |
|
||||
| `ENABLE_OAUTH_ID_TOKEN_COOKIE` | Boolean | `true` | Store ID token in cookie (for backward compatibility). |
|
||||
|
||||
### Token & Session Management
|
||||
|
||||
| Variable | Type | Default | Description |
|
||||
|----------|------|---------|-------------|
|
||||
| `OAUTH_SESSION_TOKEN_ENCRYPTION_KEY` | String | (auto-generated) | Encrypts OAuth session tokens server-side. |
|
||||
| `OAUTH_CLIENT_INFO_ENCRYPTION_KEY` | String | (auto-generated) | Encrypts OAuth client information for MCP servers. |
|
||||
| `WEBUI_AUTH_SIGNOUT_REDIRECT_URL` | String | (empty) | Redirect URL after signout (e.g., `https://idp.example.com/logout`). |
|
||||
|
||||
### Claim Mapping Variables
|
||||
|
||||
| Variable | Type | Default | Description |
|
||||
|----------|------|---------|-------------|
|
||||
| `OAUTH_USERNAME_CLAIM` | String | `preferred_username` | Claim field containing username. |
|
||||
| `OAUTH_EMAIL_CLAIM` | String | `email` | Claim field containing user email. **NOTE:** May need customization for some IdPs (e.g., Microsoft Entra). |
|
||||
| `OAUTH_PICTURE_CLAIM` | String | `picture` | Claim field containing profile picture URL. Set to empty string to disable. |
|
||||
|
||||
**Important:** Microsoft Entra ID sometimes returns email in `preferred_username` instead of `email` claim. Custom configuration may be required.
|
||||
|
||||
---
|
||||
|
||||
## Role Management Configuration
|
||||
|
||||
### Role Synchronization
|
||||
|
||||
| Variable | Type | Default | Description |
|
||||
|----------|------|---------|-------------|
|
||||
| `ENABLE_OAUTH_ROLE_MANAGEMENT` | Boolean | `false` | **Enable role synchronization from OIDC claims.** |
|
||||
| `OAUTH_ROLES_CLAIM` | String | `roles` | Claim field containing user roles. Supports nested paths (e.g., `resource_access.client.roles`). |
|
||||
| `OAUTH_ALLOWED_ROLES` | String (CSV) | (none) | Comma-separated list of roles allowed to access OpenWebUI. Empty = all authenticated users allowed. |
|
||||
| `OAUTH_ADMIN_ROLES` | String (CSV) | (none) | **Comma-separated list of roles that grant admin privileges.** Users with these roles become OpenWebUI admins. |
|
||||
|
||||
### Role Management Behavior
|
||||
|
||||
**When `ENABLE_OAUTH_ROLE_MANAGEMENT=true`:**
|
||||
- User roles are synchronized from OIDC claims on **every login**
|
||||
- `OAUTH_ROLES_CLAIM` can use dot notation for nested claims (e.g., `realm_access.roles`)
|
||||
- If user has role in `OAUTH_ADMIN_ROLES`, they receive admin privileges
|
||||
- If user doesn't have role in `OAUTH_ALLOWED_ROLES`, login is denied
|
||||
|
||||
**Example Configuration:**
|
||||
```bash
|
||||
ENABLE_OAUTH_ROLE_MANAGEMENT=true
|
||||
OAUTH_ROLES_CLAIM=roles
|
||||
OAUTH_ALLOWED_ROLES=openwebui-user,openwebui-admin
|
||||
OAUTH_ADMIN_ROLES=openwebui-admin
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Group Management Configuration
|
||||
|
||||
### Group Synchronization
|
||||
|
||||
| Variable | Type | Default | Description |
|
||||
|----------|------|---------|-------------|
|
||||
| `ENABLE_OAUTH_GROUP_MANAGEMENT` | Boolean | `false` | **Enable group membership synchronization from OIDC claims.** |
|
||||
| `OAUTH_GROUP_CLAIM` | String | `groups` | Claim field containing group memberships. |
|
||||
| `ENABLE_OAUTH_GROUP_CREATION` | Boolean | `false` | **Enable Just-in-Time (JIT) group creation.** Groups from claims are auto-created if they don't exist. |
|
||||
|
||||
### Group Management Behavior
|
||||
|
||||
**CRITICAL:** When `ENABLE_OAUTH_GROUP_MANAGEMENT=true`:
|
||||
- User's group memberships in OpenWebUI are **STRICTLY SYNCHRONIZED** with OAuth claims on **every login**
|
||||
- Manually assigned groups will be removed if not present in OAuth claims
|
||||
- Groups are identified by name matching between claim values and OpenWebUI group names
|
||||
|
||||
**Just-in-Time Group Creation:**
|
||||
When `ENABLE_OAUTH_GROUP_CREATION=true`:
|
||||
- Groups from OIDC claims are automatically created in OpenWebUI if they don't exist
|
||||
- Useful for dynamic group provisioning without manual setup
|
||||
- Groups are created with default permissions
|
||||
|
||||
**Example Configuration:**
|
||||
```bash
|
||||
ENABLE_OAUTH_GROUP_MANAGEMENT=true
|
||||
OAUTH_GROUP_CLAIM=groups
|
||||
ENABLE_OAUTH_GROUP_CREATION=true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Provider-Specific Variables
|
||||
|
||||
### Google OAuth
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `GOOGLE_CLIENT_ID` | Google OAuth client ID |
|
||||
| `GOOGLE_CLIENT_SECRET` | Google OAuth client secret |
|
||||
| `OPENID_PROVIDER_URL` | Set to `https://accounts.google.com/.well-known/openid-configuration` |
|
||||
|
||||
**Redirect URI:** `https://your-domain/oauth/google/callback`
|
||||
|
||||
### Microsoft OAuth (Entra ID)
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `MICROSOFT_CLIENT_ID` | Microsoft OAuth client ID |
|
||||
| `MICROSOFT_CLIENT_SECRET` | Microsoft OAuth client secret |
|
||||
| `MICROSOFT_CLIENT_TENANT_ID` | Microsoft tenant ID |
|
||||
| `OPENID_PROVIDER_URL` | Set to Microsoft OIDC discovery URL |
|
||||
|
||||
**Redirect URI:** `https://your-domain/oauth/microsoft/callback`
|
||||
|
||||
**Known Issues:**
|
||||
- Email may be in `preferred_username` claim instead of `email`
|
||||
- May require `OAUTH_EMAIL_CLAIM=preferred_username` for some users
|
||||
|
||||
### Generic OIDC Provider
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `OAUTH_CLIENT_ID` | Generic OAuth client ID |
|
||||
| `OAUTH_CLIENT_SECRET` | Generic OAuth client secret |
|
||||
| `OPENID_PROVIDER_URL` | **REQUIRED.** OIDC discovery URL |
|
||||
| `OAUTH_SCOPES` | OAuth scopes to request (default: `openid profile email`) |
|
||||
|
||||
**Redirect URI:** `https://your-domain/oauth/oidc/callback`
|
||||
|
||||
---
|
||||
|
||||
## Trusted Header Authentication (Alternative to OIDC)
|
||||
|
||||
For reverse proxy-based authentication (Tailscale, Cloudflare Tunnel, oauth2-proxy, Authentik, Authelia):
|
||||
|
||||
| Variable | Type | Default | Description |
|
||||
|----------|------|---------|-------------|
|
||||
| `WEBUI_AUTH_TRUSTED_EMAIL_HEADER` | String | (none) | HTTP header containing authenticated user's email |
|
||||
| `WEBUI_AUTH_TRUSTED_NAME_HEADER` | String | (none) | HTTP header containing user's display name |
|
||||
| `WEBUI_AUTH_TRUSTED_GROUPS_HEADER` | String | (none) | HTTP header containing comma-separated group list |
|
||||
|
||||
**SECURITY WARNING:**
|
||||
- Misconfiguration allows **unauthorized authentication bypass**
|
||||
- **ONLY use if reverse proxy strictly blocks direct access to OpenWebUI**
|
||||
- Ensure headers cannot be spoofed by end users
|
||||
|
||||
---
|
||||
|
||||
## Multi-User Role Mapping Configuration
|
||||
|
||||
### Method 1: OIDC Role Claims (Recommended)
|
||||
|
||||
**Step 1: Configure IdP to include roles in token**
|
||||
- Add custom claims to OIDC token
|
||||
- Include roles in `roles` claim (or custom claim path)
|
||||
|
||||
**Step 2: Configure OpenWebUI**
|
||||
```bash
|
||||
ENABLE_OAUTH_ROLE_MANAGEMENT=true
|
||||
OAUTH_ROLES_CLAIM=roles # or custom path like "realm_access.roles"
|
||||
OAUTH_ALLOWED_ROLES=user,admin,power-user # Users must have one of these roles
|
||||
OAUTH_ADMIN_ROLES=admin # Users with this role become admins
|
||||
```
|
||||
|
||||
**Step 3: Map users in IdP**
|
||||
- Assign roles to users in IdP (Authentik, Keycloak, Okta, etc.)
|
||||
- Users sync roles on every login
|
||||
|
||||
### Method 2: OIDC Group Claims
|
||||
|
||||
**Step 1: Configure IdP to include groups in token**
|
||||
- Add groups to OIDC token claims
|
||||
- Ensure group names match desired OpenWebUI groups
|
||||
|
||||
**Step 2: Configure OpenWebUI**
|
||||
```bash
|
||||
ENABLE_OAUTH_GROUP_MANAGEMENT=true
|
||||
OAUTH_GROUP_CLAIM=groups
|
||||
ENABLE_OAUTH_GROUP_CREATION=true # Auto-create groups from claims
|
||||
```
|
||||
|
||||
**Step 3: Manage permissions in OpenWebUI**
|
||||
- Groups are synced automatically
|
||||
- Admin assigns permissions to groups within OpenWebUI
|
||||
|
||||
### Method 3: Combined Role + Group Management
|
||||
|
||||
```bash
|
||||
# Enable both
|
||||
ENABLE_OAUTH_ROLE_MANAGEMENT=true
|
||||
ENABLE_OAUTH_GROUP_MANAGEMENT=true
|
||||
|
||||
# Role configuration
|
||||
OAUTH_ROLES_CLAIM=roles
|
||||
OAUTH_ALLOWED_ROLES=openwebui-user
|
||||
OAUTH_ADMIN_ROLES=openwebui-admin
|
||||
|
||||
# Group configuration
|
||||
OAUTH_GROUP_CLAIM=groups
|
||||
ENABLE_OAUTH_GROUP_CREATION=true
|
||||
```
|
||||
|
||||
**Behavior:**
|
||||
1. Roles control access (allowed) and admin privileges
|
||||
2. Groups control resource/team organization within OpenWebUI
|
||||
3. Both sync on every login
|
||||
|
||||
---
|
||||
|
||||
## Admin Designation via OIDC Claims
|
||||
|
||||
### Option 1: Role-Based Admin (Primary Method)
|
||||
|
||||
**Configuration:**
|
||||
```bash
|
||||
ENABLE_OAUTH_ROLE_MANAGEMENT=true
|
||||
OAUTH_ROLES_CLAIM=roles
|
||||
OAUTH_ADMIN_ROLES=openwebui-admin,superuser
|
||||
```
|
||||
|
||||
**How it works:**
|
||||
- Users with `openwebui-admin` OR `superuser` role become admins
|
||||
- Admin status is synchronized on every login
|
||||
- Removing role in IdP removes admin privileges on next login
|
||||
|
||||
### Option 2: Group-Based Admin (Indirect)
|
||||
|
||||
**Configuration:**
|
||||
```bash
|
||||
ENABLE_OAUTH_GROUP_MANAGEMENT=true
|
||||
OAUTH_GROUP_CLAIM=groups
|
||||
```
|
||||
|
||||
**How it works:**
|
||||
- Groups are synced to OpenWebUI
|
||||
- Admin must manually grant admin privileges to specific users/groups within OpenWebUI
|
||||
- Less automated than role-based approach
|
||||
|
||||
### Option 3: First User Auto-Admin
|
||||
|
||||
**Default Behavior:**
|
||||
- When OpenWebUI is first deployed, the first user to sign in becomes admin
|
||||
- Subsequent users require admin assignment or role-based configuration
|
||||
|
||||
**Not recommended for production** - use role-based admin instead.
|
||||
|
||||
---
|
||||
|
||||
## Example Configurations
|
||||
|
||||
### Authentik OIDC with Role Management
|
||||
|
||||
```bash
|
||||
# Core OIDC
|
||||
WEBUI_URL=https://openwebui.example.com
|
||||
OPENID_PROVIDER_URL=https://authentik.example.com/application/o/openwebui/.well-known/openid-configuration
|
||||
OAUTH_CLIENT_ID=<client-id-from-authentik>
|
||||
OAUTH_CLIENT_SECRET=<client-secret-from-authentik>
|
||||
|
||||
# OAuth behavior
|
||||
ENABLE_OAUTH_SIGNUP=true
|
||||
ENABLE_OAUTH_PERSISTENT_CONFIG=false # For containerized deployments
|
||||
|
||||
# Role management
|
||||
ENABLE_OAUTH_ROLE_MANAGEMENT=true
|
||||
OAUTH_ROLES_CLAIM=roles
|
||||
OAUTH_ALLOWED_ROLES=openwebui-user,openwebui-admin
|
||||
OAUTH_ADMIN_ROLES=openwebui-admin
|
||||
|
||||
# Group management (optional)
|
||||
ENABLE_OAUTH_GROUP_MANAGEMENT=true
|
||||
OAUTH_GROUP_CLAIM=groups
|
||||
ENABLE_OAUTH_GROUP_CREATION=true
|
||||
```
|
||||
|
||||
### Okta OIDC with Group JIT Provisioning
|
||||
|
||||
```bash
|
||||
# Core OIDC
|
||||
WEBUI_URL=https://openwebui.company.com
|
||||
OPENID_PROVIDER_URL=https://dev-12345.okta.com/.well-known/openid-configuration
|
||||
OAUTH_CLIENT_ID=<okta-client-id>
|
||||
OAUTH_CLIENT_SECRET=<okta-client-secret>
|
||||
|
||||
# OAuth behavior
|
||||
ENABLE_OAUTH_SIGNUP=true
|
||||
|
||||
# Role management
|
||||
ENABLE_OAUTH_ROLE_MANAGEMENT=true
|
||||
OAUTH_ROLES_CLAIM=groups # Okta uses groups claim for roles
|
||||
OAUTH_ADMIN_ROLES=OpenWebUI-Admins
|
||||
|
||||
# Group management with JIT
|
||||
ENABLE_OAUTH_GROUP_MANAGEMENT=true
|
||||
OAUTH_GROUP_CLAIM=groups
|
||||
ENABLE_OAUTH_GROUP_CREATION=true
|
||||
```
|
||||
|
||||
### Microsoft Entra ID with Email Claim Fix
|
||||
|
||||
```bash
|
||||
# Core OIDC
|
||||
WEBUI_URL=https://openwebui.contoso.com
|
||||
OPENID_PROVIDER_URL=https://login.microsoftonline.com/<tenant-id>/v2.0/.well-known/openid-configuration
|
||||
MICROSOFT_CLIENT_ID=<application-id>
|
||||
MICROSOFT_CLIENT_SECRET=<client-secret>
|
||||
MICROSOFT_CLIENT_TENANT_ID=<tenant-id>
|
||||
|
||||
# Fix email claim issue (Entra specific)
|
||||
OAUTH_EMAIL_CLAIM=preferred_username # Or upn
|
||||
OAUTH_USERNAME_CLAIM=preferred_username
|
||||
|
||||
# OAuth behavior
|
||||
ENABLE_OAUTH_SIGNUP=true
|
||||
|
||||
# Role management (if using app roles)
|
||||
ENABLE_OAUTH_ROLE_MANAGEMENT=true
|
||||
OAUTH_ROLES_CLAIM=roles
|
||||
OAUTH_ADMIN_ROLES=OpenWebUI.Admin
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Important Notes & Gotchas
|
||||
|
||||
### PersistentConfig Behavior
|
||||
|
||||
**Variables marked as `PersistentConfig`:**
|
||||
- Values are saved to database on **first launch**
|
||||
- Subsequent container restarts **ignore environment variables**
|
||||
- Changes must be made via OpenWebUI Admin UI or database
|
||||
|
||||
**Workaround:**
|
||||
```bash
|
||||
ENABLE_OAUTH_PERSISTENT_CONFIG=false # Disable persistence, use env vars only
|
||||
```
|
||||
|
||||
### Common Misconfigurations
|
||||
|
||||
**Incorrect Variables (Do NOT use):**
|
||||
```bash
|
||||
# WRONG - these don't exist
|
||||
OIDC_CONFIG=...
|
||||
WEBUI_OIDC_CLIENT_ID=...
|
||||
WEBUI_ENABLE_SSO=...
|
||||
WEBUI_AUTH_TYPE=...
|
||||
OPENID_CLIENT_ID=...
|
||||
OPENID_CLIENT_SECRET=...
|
||||
|
||||
# CORRECT - use these instead
|
||||
OPENID_PROVIDER_URL=...
|
||||
OAUTH_CLIENT_ID=...
|
||||
OAUTH_CLIENT_SECRET=...
|
||||
ENABLE_OAUTH_SIGNUP=...
|
||||
```
|
||||
|
||||
### Security Considerations
|
||||
|
||||
1. **Always use HTTPS** for production OIDC deployments
|
||||
2. **Validate redirect URIs** match exactly in IdP configuration
|
||||
3. **Don't enable `OAUTH_MERGE_ACCOUNTS_BY_EMAIL`** unless provider verifies emails
|
||||
4. **Use `OAUTH_ALLOWED_ROLES`** to restrict access to authorized users
|
||||
5. **Trusted Headers** require strict reverse proxy configuration
|
||||
|
||||
### Role vs Group Management
|
||||
|
||||
| Feature | Roles | Groups |
|
||||
|---------|-------|--------|
|
||||
| **Purpose** | Access control, admin privileges | Resource organization, team structure |
|
||||
| **Sync Behavior** | Strict sync on login | Strict sync on login |
|
||||
| **Admin Assignment** | Via `OAUTH_ADMIN_ROLES` | Manual in OpenWebUI UI |
|
||||
| **Access Control** | Via `OAUTH_ALLOWED_ROLES` | Not built-in (manual management) |
|
||||
| **JIT Creation** | N/A | Via `ENABLE_OAUTH_GROUP_CREATION` |
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### OIDC Provider Not Activating
|
||||
|
||||
**Check:**
|
||||
1. `WEBUI_URL` is set in environment **and** Admin Panel
|
||||
2. `OPENID_PROVIDER_URL` is valid and accessible from container
|
||||
3. `OAUTH_CLIENT_ID` and `OAUTH_CLIENT_SECRET` are correct
|
||||
4. Redirect URI in IdP matches `https://<WEBUI_URL>/oauth/oidc/callback`
|
||||
|
||||
### OAuth Callback Failed - Email Missing
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
OAUTH_EMAIL_CLAIM=preferred_username # Or other claim containing email
|
||||
```
|
||||
|
||||
### Users Not Getting Admin Privileges
|
||||
|
||||
**Check:**
|
||||
1. `ENABLE_OAUTH_ROLE_MANAGEMENT=true`
|
||||
2. User has role listed in `OAUTH_ADMIN_ROLES`
|
||||
3. `OAUTH_ROLES_CLAIM` matches claim path in token (use JWT debugger)
|
||||
4. Role claim is included in scopes requested
|
||||
|
||||
### Groups Not Syncing
|
||||
|
||||
**Check:**
|
||||
1. `ENABLE_OAUTH_GROUP_MANAGEMENT=true`
|
||||
2. `OAUTH_GROUP_CLAIM` matches claim name in token
|
||||
3. Groups claim is included in requested scopes
|
||||
4. `ENABLE_OAUTH_GROUP_CREATION=true` if groups don't exist yet
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- [OpenWebUI SSO Documentation](https://docs.openwebui.com/features/auth/sso/)
|
||||
- [OpenWebUI Environment Variables](https://docs.openwebui.com/getting-started/env-configuration/)
|
||||
- [Troubleshooting SSO Issues](https://docs.openwebui.com/troubleshooting/sso/)
|
||||
- [Authentik Integration Guide](https://integrations.goauthentik.io/miscellaneous/open-webui/)
|
||||
- [Authelia Integration Guide](https://www.authelia.com/integration/openid-connect/clients/open-webui/)
|
||||
- [Okta OIDC Tutorial](https://docs.openwebui.com/tutorials/integrations/okta-oidc-sso/)
|
||||
|
||||
---
|
||||
|
||||
## Research Metadata
|
||||
|
||||
- **Task ID:** RESEARCH-005
|
||||
- **Completed:** 2026-01-20
|
||||
- **OpenWebUI Version:** v0.7.1+
|
||||
- **Research Method:** Web documentation review via Exa MCP
|
||||
- **Primary Sources:** Official OpenWebUI documentation, GitHub issues, integration guides
|
||||
935
.tasks/artifacts/pangolin-research.md
Normal file
935
.tasks/artifacts/pangolin-research.md
Normal file
@@ -0,0 +1,935 @@
|
||||
# Pangolin Self-Hosting Research
|
||||
|
||||
**Research Date:** 2026-01-20
|
||||
**Purpose:** RESEARCH-001 - Comprehensive documentation for self-hosting Pangolin control plane
|
||||
|
||||
## Executive Summary
|
||||
|
||||
Pangolin is an open-source, identity-based remote access platform built on WireGuard. It functions as a self-hosted tunneled reverse proxy with identity-aware access control, providing a secure alternative to Cloudflare Tunnels and similar managed services. Pangolin combines two primary use cases: tunneled reverse proxy for web applications and VPN-style access for private network resources, all managed through a centralized control plane.
|
||||
|
||||
**Key Architecture Components:**
|
||||
- **Pangolin Server (Control Plane)**: Centralized dashboard and management plane hosted on a VPS
|
||||
- **Newt (Site/Client Connector)**: Tunnel client installed on private networks to connect backend services
|
||||
- **WireGuard Tunnels**: Encrypted tunneling protocol for secure communication
|
||||
- **Traefik**: Underlying reverse proxy technology
|
||||
- **Identity Providers**: OIDC/OAuth2 integration for authentication
|
||||
|
||||
---
|
||||
|
||||
## 1. Installation Methods for Self-Hosting Pangolin Control Plane
|
||||
|
||||
### 1.1 Quick Install (Recommended for Most Users)
|
||||
|
||||
**Official Installer Script:**
|
||||
The quickest method uses Pangolin's automated installer script that handles all dependencies and configuration.
|
||||
|
||||
**Prerequisites:**
|
||||
- Linux server (Ubuntu 22.04/24.04 or Debian 11/12 recommended)
|
||||
- Root or sudo access
|
||||
- Public IPv4 address
|
||||
- Domain name with DNS configured
|
||||
- Email address for Let's Encrypt SSL certificates
|
||||
- Open firewall ports:
|
||||
- TCP 80 (HTTP & Let's Encrypt validation)
|
||||
- TCP 443 (HTTPS)
|
||||
- UDP 51820 (WireGuard tunnels)
|
||||
|
||||
**Installation Command:**
|
||||
```bash
|
||||
curl -fsSL https://get.pangolin.net | sudo bash
|
||||
```
|
||||
|
||||
The installer will:
|
||||
1. Install required dependencies (Docker, Docker Compose, WireGuard kernel module)
|
||||
2. Generate configuration files
|
||||
3. Set up Traefik reverse proxy
|
||||
4. Configure SSL certificates via Let's Encrypt
|
||||
5. Create initial admin account
|
||||
6. Start all services
|
||||
|
||||
**Post-Installation:**
|
||||
- Access dashboard at `https://your-domain.com`
|
||||
- Log in with admin credentials sent to provided email
|
||||
- Complete organization setup
|
||||
|
||||
### 1.2 Manual Docker Compose Installation
|
||||
|
||||
For users requiring full control over configuration, manual Docker Compose deployment is available.
|
||||
|
||||
**File Structure:**
|
||||
```
|
||||
pangolin/
|
||||
├── docker-compose.yml
|
||||
├── config/
|
||||
│ ├── pangolin.yml # Pangolin configuration
|
||||
│ ├── traefik.yml # Traefik static configuration
|
||||
│ └── traefik-dynamic.yml # Traefik dynamic configuration
|
||||
├── data/
|
||||
│ ├── pangolin/ # Application data
|
||||
│ ├── traefik/ # Traefik certificates
|
||||
│ └── postgres/ # Database (if using PostgreSQL)
|
||||
└── logs/
|
||||
```
|
||||
|
||||
**Docker Compose Configuration:**
|
||||
```yaml
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
pangolin:
|
||||
image: fosrl/pangolin:latest
|
||||
container_name: pangolin
|
||||
restart: unless-stopped
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
sysctls:
|
||||
- net.ipv4.conf.all.src_valid_mark=1
|
||||
- net.ipv4.ip_forward=1
|
||||
- net.ipv6.conf.all.forwarding=1
|
||||
volumes:
|
||||
- ./config/pangolin.yml:/app/config.yml:ro
|
||||
- ./data/pangolin:/app/data
|
||||
- /dev/net/tun:/dev/net/tun
|
||||
environment:
|
||||
- CONFIG_FILE=/app/config.yml
|
||||
ports:
|
||||
- "51820:51820/udp"
|
||||
networks:
|
||||
- pangolin
|
||||
|
||||
traefik:
|
||||
image: traefik:latest
|
||||
container_name: traefik
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./config/traefik.yml:/etc/traefik/traefik.yml:ro
|
||||
- ./config/traefik-dynamic.yml:/etc/traefik/dynamic.yml:ro
|
||||
- ./data/traefik:/letsencrypt
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
networks:
|
||||
- pangolin
|
||||
|
||||
postgres:
|
||||
image: postgres:15-alpine
|
||||
container_name: pangolin-db
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- POSTGRES_DB=pangolin
|
||||
- POSTGRES_USER=pangolin
|
||||
- POSTGRES_PASSWORD=<secure_password>
|
||||
volumes:
|
||||
- ./data/postgres:/var/lib/postgresql/data
|
||||
networks:
|
||||
- pangolin
|
||||
|
||||
networks:
|
||||
pangolin:
|
||||
driver: bridge
|
||||
```
|
||||
|
||||
**Starting the Stack:**
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### 1.3 Alternative Installation Methods
|
||||
|
||||
**Unraid Deployment:**
|
||||
- Community applications available in Unraid App Store
|
||||
- Template-based deployment with GUI configuration
|
||||
- Integration with Unraid Docker management
|
||||
|
||||
**Kubernetes Deployment:**
|
||||
- Helm charts available for production deployments
|
||||
- Supports horizontal scaling of control plane
|
||||
- Advanced orchestration for enterprise use cases
|
||||
|
||||
---
|
||||
|
||||
## 2. OIDC Integration Configuration
|
||||
|
||||
Pangolin supports external identity providers via OAuth2/OIDC for Single Sign-On (SSO), enabling centralized authentication for both the Pangolin dashboard and applications behind the proxy.
|
||||
|
||||
### 2.1 Supported Identity Providers
|
||||
|
||||
- **Google Workspace** - Native integration
|
||||
- **Microsoft Entra ID (Azure AD)** - Native integration
|
||||
- **Generic OAuth2/OIDC** - Any compliant provider
|
||||
- **Authelia** - Tested with v4.39.15+
|
||||
- **Authentik** - Community-supported integration
|
||||
- **Zitadel** - Native support
|
||||
- **Pocket ID** - Native support
|
||||
|
||||
### 2.2 OIDC Provider Configuration
|
||||
|
||||
**Step 1: Create OAuth2/OIDC Application in Identity Provider**
|
||||
|
||||
In your IdP (e.g., Authentik, Authelia, Google), create a new OAuth2/OIDC application with the following settings:
|
||||
|
||||
**Redirect URIs (Critical):**
|
||||
```
|
||||
https://<pangolin-domain>/api/v1/auth/callback
|
||||
https://<pangolin-domain>/api/v1/auth/callback/google # If using Google
|
||||
https://<pangolin-domain>/api/v1/auth/callback/microsoft # If using Microsoft
|
||||
```
|
||||
|
||||
For custom authentication domains:
|
||||
```
|
||||
https://auth.<your-company>.com/api/v1/auth/callback
|
||||
```
|
||||
|
||||
**Required Scopes:**
|
||||
- `openid` (required)
|
||||
- `profile` (recommended)
|
||||
- `email` (recommended)
|
||||
- `groups` (optional, for role-based access)
|
||||
|
||||
**Client Configuration:**
|
||||
- **Client Type:** Confidential
|
||||
- **Grant Type:** Authorization Code
|
||||
- **PKCE:** Enabled (recommended)
|
||||
- **Token Endpoint Auth Method:** client_secret_post or client_secret_basic
|
||||
|
||||
**Step 2: Configure Pangolin Identity Provider**
|
||||
|
||||
In Pangolin Dashboard:
|
||||
|
||||
1. Navigate to **Settings** → **Identity Providers** → **Add Identity Provider**
|
||||
2. Select provider type:
|
||||
- **Google** for Google Workspace
|
||||
- **Azure Entra ID** for Microsoft
|
||||
- **Generic OAuth2/OIDC** for other providers
|
||||
|
||||
3. Enter configuration details:
|
||||
|
||||
**For Generic OAuth2/OIDC:**
|
||||
```yaml
|
||||
Provider Name: Your IdP Name
|
||||
Provider Type: OAuth2/OIDC
|
||||
|
||||
# Discovery URL (auto-populates endpoints)
|
||||
OIDC Discovery URL: https://your-idp.com/.well-known/openid-configuration
|
||||
|
||||
# Manual configuration (if discovery not available)
|
||||
Authorization Endpoint: https://your-idp.com/oauth/authorize
|
||||
Token Endpoint: https://your-idp.com/oauth/token
|
||||
Userinfo Endpoint: https://your-idp.com/oauth/userinfo
|
||||
JWKS URI: https://your-idp.com/oauth/jwks
|
||||
|
||||
# Client credentials
|
||||
Client ID: <from_your_idp>
|
||||
Client Secret: <from_your_idp>
|
||||
|
||||
# Scopes
|
||||
Scopes: openid profile email groups
|
||||
|
||||
# User Mapping
|
||||
Username Claim: preferred_username
|
||||
Email Claim: email
|
||||
Display Name Claim: name
|
||||
Groups Claim: groups (optional)
|
||||
```
|
||||
|
||||
**For Google Workspace:**
|
||||
```yaml
|
||||
Provider Type: Google
|
||||
Client ID: <google_client_id>
|
||||
Client Secret: <google_client_secret>
|
||||
Hosted Domain: your-domain.com (optional, restricts to specific domain)
|
||||
```
|
||||
|
||||
**For Microsoft Entra ID:**
|
||||
```yaml
|
||||
Provider Type: Azure Entra ID
|
||||
Client ID: <azure_client_id>
|
||||
Client Secret: <azure_client_secret>
|
||||
Tenant ID: <azure_tenant_id>
|
||||
```
|
||||
|
||||
### 2.3 Auto-Provisioning Users
|
||||
|
||||
Pangolin supports automatic user provisioning from IdP:
|
||||
|
||||
**Configuration Options:**
|
||||
- **Auto-provision on first login:** Create user accounts automatically when users authenticate
|
||||
- **Group mapping:** Map IdP groups to Pangolin roles/organizations
|
||||
- **Just-in-Time (JIT) provisioning:** Update user attributes on each login
|
||||
|
||||
**Example Configuration:**
|
||||
```yaml
|
||||
auto_provisioning:
|
||||
enabled: true
|
||||
default_role: user
|
||||
group_mapping:
|
||||
- idp_group: "admin"
|
||||
pangolin_role: "admin"
|
||||
- idp_group: "developers"
|
||||
pangolin_role: "developer"
|
||||
```
|
||||
|
||||
### 2.4 Custom Authentication Domains
|
||||
|
||||
For branded authentication experiences:
|
||||
|
||||
1. Configure custom domain in Pangolin settings
|
||||
2. Set authentication domain: `auth.yourcompany.com`
|
||||
3. Update DNS records to point to Pangolin server
|
||||
4. Update redirect URIs in IdP configuration
|
||||
|
||||
---
|
||||
|
||||
## 3. Site Configuration (Defining Backend Services)
|
||||
|
||||
Sites in Pangolin represent network locations where backend services run. The Newt client connects these sites to the Pangolin control plane via WireGuard tunnels.
|
||||
|
||||
### 3.1 Understanding Sites vs Resources
|
||||
|
||||
**Sites:**
|
||||
- Physical or virtual network locations (e.g., home lab, office network, VPS)
|
||||
- Run the Newt client to establish WireGuard tunnels
|
||||
- Can host multiple resources/services
|
||||
- Types:
|
||||
- **Newt Sites:** Traditional tunnel-based sites for private networks
|
||||
- **Remote Nodes:** Exit nodes for client VPN access
|
||||
|
||||
**Resources:**
|
||||
- Individual services/applications exposed through Pangolin
|
||||
- Defined within sites
|
||||
- Types:
|
||||
- **Public Resources (HTTPS):** Web applications accessible via public URLs
|
||||
- **Private Resources:** TCP/UDP services for VPN-style access
|
||||
|
||||
### 3.2 Creating a Site
|
||||
|
||||
**Via Dashboard:**
|
||||
|
||||
1. Navigate to **Sites** → **Add Site**
|
||||
2. Configure site settings:
|
||||
|
||||
```yaml
|
||||
Site Name: Home Lab
|
||||
Description: Private home network services
|
||||
Site Type: Newt Site
|
||||
|
||||
# Tunnel Configuration
|
||||
WireGuard IP Range: 10.0.1.0/24 (auto-assigned)
|
||||
Listen Port: 51820 (default)
|
||||
|
||||
# Site Location (optional)
|
||||
Location: Home Network
|
||||
Tags: homelab, production
|
||||
```
|
||||
|
||||
3. Copy the generated installation command with credentials
|
||||
|
||||
**Installation Command Format:**
|
||||
```bash
|
||||
newt --id <site_id> --key <site_key> --endpoint <pangolin_server>:51820
|
||||
```
|
||||
|
||||
### 3.3 Defining Resources (Backend Services)
|
||||
|
||||
**HTTPS Resources (Public Web Applications):**
|
||||
|
||||
1. Navigate to **Resources** → **Add Resource**
|
||||
2. Select resource type: **HTTPS Resource**
|
||||
|
||||
```yaml
|
||||
Resource Name: Internal Wiki
|
||||
Public URL: wiki.yourdomain.com
|
||||
|
||||
# Backend Configuration
|
||||
Site: Home Lab
|
||||
Backend Type: HTTP
|
||||
Backend Address: 192.168.1.100:8080
|
||||
Backend Protocol: http
|
||||
|
||||
# Health Checks
|
||||
Health Check: Enabled
|
||||
Health Check Path: /health
|
||||
Health Check Interval: 30s
|
||||
|
||||
# Access Control
|
||||
Access Level: Authenticated Users Only
|
||||
Allowed Groups: developers, admins
|
||||
|
||||
# Additional Settings
|
||||
Enable WebSocket: Yes (for real-time apps)
|
||||
Custom Headers: X-Forwarded-Proto: https
|
||||
```
|
||||
|
||||
**Private Resources (TCP/UDP Services):**
|
||||
|
||||
For services accessed via Pangolin client VPN:
|
||||
|
||||
```yaml
|
||||
Resource Name: SSH Server
|
||||
Resource Type: TCP
|
||||
|
||||
# Backend Configuration
|
||||
Site: Home Lab
|
||||
Backend Address: 192.168.1.50:22
|
||||
Protocol: TCP
|
||||
|
||||
# Access Control
|
||||
Access Level: Specific Users
|
||||
Allowed Users: admin@company.com, ops@company.com
|
||||
```
|
||||
|
||||
### 3.4 Advanced Site Configuration
|
||||
|
||||
**Environment Variables (for Newt):**
|
||||
```bash
|
||||
# Custom configuration
|
||||
NEWT_CONFIG_FILE=/etc/newt/config.yml
|
||||
NEWT_LOG_LEVEL=info
|
||||
NEWT_RECONNECT_INTERVAL=10s
|
||||
```
|
||||
|
||||
**Configuration File (`/etc/newt/config.yml`):**
|
||||
```yaml
|
||||
server:
|
||||
endpoint: pangolin.yourdomain.com:51820
|
||||
id: <site_id>
|
||||
key: <site_key>
|
||||
|
||||
tunnel:
|
||||
mtu: 1420
|
||||
keepalive: 25
|
||||
persistent_keepalive: true
|
||||
|
||||
logging:
|
||||
level: info
|
||||
format: json
|
||||
output: /var/log/newt/newt.log
|
||||
|
||||
health:
|
||||
enable: true
|
||||
interval: 30s
|
||||
```
|
||||
|
||||
### 3.5 DNS Configuration for Resources
|
||||
|
||||
**For HTTPS Resources:**
|
||||
|
||||
Create DNS A/CNAME records pointing to Pangolin server:
|
||||
|
||||
```
|
||||
wiki.yourdomain.com A <pangolin_server_ip>
|
||||
app1.yourdomain.com A <pangolin_server_ip>
|
||||
app2.yourdomain.com A <pangolin_server_ip>
|
||||
```
|
||||
|
||||
**Wildcard Domain (Advanced):**
|
||||
```
|
||||
*.yourdomain.com A <pangolin_server_ip>
|
||||
```
|
||||
|
||||
Configure wildcard domain in Pangolin settings to automatically route all subdomains.
|
||||
|
||||
---
|
||||
|
||||
## 4. Client Deployment (Newt Client for VPS/Mobile)
|
||||
|
||||
The Newt client serves dual purposes:
|
||||
1. **Site Connector:** Install on private networks to expose backend services
|
||||
2. **VPN Client:** Install on user devices for accessing private resources
|
||||
|
||||
### 4.1 Newt Site Connector Deployment
|
||||
|
||||
**For VPS/Server Sites:**
|
||||
|
||||
**Installation Methods:**
|
||||
|
||||
**A. Binary Installation (Linux):**
|
||||
```bash
|
||||
# Download latest release
|
||||
curl -fsSL https://get.pangolin.net/newt | sudo bash
|
||||
|
||||
# Or manual installation
|
||||
wget https://github.com/fosrl/newt/releases/latest/download/newt-linux-amd64
|
||||
chmod +x newt-linux-amd64
|
||||
sudo mv newt-linux-amd64 /usr/local/bin/newt
|
||||
|
||||
# Run with credentials from Pangolin dashboard
|
||||
newt --id <site_id> --key <site_key> --endpoint <pangolin_server>:51820
|
||||
```
|
||||
|
||||
**B. Docker Deployment (Recommended):**
|
||||
|
||||
Create `docker-compose.yml` for Newt site:
|
||||
|
||||
```yaml
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
newt:
|
||||
image: fosrl/newt:latest
|
||||
container_name: newt-site
|
||||
restart: unless-stopped
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
sysctls:
|
||||
- net.ipv4.conf.all.src_valid_mark=1
|
||||
devices:
|
||||
- /dev/net/tun
|
||||
environment:
|
||||
- NEWT_SERVER_ENDPOINT=pangolin.yourdomain.com:51820
|
||||
- NEWT_SITE_ID=<site_id>
|
||||
- NEWT_SITE_KEY=<site_key>
|
||||
volumes:
|
||||
- ./config:/etc/newt
|
||||
network_mode: host # Required for proper networking
|
||||
```
|
||||
|
||||
**C. Systemd Service (Production):**
|
||||
|
||||
Create `/etc/systemd/system/newt.service`:
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=Pangolin Newt Site Connector
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
ExecStart=/usr/local/bin/newt \
|
||||
--id <site_id> \
|
||||
--key <site_key> \
|
||||
--endpoint pangolin.yourdomain.com:51820 \
|
||||
--config /etc/newt/config.yml
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
Enable and start:
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable newt
|
||||
sudo systemctl start newt
|
||||
sudo systemctl status newt
|
||||
```
|
||||
|
||||
**D. Kubernetes Deployment:**
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: newt-site
|
||||
namespace: pangolin
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: newt-site
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: newt-site
|
||||
spec:
|
||||
hostNetwork: true
|
||||
containers:
|
||||
- name: newt
|
||||
image: fosrl/newt:latest
|
||||
securityContext:
|
||||
capabilities:
|
||||
add:
|
||||
- NET_ADMIN
|
||||
env:
|
||||
- name: NEWT_SERVER_ENDPOINT
|
||||
value: "pangolin.yourdomain.com:51820"
|
||||
- name: NEWT_SITE_ID
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: newt-credentials
|
||||
key: site-id
|
||||
- name: NEWT_SITE_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: newt-credentials
|
||||
key: site-key
|
||||
```
|
||||
|
||||
### 4.2 Newt VPN Client Deployment
|
||||
|
||||
**For End-User Devices (Accessing Private Resources):**
|
||||
|
||||
**A. Desktop Clients (Windows/Mac/Linux):**
|
||||
|
||||
Download from Pangolin dashboard or releases page:
|
||||
- Windows: `newt-windows-amd64.exe`
|
||||
- macOS: `newt-darwin-amd64` (Intel) or `newt-darwin-arm64` (Apple Silicon)
|
||||
- Linux: `newt-linux-amd64`
|
||||
|
||||
**Installation Steps:**
|
||||
|
||||
1. Create client in Pangolin dashboard:
|
||||
- Navigate to **Clients** → **Add Client**
|
||||
- Configure client settings:
|
||||
```yaml
|
||||
Client Name: John's Laptop
|
||||
Client Type: VPN Client
|
||||
Allowed Resources: SSH Server, Database Server
|
||||
User: john@company.com
|
||||
```
|
||||
|
||||
2. Copy generated credentials
|
||||
|
||||
3. Run Newt client:
|
||||
```bash
|
||||
newt --mode client \
|
||||
--id <client_id> \
|
||||
--key <client_key> \
|
||||
--endpoint pangolin.yourdomain.com:51820
|
||||
```
|
||||
|
||||
**B. Mobile Clients (iOS/Android):**
|
||||
|
||||
Currently, mobile clients use standard WireGuard configuration:
|
||||
|
||||
1. Generate WireGuard config in Pangolin dashboard
|
||||
2. Export configuration as QR code or config file
|
||||
3. Import into WireGuard mobile app:
|
||||
- iOS: WireGuard from App Store
|
||||
- Android: WireGuard from Google Play
|
||||
|
||||
**Example WireGuard Config:**
|
||||
```ini
|
||||
[Interface]
|
||||
PrivateKey = <client_private_key>
|
||||
Address = 10.0.2.5/24
|
||||
DNS = 1.1.1.1
|
||||
|
||||
[Peer]
|
||||
PublicKey = <pangolin_server_public_key>
|
||||
Endpoint = pangolin.yourdomain.com:51820
|
||||
AllowedIPs = 10.0.0.0/16, 192.168.1.0/24
|
||||
PersistentKeepalive = 25
|
||||
```
|
||||
|
||||
**C. Configuration Management:**
|
||||
|
||||
**Client Configuration File (`config.yml`):**
|
||||
```yaml
|
||||
client:
|
||||
id: <client_id>
|
||||
key: <client_key>
|
||||
name: Johns-Laptop
|
||||
|
||||
server:
|
||||
endpoint: pangolin.yourdomain.com:51820
|
||||
public_key: <server_public_key>
|
||||
|
||||
tunnel:
|
||||
mtu: 1420
|
||||
dns_servers:
|
||||
- 1.1.1.1
|
||||
- 8.8.8.8
|
||||
routes:
|
||||
- 10.0.0.0/16 # Pangolin internal network
|
||||
- 192.168.1.0/24 # Home lab network
|
||||
|
||||
access:
|
||||
auto_connect: true
|
||||
reconnect_on_failure: true
|
||||
kill_switch: false # Block internet if VPN disconnects
|
||||
```
|
||||
|
||||
### 4.3 Updating Newt Clients
|
||||
|
||||
**Manual Update:**
|
||||
```bash
|
||||
# Stop service
|
||||
sudo systemctl stop newt
|
||||
|
||||
# Download latest version
|
||||
curl -fsSL https://get.pangolin.net/newt | sudo bash
|
||||
|
||||
# Restart service
|
||||
sudo systemctl start newt
|
||||
```
|
||||
|
||||
**Docker Update:**
|
||||
```bash
|
||||
docker compose pull
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
**Auto-Update Configuration:**
|
||||
```yaml
|
||||
updates:
|
||||
auto_update: true
|
||||
channel: stable # or beta, nightly
|
||||
check_interval: 24h
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Integration Points with Authentik
|
||||
|
||||
Based on research, here's how Pangolin integrates with Authentik for your use case:
|
||||
|
||||
### 5.1 Authentik Configuration
|
||||
|
||||
**Create OAuth2/OIDC Provider in Authentik:**
|
||||
|
||||
1. In Authentik Admin Interface:
|
||||
- Navigate to **Applications** → **Providers** → **Create Provider**
|
||||
- Select **OAuth2/OpenID Provider**
|
||||
|
||||
2. Configure Provider:
|
||||
```yaml
|
||||
Name: Pangolin
|
||||
Authorization Flow: default-provider-authorization-implicit-consent
|
||||
Client Type: Confidential
|
||||
Client ID: <generate_or_custom>
|
||||
Client Secret: <generate_secure_secret>
|
||||
|
||||
Redirect URIs:
|
||||
- https://pangolin.photon.obnh.io/api/v1/auth/callback
|
||||
|
||||
Scopes:
|
||||
- openid
|
||||
- profile
|
||||
- email
|
||||
- groups
|
||||
|
||||
Subject Mode: Based on User's hashed ID
|
||||
Include claims in id_token: Yes
|
||||
```
|
||||
|
||||
3. Create Application:
|
||||
- Navigate to **Applications** → **Applications** → **Create**
|
||||
```yaml
|
||||
Name: Pangolin
|
||||
Slug: pangolin
|
||||
Provider: Pangolin (from step 2)
|
||||
Launch URL: https://pangolin.photon.obnh.io
|
||||
```
|
||||
|
||||
### 5.2 Pangolin Configuration for Authentik
|
||||
|
||||
In Pangolin Dashboard:
|
||||
|
||||
1. Navigate to **Settings** → **Identity Providers** → **Add Identity Provider**
|
||||
2. Select **Generic OAuth2/OIDC**
|
||||
|
||||
```yaml
|
||||
Provider Name: Authentik
|
||||
Provider Type: OAuth2/OIDC
|
||||
|
||||
# Discovery
|
||||
OIDC Discovery URL: https://auth.photon.obnh.io/application/o/pangolin/.well-known/openid-configuration
|
||||
|
||||
# Or manual configuration
|
||||
Authorization Endpoint: https://auth.photon.obnh.io/application/o/authorize/
|
||||
Token Endpoint: https://auth.photon.obnh.io/application/o/token/
|
||||
Userinfo Endpoint: https://auth.photon.obnh.io/application/o/userinfo/
|
||||
JWKS URI: https://auth.photon.obnh.io/application/o/pangolin/jwks/
|
||||
|
||||
# Credentials
|
||||
Client ID: <from_authentik>
|
||||
Client Secret: <from_authentik>
|
||||
|
||||
# Scopes
|
||||
Scopes: openid profile email groups
|
||||
|
||||
# Claims Mapping
|
||||
Username Claim: preferred_username
|
||||
Email Claim: email
|
||||
Display Name Claim: name
|
||||
Groups Claim: groups
|
||||
|
||||
# Provisioning
|
||||
Auto-provision Users: Enabled
|
||||
Default Role: user
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Production Deployment Checklist
|
||||
|
||||
### 6.1 Security Hardening
|
||||
|
||||
- [ ] Enable firewall with only required ports open (80, 443, 51820)
|
||||
- [ ] Use strong passwords and rotate secrets regularly
|
||||
- [ ] Enable 2FA/MFA for admin accounts
|
||||
- [ ] Configure rate limiting in Traefik
|
||||
- [ ] Enable HTTPS-only (HSTS headers)
|
||||
- [ ] Implement fail2ban or CrowdSec for intrusion detection
|
||||
- [ ] Regular security updates and patching
|
||||
- [ ] Audit logs enabled and monitored
|
||||
|
||||
### 6.2 Backup Strategy
|
||||
|
||||
**Critical Data to Backup:**
|
||||
- `/data/pangolin/` - Application data and database
|
||||
- `/data/traefik/acme.json` - SSL certificates
|
||||
- `/config/` - Configuration files
|
||||
- Database dumps (if using PostgreSQL)
|
||||
|
||||
**Backup Script Example:**
|
||||
```bash
|
||||
#!/bin/bash
|
||||
BACKUP_DIR="/backups/pangolin-$(date +%Y%m%d)"
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
# Stop services
|
||||
docker compose stop
|
||||
|
||||
# Backup data
|
||||
tar -czf "$BACKUP_DIR/pangolin-data.tar.gz" /opt/pangolin/data/
|
||||
tar -czf "$BACKUP_DIR/pangolin-config.tar.gz" /opt/pangolin/config/
|
||||
|
||||
# Backup database
|
||||
docker exec pangolin-db pg_dump -U pangolin pangolin > "$BACKUP_DIR/database.sql"
|
||||
|
||||
# Start services
|
||||
docker compose start
|
||||
|
||||
# Retain last 7 days
|
||||
find /backups -name "pangolin-*" -mtime +7 -exec rm -rf {} \;
|
||||
```
|
||||
|
||||
### 6.3 Monitoring
|
||||
|
||||
**Metrics to Monitor:**
|
||||
- WireGuard tunnel status
|
||||
- Active client connections
|
||||
- Resource health checks
|
||||
- Certificate expiration (Let's Encrypt)
|
||||
- Disk space and memory usage
|
||||
- Authentication failures
|
||||
|
||||
**Integration Options:**
|
||||
- Prometheus + Grafana for metrics
|
||||
- ELK stack for log aggregation
|
||||
- Uptime monitoring (UptimeRobot, Healthchecks.io)
|
||||
|
||||
---
|
||||
|
||||
## 7. Common Use Cases
|
||||
|
||||
### 7.1 Home Lab Exposure
|
||||
- Expose home services (Jellyfin, Home Assistant, Plex) without port forwarding
|
||||
- Centralized authentication via Authentik
|
||||
- SSL certificates automatically managed
|
||||
|
||||
### 7.2 Multi-Site Corporate Network
|
||||
- Connect multiple office locations
|
||||
- Centralized access control for all resources
|
||||
- VPN access for remote employees
|
||||
|
||||
### 7.3 Hybrid Cloud Architecture
|
||||
- Bridge on-premises and cloud resources
|
||||
- Single pane of glass for access management
|
||||
- Secure communication between cloud regions
|
||||
|
||||
---
|
||||
|
||||
## 8. Troubleshooting
|
||||
|
||||
### 8.1 Common Issues
|
||||
|
||||
**Newt Cannot Connect to Server:**
|
||||
- Verify UDP port 51820 is open on firewall
|
||||
- Check WireGuard kernel module: `lsmod | grep wireguard`
|
||||
- Verify credentials are correct
|
||||
- Check logs: `docker logs newt-site`
|
||||
|
||||
**SSL Certificate Issues:**
|
||||
- Ensure DNS points to server IP
|
||||
- Verify ports 80/443 are accessible
|
||||
- Check Let's Encrypt rate limits
|
||||
- Review Traefik logs: `docker logs traefik`
|
||||
|
||||
**Authentication Failures:**
|
||||
- Verify IdP redirect URIs match exactly
|
||||
- Check client ID/secret are correct
|
||||
- Review Pangolin logs for OIDC errors
|
||||
- Test IdP discovery URL is accessible
|
||||
|
||||
### 8.2 Logging
|
||||
|
||||
**Enable Debug Logging:**
|
||||
```yaml
|
||||
# In pangolin.yml
|
||||
logging:
|
||||
level: debug
|
||||
format: json
|
||||
outputs:
|
||||
- type: file
|
||||
path: /app/logs/pangolin.log
|
||||
- type: stdout
|
||||
```
|
||||
|
||||
**View Logs:**
|
||||
```bash
|
||||
# Pangolin
|
||||
docker logs -f pangolin
|
||||
|
||||
# Newt
|
||||
docker logs -f newt-site
|
||||
|
||||
# Traefik
|
||||
docker logs -f traefik
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Additional Resources
|
||||
|
||||
### Official Documentation
|
||||
- Main Documentation: https://docs.pangolin.net/
|
||||
- GitHub Repository: https://github.com/fosrl/pangolin
|
||||
- Newt Client: https://github.com/fosrl/newt
|
||||
- Community Discord: https://discord.gg/pangolin
|
||||
|
||||
### Integration Guides
|
||||
- Authentik Integration: https://docs.goauthentik.io/integrations/services/pangolin/
|
||||
- Authelia Integration: https://www.authelia.com/integration/openid-connect/clients/pangolin/
|
||||
- CrowdSec Integration: https://www.crowdsec.net/blog/web-defense-with-pangolin-and-crowdsec
|
||||
|
||||
### Community Resources
|
||||
- Unraid Deployment Guide: https://forums.unraid.net/topic/193431-secure-external-access-to-plex-more-using-pangolin-tunneled-reverse-proxy/
|
||||
- Docker Compose Examples: https://github.com/fosrl/pangolin/blob/main/docker-compose.yml
|
||||
- YouTube Tutorials: Search "Pangolin self-hosting" for video guides
|
||||
|
||||
---
|
||||
|
||||
## 10. Conclusion
|
||||
|
||||
Pangolin provides a robust, self-hosted alternative to managed tunneling services with enterprise-grade features:
|
||||
|
||||
**Key Advantages:**
|
||||
- Complete data ownership and control
|
||||
- Identity-aware access control with OIDC/OAuth2
|
||||
- No port forwarding required on client networks
|
||||
- Automatic SSL certificate management
|
||||
- Centralized management for distributed resources
|
||||
- Open-source and community-driven
|
||||
|
||||
**Ideal for:**
|
||||
- Home lab enthusiasts requiring secure external access
|
||||
- Organizations needing multi-site connectivity
|
||||
- Teams requiring centralized identity management
|
||||
- Privacy-conscious users avoiding third-party services
|
||||
|
||||
**Next Steps for Implementation:**
|
||||
1. Provision VPS with required specifications
|
||||
2. Configure DNS records for Pangolin domain
|
||||
3. Run installation script or deploy via Docker Compose
|
||||
4. Configure Authentik OIDC integration
|
||||
5. Deploy Newt clients on target networks
|
||||
6. Define resources and access policies
|
||||
7. Test connectivity and authentication flows
|
||||
8. Implement monitoring and backup strategies
|
||||
|
||||
This research provides a comprehensive foundation for deploying Pangolin as a self-hosted control plane integrated with Authentik for identity management.
|
||||
348
.tasks/artifacts/traefik-analysis.md
Normal file
348
.tasks/artifacts/traefik-analysis.md
Normal file
@@ -0,0 +1,348 @@
|
||||
# Traefik Configuration Analysis
|
||||
|
||||
**Analysis Date:** 2025-01-20
|
||||
**Source:** /srv/docker/traefik/
|
||||
**Purpose:** Document Traefik configuration for Authentik and Pangolin integration
|
||||
|
||||
---
|
||||
|
||||
## Docker Network Configuration
|
||||
|
||||
### Primary Network
|
||||
- **Network Name:** `traefik`
|
||||
- **Type:** External bridge network
|
||||
- **Subnet:** 172.19.0.0/16
|
||||
- **Gateway:** 172.19.0.1
|
||||
- **Network ID:** fffc2e33eb02203ed2ebd30725098d0ef8c7d5b0890fdf9a6d4aba5eb0784cc8
|
||||
|
||||
### Connected Containers
|
||||
Currently attached services on the traefik network:
|
||||
- traefik (172.19.0.3)
|
||||
- transmission (172.19.0.2)
|
||||
- openwebui (172.19.0.4)
|
||||
- static-site-baumert-cc (172.19.0.5)
|
||||
- static-sites (172.19.0.6)
|
||||
- jellyfin (172.19.0.7)
|
||||
|
||||
**CRITICAL:** All services that need Traefik routing must be connected to the `traefik` external network.
|
||||
|
||||
---
|
||||
|
||||
## Certificate Resolver Configuration
|
||||
|
||||
### ACME Settings
|
||||
- **Resolver Name:** `letsencrypt`
|
||||
- **Challenge Type:** DNS-01 (Exoscale)
|
||||
- **Provider:** exoscale
|
||||
- **DNS Resolvers:** 1.1.1.1:53, 8.8.8.8:53
|
||||
- **Delay Before Check:** 30s
|
||||
- **ACME Email:** admin@obr.digital
|
||||
- **Storage Path:** /letsencrypt/acme.json
|
||||
|
||||
### Environment Variables Required
|
||||
```bash
|
||||
EXOSCALE_API_KEY=EXO7929dee61aff39dce8dd104a
|
||||
EXOSCALE_API_SECRET=vgfgnK3fmB-76v-YrZSQuu6Q_gBY6OMsOP_QuWWcr1A
|
||||
```
|
||||
|
||||
**Note:** DNS-01 challenge allows wildcard certificates and works without requiring ports 80/443 to be publicly accessible during certificate issuance.
|
||||
|
||||
---
|
||||
|
||||
## Traefik Label Patterns
|
||||
|
||||
### Standard Service Pattern (Docker Labels)
|
||||
|
||||
Example from transmission service:
|
||||
```yaml
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.transmission.rule=Host(`transmission.local.obr.digital`)"
|
||||
- "traefik.http.routers.transmission.entrypoints=websecure"
|
||||
- "traefik.http.routers.transmission.tls=true"
|
||||
- "traefik.http.routers.transmission.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.routers.transmission.service=transmission"
|
||||
- "traefik.http.services.transmission.loadbalancer.server.port=9091"
|
||||
```
|
||||
|
||||
Example from openwebui service:
|
||||
```yaml
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.openwebui.rule=Host(`openwebui.local.obr.digital`)"
|
||||
- "traefik.http.routers.openwebui.entrypoints=websecure"
|
||||
- "traefik.http.routers.openwebui.tls=true"
|
||||
- "traefik.http.routers.openwebui.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.services.openwebui.loadbalancer.server.port=8080"
|
||||
- "traefik.docker.network=traefik"
|
||||
```
|
||||
|
||||
### Label Pattern Template
|
||||
```yaml
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.<service-name>.rule=Host(`<domain>`)"
|
||||
- "traefik.http.routers.<service-name>.entrypoints=websecure"
|
||||
- "traefik.http.routers.<service-name>.tls=true"
|
||||
- "traefik.http.routers.<service-name>.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.services.<service-name>.loadbalancer.server.port=<port>"
|
||||
- "traefik.docker.network=traefik" # Required when service uses multiple networks
|
||||
```
|
||||
|
||||
### Middleware Pattern (Basic Auth)
|
||||
From Traefik dashboard configuration:
|
||||
```yaml
|
||||
labels:
|
||||
- "traefik.http.middlewares.dashboard-auth.basicauth.users=admin:$$apr1$$Di.EvXJZ$$z3Tc1Oss4W3nx/enE0gk71"
|
||||
- "traefik.http.routers.dashboard.middlewares=dashboard-auth"
|
||||
```
|
||||
|
||||
**Note:** Dollar signs must be doubled (`$$`) in docker-compose files to escape them properly.
|
||||
|
||||
---
|
||||
|
||||
## Entry Points
|
||||
|
||||
### Configured Entry Points
|
||||
1. **web** (Port 80)
|
||||
- Automatic redirect to HTTPS
|
||||
- Permanent redirect (301)
|
||||
- Scheme: https
|
||||
|
||||
2. **websecure** (Port 443)
|
||||
- TLS enabled by default
|
||||
- Primary entry point for all services
|
||||
|
||||
3. **metrics** (Port 8082)
|
||||
- Prometheus metrics endpoint
|
||||
- Internal only (not exposed in ports)
|
||||
|
||||
### Entry Point Configuration
|
||||
```yaml
|
||||
command:
|
||||
- "--entrypoints.web.address=:80"
|
||||
- "--entrypoints.web.http.redirections.entrypoint.to=websecure"
|
||||
- "--entrypoints.web.http.redirections.entrypoint.scheme=https"
|
||||
- "--entrypoints.web.http.redirections.entrypoint.permanent=true"
|
||||
- "--entrypoints.websecure.address=:443"
|
||||
- "--entrypoints.websecure.http.tls=true"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Dynamic Configuration Structure
|
||||
|
||||
### File Location
|
||||
- **Path:** /srv/docker/traefik/traefik_dynamic.yaml
|
||||
- **Mount:** Read-only volume in Traefik container
|
||||
- **Provider:** File provider enabled in Traefik config
|
||||
|
||||
### Dynamic Configuration Pattern
|
||||
The dynamic configuration file defines routers and services in YAML format:
|
||||
|
||||
```yaml
|
||||
http:
|
||||
routers:
|
||||
<router-name>:
|
||||
rule: "Host(`<domain>`)"
|
||||
service: <service-name>
|
||||
priority: 100
|
||||
entryPoints:
|
||||
- websecure
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
|
||||
services:
|
||||
<service-name>:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://<container-name>:<port>"
|
||||
```
|
||||
|
||||
### Current Dynamic Routes
|
||||
The dynamic configuration currently handles:
|
||||
- Static sites (1.obr.sh, fw.obr.sh, brn.obnh.io)
|
||||
- Pi-hole admin (dns.obnh.io, dns.obnh.network) - routes to host.docker.internal:8080
|
||||
- Transmission (tor.obnh.org, tor.obnh.network)
|
||||
- OpenWebUI (ll.obr.sh)
|
||||
- HTTPS fallback router (priority: 1)
|
||||
|
||||
### Service Backend Patterns
|
||||
1. **Docker containers:** `http://<container-name>:<port>`
|
||||
2. **Host services:** `http://host.docker.internal:<port>`
|
||||
|
||||
---
|
||||
|
||||
## Traefik Provider Configuration
|
||||
|
||||
### Docker Provider
|
||||
```yaml
|
||||
command:
|
||||
- "--providers.docker=true"
|
||||
- "--providers.docker.exposedbydefault=false"
|
||||
- "--providers.docker.network=traefik"
|
||||
```
|
||||
|
||||
**Key Points:**
|
||||
- Services must explicitly set `traefik.enable=true`
|
||||
- Default network for label-based routing is `traefik`
|
||||
- Docker socket mounted read-only for security
|
||||
|
||||
### File Provider
|
||||
```yaml
|
||||
command:
|
||||
- "--providers.file.filename=/traefik_dynamic.yaml"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API and Dashboard
|
||||
|
||||
### Dashboard Access
|
||||
- **URL:** https://traefik.local.obr.digital
|
||||
- **Authentication:** Basic Auth (admin:obr2024)
|
||||
- **Router:** dashboard
|
||||
- **Service:** api@internal
|
||||
- **TLS:** Let's Encrypt certificate
|
||||
|
||||
### API Configuration
|
||||
```yaml
|
||||
command:
|
||||
- "--api.dashboard=true"
|
||||
- "--api.insecure=false"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Recommendations for Authentik Integration
|
||||
|
||||
### 1. Network Configuration
|
||||
Authentik services must be added to the `traefik` external network:
|
||||
```yaml
|
||||
networks:
|
||||
- traefik
|
||||
- authentik-internal # For PostgreSQL/Redis internal communication
|
||||
```
|
||||
|
||||
### 2. Required Labels for Authentik Server
|
||||
```yaml
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.authentik.rule=Host(`auth.obr.digital`)"
|
||||
- "traefik.http.routers.authentik.entrypoints=websecure"
|
||||
- "traefik.http.routers.authentik.tls=true"
|
||||
- "traefik.http.routers.authentik.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.services.authentik.loadbalancer.server.port=9000"
|
||||
- "traefik.docker.network=traefik"
|
||||
```
|
||||
|
||||
### 3. Forward Auth Middleware Pattern
|
||||
For protecting services with Authentik SSO:
|
||||
```yaml
|
||||
labels:
|
||||
- "traefik.http.middlewares.authentik.forwardauth.address=http://authentik-server:9000/outpost.goauthentik.io/auth/traefik"
|
||||
- "traefik.http.middlewares.authentik.forwardauth.trustForwardHeader=true"
|
||||
- "traefik.http.middlewares.authentik.forwardauth.authResponseHeaders=X-authentik-username,X-authentik-groups,X-authentik-email,X-authentik-name,X-authentik-uid"
|
||||
- "traefik.http.routers.<service>.middlewares=authentik"
|
||||
```
|
||||
|
||||
### 4. Network Declaration in docker-compose.yml
|
||||
```yaml
|
||||
networks:
|
||||
traefik:
|
||||
external: true
|
||||
authentik-internal:
|
||||
driver: bridge
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Recommendations for Pangolin Integration
|
||||
|
||||
### 1. Service Labels
|
||||
```yaml
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.pangolin.rule=Host(`pangolin.obr.digital`)"
|
||||
- "traefik.http.routers.pangolin.entrypoints=websecure"
|
||||
- "traefik.http.routers.pangolin.tls=true"
|
||||
- "traefik.http.routers.pangolin.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.services.pangolin.loadbalancer.server.port=8000"
|
||||
- "traefik.http.routers.pangolin.middlewares=authentik"
|
||||
- "traefik.docker.network=traefik"
|
||||
```
|
||||
|
||||
### 2. Network Configuration
|
||||
```yaml
|
||||
networks:
|
||||
- traefik
|
||||
- pangolin-internal # For PostgreSQL internal communication
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### 1. Docker Socket Access
|
||||
- Traefik has read-only access to Docker socket
|
||||
- No containers can modify Docker state through Traefik
|
||||
|
||||
### 2. Exposed by Default
|
||||
- `exposedbydefault=false` requires explicit opt-in via labels
|
||||
- Prevents accidental service exposure
|
||||
|
||||
### 3. TLS Configuration
|
||||
- All services force HTTPS via websecure entry point
|
||||
- HTTP automatically redirects to HTTPS (permanent 301)
|
||||
- Let's Encrypt certificates auto-renewed
|
||||
|
||||
### 4. Metrics
|
||||
- Prometheus metrics on port 8082
|
||||
- Not exposed externally in port mappings
|
||||
- Can be accessed internally by monitoring containers
|
||||
|
||||
---
|
||||
|
||||
## Files Reference
|
||||
|
||||
### Configuration Files
|
||||
1. `/srv/docker/traefik/docker-compose.yml` - Main Traefik service definition
|
||||
2. `/srv/docker/traefik/traefik_dynamic.yaml` - Dynamic file-based configuration
|
||||
3. `/srv/docker/traefik/.env` - Exoscale API credentials
|
||||
4. `/srv/docker/traefik/letsencrypt/acme.json` - Certificate storage (auto-generated)
|
||||
|
||||
### Example Service Configurations
|
||||
1. `/srv/docker/transmission/docker-compose.yml` - Full label-based routing
|
||||
2. `/srv/docker/openwebui/docker-compose.yml` - Multi-network service with Traefik
|
||||
3. `/srv/docker/static-site/docker-compose.yml` - Minimal service (uses dynamic config)
|
||||
|
||||
---
|
||||
|
||||
## Summary for Integration Tasks
|
||||
|
||||
### For TASK-003 (Authentik Deployment)
|
||||
- Use external network: `traefik`
|
||||
- Certificate resolver: `letsencrypt`
|
||||
- Entry point: `websecure`
|
||||
- Suggested domain: `auth.obr.digital`
|
||||
- Required port: 9000 (Authentik server)
|
||||
- Add `traefik.docker.network=traefik` label (multi-network scenario)
|
||||
|
||||
### For TASK-008 (Pangolin Deployment)
|
||||
- Use external network: `traefik`
|
||||
- Certificate resolver: `letsencrypt`
|
||||
- Entry point: `websecure`
|
||||
- Suggested domain: `pangolin.obr.digital`
|
||||
- Required port: 8000 (typical Django/FastAPI port)
|
||||
- Apply Authentik forward auth middleware
|
||||
- Add `traefik.docker.network=traefik` label (multi-network scenario)
|
||||
|
||||
### Network Connectivity
|
||||
Both services must declare:
|
||||
```yaml
|
||||
networks:
|
||||
traefik:
|
||||
external: true
|
||||
```
|
||||
|
||||
This ensures they can communicate with Traefik and be discovered by its Docker provider.
|
||||
Reference in New Issue
Block a user