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:
19
.gitignore
vendored
Normal file
19
.gitignore
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Secrets - never commit
|
||||||
|
*.env
|
||||||
|
*-creds.env
|
||||||
|
oidc-pangolin.txt
|
||||||
|
|
||||||
|
# Archives - historical iterations
|
||||||
|
.archive/
|
||||||
|
|
||||||
|
# Claude local state
|
||||||
|
.claude/
|
||||||
|
|
||||||
|
# Editor
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
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.
|
||||||
80
.tasks/state.json
Normal file
80
.tasks/state.json
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
{
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"id": "TASK-001",
|
||||||
|
"name": "Analyze Traefik configuration",
|
||||||
|
"status": "completed",
|
||||||
|
"completed_at": "2026-01-20T21:15:00+00:00",
|
||||||
|
"artifacts": [
|
||||||
|
"/home/olaf/pangolin/.tasks/artifacts/traefik-analysis.md"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-002",
|
||||||
|
"name": "Generate Authentik secrets",
|
||||||
|
"status": "completed",
|
||||||
|
"completed_at": "2026-01-20T20:47:00+00:00",
|
||||||
|
"artifacts": [
|
||||||
|
"/home/olaf/pangolin/.tasks/artifacts/authentik.env"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TASK-004",
|
||||||
|
"name": "Create DNS records for SSO control plane",
|
||||||
|
"status": "completed",
|
||||||
|
"completed_at": "2026-01-20T21:02:00+00:00",
|
||||||
|
"artifacts": [
|
||||||
|
"sso.obr.sh \u2192 31.24.10.184 (A) and 2001:8e0:9ff:21a0:12ff:e0ff:fe3c:3616 (AAAA)",
|
||||||
|
"tunnel.obr.sh \u2192 31.24.10.184 (A) and 2001:8e0:9ff:21a0:12ff:e0ff:fe3c:3616 (AAAA)",
|
||||||
|
"remote.obr.sh \u2192 31.24.10.184 (A) and 2001:8e0:9ff:21a0:12ff:e0ff:fe3c:3616 (AAAA)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "RESEARCH-002",
|
||||||
|
"name": "Research Authentik best practices for single-user/admin deployments",
|
||||||
|
"status": "completed",
|
||||||
|
"completed_at": "2026-01-20T21:15:00+00:00",
|
||||||
|
"artifacts": [
|
||||||
|
"/home/olaf/pangolin/.tasks/artifacts/authentik-research.md"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "RESEARCH-004",
|
||||||
|
"name": "Jellyfin SSO-Auth Plugin Research",
|
||||||
|
"status": "completed",
|
||||||
|
"completed_at": "2026-01-20T20:50:00+00:00",
|
||||||
|
"artifacts": [
|
||||||
|
"/home/olaf/pangolin/.tasks/artifacts/jellyfin-sso-research.md"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "RESEARCH-001",
|
||||||
|
"name": "Research Pangolin self-hosting documentation",
|
||||||
|
"status": "completed",
|
||||||
|
"completed_at": "2026-01-20T21:30:00+00:00",
|
||||||
|
"artifacts": [
|
||||||
|
"/home/olaf/pangolin/.tasks/artifacts/pangolin-research.md"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "RESEARCH-003",
|
||||||
|
"name": "Research Apache Guacamole OIDC integration with Authentik",
|
||||||
|
"status": "completed",
|
||||||
|
"completed_at": "2026-01-20T21:40:00+00:00",
|
||||||
|
"artifacts": [
|
||||||
|
"/home/olaf/pangolin/.tasks/artifacts/guacamole-research.md"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "RESEARCH-005",
|
||||||
|
"name": "OpenWebUI OIDC Integration Variables Research",
|
||||||
|
"status": "completed",
|
||||||
|
"completed_at": "2026-01-20T21:35:00+00:00",
|
||||||
|
"artifacts": [
|
||||||
|
"/home/olaf/pangolin/.tasks/artifacts/openwebui-research.md"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"started_at": "2026-01-20T20:45:25+00:00",
|
||||||
|
"plan": "Pangolin+Authentik+Guacamole SSO Implementation"
|
||||||
|
}
|
||||||
763
ADD-OIDC-INTEGRATIONS.md
Normal file
763
ADD-OIDC-INTEGRATIONS.md
Normal file
@@ -0,0 +1,763 @@
|
|||||||
|
# OIDC Integration Guide - Complete Reference
|
||||||
|
|
||||||
|
**Created:** 2026-01-20 21:27:00+00:00
|
||||||
|
**Purpose:** Step-by-step guide to add SSO to all deployed platforms
|
||||||
|
|
||||||
|
**Prerequisites:**
|
||||||
|
- ✅ Authentik admin account created
|
||||||
|
- ✅ Pangolin admin account created
|
||||||
|
- ✅ Guacamole password changed
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step-by-Step Integration Process
|
||||||
|
|
||||||
|
### Phase 1: Create OIDC Providers in Authentik
|
||||||
|
|
||||||
|
**Login to Authentik Admin:** https://sso.obr.sh/if/admin
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Provider 1: Pangolin Control Plane
|
||||||
|
|
||||||
|
**Navigation:** Admin → Applications → Providers → Create
|
||||||
|
|
||||||
|
**Configuration:**
|
||||||
|
```
|
||||||
|
Provider Type: OAuth2/OpenID Provider
|
||||||
|
Name: Pangolin Control Plane
|
||||||
|
|
||||||
|
Client Configuration:
|
||||||
|
Client Type: Confidential
|
||||||
|
Client ID: (auto-generated - COPY THIS)
|
||||||
|
Client Secret: (click Show - COPY THIS)
|
||||||
|
|
||||||
|
Redirect URIs:
|
||||||
|
https://tunnel.obr.sh/api/v1/auth/callback
|
||||||
|
|
||||||
|
Scopes: (select these)
|
||||||
|
✓ openid
|
||||||
|
✓ profile
|
||||||
|
✓ email
|
||||||
|
✓ groups
|
||||||
|
|
||||||
|
Advanced Protocol Settings:
|
||||||
|
Token validity: 720 minutes (12 hours)
|
||||||
|
Signing Key: authentik Self-signed Certificate
|
||||||
|
Subject mode: Based on User's hashed ID
|
||||||
|
```
|
||||||
|
|
||||||
|
**Click Create**
|
||||||
|
|
||||||
|
**Then create Application:**
|
||||||
|
```
|
||||||
|
Name: Pangolin
|
||||||
|
Provider: (select Pangolin Control Plane provider)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Save Client ID and Secret for later:**
|
||||||
|
```bash
|
||||||
|
# Save to file
|
||||||
|
cat > /home/olaf/pangolin/pangolin-oidc-creds.env << 'EOF'
|
||||||
|
PANGOLIN_OIDC_CLIENT_ID=paste_here
|
||||||
|
PANGOLIN_OIDC_CLIENT_SECRET=paste_here
|
||||||
|
EOF
|
||||||
|
chmod 600 /home/olaf/pangolin/pangolin-oidc-creds.env
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Provider 2: Guacamole RDP Gateway
|
||||||
|
|
||||||
|
**Navigation:** Admin → Applications → Providers → Create
|
||||||
|
|
||||||
|
**Configuration:**
|
||||||
|
```
|
||||||
|
Provider Type: OAuth2/OpenID Provider
|
||||||
|
Name: Guacamole RDP Gateway
|
||||||
|
|
||||||
|
Client Configuration:
|
||||||
|
Client Type: Confidential
|
||||||
|
Client ID: (auto-generated - COPY THIS)
|
||||||
|
Client Secret: (click Show - COPY THIS)
|
||||||
|
|
||||||
|
Redirect URIs:
|
||||||
|
https://remote.obr.sh/guacamole/
|
||||||
|
|
||||||
|
Scopes:
|
||||||
|
✓ openid
|
||||||
|
✓ profile
|
||||||
|
✓ email
|
||||||
|
|
||||||
|
Advanced Protocol Settings:
|
||||||
|
Token validity: 300 minutes (5 hours - Guacamole maximum)
|
||||||
|
Signing Key: authentik Self-signed Certificate
|
||||||
|
```
|
||||||
|
|
||||||
|
**Click Create**
|
||||||
|
|
||||||
|
**Then create Application:**
|
||||||
|
```
|
||||||
|
Name: Guacamole
|
||||||
|
Provider: (select Guacamole RDP Gateway provider)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Optional: Create MFA Policy:**
|
||||||
|
```
|
||||||
|
Admin → Policies → Create
|
||||||
|
Type: Expression Policy
|
||||||
|
Name: Require TOTP for Guacamole
|
||||||
|
Expression: return True # Require MFA for all Guacamole users
|
||||||
|
|
||||||
|
Bindings:
|
||||||
|
Go to Applications → Guacamole → Policy Bindings
|
||||||
|
Add Binding → Select "Require TOTP for Guacamole"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Save credentials:**
|
||||||
|
```bash
|
||||||
|
cat > /home/olaf/pangolin/guacamole-oidc-creds.env << 'EOF'
|
||||||
|
GUAC_OIDC_CLIENT_ID=paste_here
|
||||||
|
GUAC_OIDC_CLIENT_SECRET=paste_here
|
||||||
|
EOF
|
||||||
|
chmod 600 /home/olaf/pangolin/guacamole-oidc-creds.env
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Provider 3: Jellyfin Media Server
|
||||||
|
|
||||||
|
**Configuration:**
|
||||||
|
```
|
||||||
|
Provider Type: OAuth2/OpenID Provider
|
||||||
|
Name: Jellyfin Media Server
|
||||||
|
|
||||||
|
Client Configuration:
|
||||||
|
Client Type: Confidential
|
||||||
|
Client Authentication: client_secret_post (CRITICAL!)
|
||||||
|
|
||||||
|
Redirect URIs:
|
||||||
|
https://video.obnh.io/sso/OID/redirect/Authentik
|
||||||
|
|
||||||
|
Scopes:
|
||||||
|
✓ openid
|
||||||
|
✓ profile
|
||||||
|
✓ email
|
||||||
|
✓ groups
|
||||||
|
```
|
||||||
|
|
||||||
|
**Create Groups for Jellyfin:**
|
||||||
|
```
|
||||||
|
Admin → Directory → Groups → Create
|
||||||
|
Name: jellyfin-admins
|
||||||
|
Name: jellyfin-users
|
||||||
|
```
|
||||||
|
|
||||||
|
**Save credentials to file:**
|
||||||
|
```bash
|
||||||
|
cat > /home/olaf/pangolin/jellyfin-oidc-creds.env << 'EOF'
|
||||||
|
JELLYFIN_OIDC_CLIENT_ID=paste_here
|
||||||
|
JELLYFIN_OIDC_CLIENT_SECRET=paste_here
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Provider 4: OpenWebUI
|
||||||
|
|
||||||
|
**Configuration:**
|
||||||
|
```
|
||||||
|
Provider Type: OAuth2/OpenID Provider
|
||||||
|
Name: OpenWebUI
|
||||||
|
|
||||||
|
Redirect URIs:
|
||||||
|
https://ll.obr.sh/oauth/oidc/callback
|
||||||
|
|
||||||
|
Scopes:
|
||||||
|
✓ openid
|
||||||
|
✓ profile
|
||||||
|
✓ email
|
||||||
|
✓ groups
|
||||||
|
```
|
||||||
|
|
||||||
|
**Create Groups:**
|
||||||
|
```
|
||||||
|
Admin → Directory → Groups → Create
|
||||||
|
Name: openwebui-admins
|
||||||
|
Name: openwebui-users
|
||||||
|
```
|
||||||
|
|
||||||
|
**Save credentials:**
|
||||||
|
```bash
|
||||||
|
cat > /home/olaf/pangolin/openwebui-oidc-creds.env << 'EOF'
|
||||||
|
OPENWEBUI_OIDC_CLIENT_ID=paste_here
|
||||||
|
OPENWEBUI_OIDC_CLIENT_SECRET=paste_here
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Provider 5: Gitea (fry.obr.sh)
|
||||||
|
|
||||||
|
**Configuration:**
|
||||||
|
```
|
||||||
|
Provider Type: OAuth2/OpenID Provider
|
||||||
|
Name: Gitea (fry)
|
||||||
|
|
||||||
|
Redirect URIs:
|
||||||
|
https://git.obnh.io/user/oauth2/authentik/callback
|
||||||
|
|
||||||
|
Scopes:
|
||||||
|
✓ openid
|
||||||
|
✓ profile
|
||||||
|
✓ email
|
||||||
|
```
|
||||||
|
|
||||||
|
**Save credentials:**
|
||||||
|
```bash
|
||||||
|
cat > /home/olaf/pangolin/gitea-fry-oidc-creds.env << 'EOF'
|
||||||
|
GITEA_FRY_CLIENT_ID=paste_here
|
||||||
|
GITEA_FRY_CLIENT_SECRET=paste_here
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Provider 6: Gitea (proton.obr.sh)
|
||||||
|
|
||||||
|
**Configuration:**
|
||||||
|
```
|
||||||
|
Provider Type: OAuth2/OpenID Provider
|
||||||
|
Name: Gitea (proton)
|
||||||
|
|
||||||
|
Redirect URIs:
|
||||||
|
https://git.proton.obr.sh/user/oauth2/authentik/callback
|
||||||
|
|
||||||
|
Scopes:
|
||||||
|
✓ openid
|
||||||
|
✓ profile
|
||||||
|
✓ email
|
||||||
|
```
|
||||||
|
|
||||||
|
**Save credentials:**
|
||||||
|
```bash
|
||||||
|
cat > /home/olaf/pangolin/gitea-proton-oidc-creds.env << 'EOF'
|
||||||
|
GITEA_PROTON_CLIENT_ID=paste_here
|
||||||
|
GITEA_PROTON_CLIENT_SECRET=paste_here
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 2: Add OIDC to Deployed Platforms
|
||||||
|
|
||||||
|
### Pangolin OIDC Integration
|
||||||
|
|
||||||
|
**After creating Provider 1 in Authentik:**
|
||||||
|
|
||||||
|
**1. Login to Pangolin:** https://tunnel.obr.sh
|
||||||
|
|
||||||
|
**2. Navigate to:** Settings → Identity Providers → Add Provider
|
||||||
|
|
||||||
|
**3. Select:** Generic OAuth2/OIDC
|
||||||
|
|
||||||
|
**4. Configure:**
|
||||||
|
```
|
||||||
|
Provider Name: Authentik SSO
|
||||||
|
Provider Type: Generic OIDC
|
||||||
|
|
||||||
|
Discovery URL:
|
||||||
|
https://sso.obr.sh/application/o/pangolin/.well-known/openid-configuration
|
||||||
|
|
||||||
|
Client ID: (from pangolin-oidc-creds.env)
|
||||||
|
Client Secret: (from pangolin-oidc-creds.env)
|
||||||
|
|
||||||
|
Scopes: openid profile email groups
|
||||||
|
```
|
||||||
|
|
||||||
|
**5. Save and Test:**
|
||||||
|
- Logout of Pangolin
|
||||||
|
- Click "Login with Authentik"
|
||||||
|
- Should redirect to Authentik
|
||||||
|
- Login with Authentik credentials
|
||||||
|
- Should return to Pangolin dashboard
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Guacamole OIDC Integration
|
||||||
|
|
||||||
|
**After creating Provider 2 in Authentik:**
|
||||||
|
|
||||||
|
**1. Update docker-compose.yml:**
|
||||||
|
|
||||||
|
Add these environment variables to the `guacamole` service:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
environment:
|
||||||
|
# Existing variables...
|
||||||
|
GUACD_HOSTNAME: guacd
|
||||||
|
POSTGRESQL_HOSTNAME: postgres
|
||||||
|
# ... etc
|
||||||
|
|
||||||
|
# Add OIDC configuration:
|
||||||
|
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: ${GUAC_OIDC_CLIENT_ID}
|
||||||
|
OPENID_REDIRECT_URI: https://remote.obr.sh/guacamole/
|
||||||
|
OPENID_USERNAME_CLAIM_TYPE: preferred_username
|
||||||
|
OPENID_SCOPE: openid profile email
|
||||||
|
EXTENSION_PRIORITY: openid
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. Update .env file:**
|
||||||
|
```bash
|
||||||
|
# Add to /srv/docker/guacamole/.env
|
||||||
|
cat /home/olaf/pangolin/guacamole-oidc-creds.env >> /srv/docker/guacamole/.env
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. Restart Guacamole:**
|
||||||
|
```bash
|
||||||
|
cd /srv/docker/guacamole
|
||||||
|
sudo docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
**4. Test SSO Login:**
|
||||||
|
- Go to https://remote.obr.sh/guacamole/
|
||||||
|
- Should see "Login with SSO" option
|
||||||
|
- Click it
|
||||||
|
- Should redirect to Authentik
|
||||||
|
- Login with Authentik credentials
|
||||||
|
- Should return to Guacamole dashboard
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 3: Service SSO Integrations
|
||||||
|
|
||||||
|
### Jellyfin SSO Plugin
|
||||||
|
|
||||||
|
**After creating Provider 3 in Authentik:**
|
||||||
|
|
||||||
|
**1. Install SSO-Auth Plugin:**
|
||||||
|
- Login to Jellyfin: https://video.obnh.io
|
||||||
|
- Navigate to: Dashboard → Plugins → Catalog
|
||||||
|
- Find "SSO-Auth" plugin
|
||||||
|
- Click Install
|
||||||
|
- Restart Jellyfin when prompted
|
||||||
|
|
||||||
|
**2. Configure Plugin:**
|
||||||
|
- Dashboard → Plugins → SSO-Auth → Settings
|
||||||
|
|
||||||
|
```
|
||||||
|
Provider: Generic OpenID
|
||||||
|
Provider Name: Authentik
|
||||||
|
|
||||||
|
OID Endpoint: https://sso.obr.sh/application/o/jellyfin/
|
||||||
|
Client ID: (from jellyfin-oidc-creds.env)
|
||||||
|
Client Secret: (from jellyfin-oidc-creds.env)
|
||||||
|
|
||||||
|
OID Client Authentication Method: client_secret_post
|
||||||
|
|
||||||
|
Enable Authorization by Plugin Default: Yes
|
||||||
|
Enable All Folders: Yes
|
||||||
|
|
||||||
|
Admin Role: jellyfin-admins
|
||||||
|
User Role: jellyfin-users
|
||||||
|
Role Claim: groups
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. Test:**
|
||||||
|
- Logout of Jellyfin
|
||||||
|
- Should see "Sign in with Authentik" button
|
||||||
|
- Click it
|
||||||
|
- Login with Authentik
|
||||||
|
- Should return to Jellyfin
|
||||||
|
|
||||||
|
**4. Mobile Apps:**
|
||||||
|
- Use Quick Connect (6-digit code pairing)
|
||||||
|
- OR generate API tokens in Jellyfin dashboard
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### OpenWebUI OIDC Configuration
|
||||||
|
|
||||||
|
**After creating Provider 4 in Authentik:**
|
||||||
|
|
||||||
|
**1. Edit OpenWebUI docker-compose.yml:**
|
||||||
|
|
||||||
|
Add environment variables:
|
||||||
|
```yaml
|
||||||
|
environment:
|
||||||
|
# Existing variables...
|
||||||
|
|
||||||
|
# Add OIDC configuration:
|
||||||
|
ENABLE_OAUTH_SIGNUP: "true"
|
||||||
|
OAUTH_MERGE_ACCOUNTS_BY_EMAIL: "true"
|
||||||
|
OAUTH_CLIENT_ID: ${OPENWEBUI_OIDC_CLIENT_ID}
|
||||||
|
OAUTH_CLIENT_SECRET: ${OPENWEBUI_OIDC_CLIENT_SECRET}
|
||||||
|
OPENID_PROVIDER_URL: https://sso.obr.sh/application/o/openwebui/.well-known/openid-configuration
|
||||||
|
OAUTH_SCOPES: openid profile email groups
|
||||||
|
|
||||||
|
# Role management:
|
||||||
|
ENABLE_OAUTH_ROLE_MANAGEMENT: "true"
|
||||||
|
OAUTH_ROLES_CLAIM: groups
|
||||||
|
OAUTH_ADMIN_ROLES: openwebui-admins
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. Update .env:**
|
||||||
|
```bash
|
||||||
|
cat /home/olaf/pangolin/openwebui-oidc-creds.env >> /srv/docker/openwebui/.env
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. Restart OpenWebUI:**
|
||||||
|
```bash
|
||||||
|
cd /srv/docker/openwebui
|
||||||
|
sudo docker compose restart
|
||||||
|
```
|
||||||
|
|
||||||
|
**4. Test:**
|
||||||
|
- Go to https://ll.obr.sh
|
||||||
|
- Click "Sign in with SSO"
|
||||||
|
- Login with Authentik
|
||||||
|
- Should have admin role if in openwebui-admins group
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gitea OAuth2 Configuration (Both Instances)
|
||||||
|
|
||||||
|
**After creating Providers 5 & 6 in Authentik:**
|
||||||
|
|
||||||
|
**For Gitea on fry.obr.sh:**
|
||||||
|
|
||||||
|
**1. SSH to fry:**
|
||||||
|
```bash
|
||||||
|
ssh fry.obr.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. Login to Gitea admin:**
|
||||||
|
- URL: https://git.obnh.io
|
||||||
|
- Login as admin
|
||||||
|
|
||||||
|
**3. Add Authentication Source:**
|
||||||
|
- Navigate to: Site Administration → Authentication Sources → Add Source
|
||||||
|
- Type: OAuth2
|
||||||
|
- Provider: OpenID Connect
|
||||||
|
|
||||||
|
**Configuration:**
|
||||||
|
```
|
||||||
|
Name: Authentik SSO
|
||||||
|
Provider: OpenID Connect
|
||||||
|
|
||||||
|
Client ID: (from gitea-fry-oidc-creds.env)
|
||||||
|
Client Secret: (from gitea-fry-oidc-creds.env)
|
||||||
|
|
||||||
|
Auto Discovery URL:
|
||||||
|
https://sso.obr.sh/application/o/gitea-fry/.well-known/openid-configuration
|
||||||
|
|
||||||
|
Additional Scopes: groups
|
||||||
|
Required Claim Name: (leave blank)
|
||||||
|
Group Claim Name: groups
|
||||||
|
Admin Group: gitea-admins
|
||||||
|
Restricted Group: (leave blank)
|
||||||
|
|
||||||
|
This Source is Activated: ✓
|
||||||
|
```
|
||||||
|
|
||||||
|
**4. Test:**
|
||||||
|
- Logout of Gitea
|
||||||
|
- Should see "Sign in with Authentik SSO"
|
||||||
|
- Click and login
|
||||||
|
- Should create/login user account
|
||||||
|
|
||||||
|
**Repeat for Gitea on proton.obr.sh** with gitea-proton credentials
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 4: Configure Pangolin Sites
|
||||||
|
|
||||||
|
**After Pangolin admin setup complete:**
|
||||||
|
|
||||||
|
**Login to Pangolin Dashboard:** https://tunnel.obr.sh
|
||||||
|
|
||||||
|
### Create Site: brn-home
|
||||||
|
|
||||||
|
**1. Navigate to:** Sites → Create Site
|
||||||
|
|
||||||
|
**2. Configuration:**
|
||||||
|
```
|
||||||
|
Site Name: brn-home
|
||||||
|
Description: Home LAN services (Jellyfin, OpenWebUI, Transmission, Pi-hole)
|
||||||
|
Type: Newt Site
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. Deploy Newt Client:**
|
||||||
|
|
||||||
|
The dashboard will show a connection command like:
|
||||||
|
```bash
|
||||||
|
docker run -d --name newt --cap-add NET_ADMIN \
|
||||||
|
-e SITE_TOKEN="<generated_token>" \
|
||||||
|
-e PANGOLIN_URL="https://tunnel.obr.sh" \
|
||||||
|
fosrl/newt:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
**Run this on brn** to connect local services
|
||||||
|
|
||||||
|
### Create Resources for brn-home Site
|
||||||
|
|
||||||
|
**After Newt connected:**
|
||||||
|
|
||||||
|
**Resource 1: Jellyfin**
|
||||||
|
```
|
||||||
|
Type: Public Resource (HTTPS)
|
||||||
|
Name: Jellyfin Media Server
|
||||||
|
Public Domain: video.obnh.io
|
||||||
|
Backend Address: jellyfin:8096
|
||||||
|
Protocol: HTTP
|
||||||
|
Site: brn-home
|
||||||
|
Access Control: Authenticated Users
|
||||||
|
```
|
||||||
|
|
||||||
|
**Resource 2: OpenWebUI**
|
||||||
|
```
|
||||||
|
Type: Public Resource (HTTPS)
|
||||||
|
Name: OpenWebUI AI Chat
|
||||||
|
Public Domain: ll.obr.sh
|
||||||
|
Backend Address: openwebui:8080
|
||||||
|
Protocol: HTTP
|
||||||
|
Site: brn-home
|
||||||
|
Access Control: Authenticated Users
|
||||||
|
```
|
||||||
|
|
||||||
|
**Resource 3: Transmission**
|
||||||
|
```
|
||||||
|
Type: Public Resource (HTTPS)
|
||||||
|
Name: Transmission
|
||||||
|
Public Domain: tor.obnh.network
|
||||||
|
Backend Address: transmission:9091
|
||||||
|
Protocol: HTTP
|
||||||
|
Site: brn-home
|
||||||
|
Access Control: Authenticated Users
|
||||||
|
```
|
||||||
|
|
||||||
|
**Resource 4: Pi-hole**
|
||||||
|
```
|
||||||
|
Type: Public Resource (HTTPS)
|
||||||
|
Name: Pi-hole Admin
|
||||||
|
Public Domain: dns.obnh.io
|
||||||
|
Backend Address: host.docker.internal:8080
|
||||||
|
Protocol: HTTP
|
||||||
|
Site: brn-home
|
||||||
|
Access Control: Authenticated Users
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 5: Deploy Newt to VPS Hosts
|
||||||
|
|
||||||
|
### fry.obr.sh (Gitea)
|
||||||
|
|
||||||
|
**SSH to fry:**
|
||||||
|
```bash
|
||||||
|
ssh fry.obr.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
**Create site in Pangolin dashboard:**
|
||||||
|
```
|
||||||
|
Site Name: fry-vps
|
||||||
|
Description: Gitea hosting
|
||||||
|
```
|
||||||
|
|
||||||
|
**Get Newt connection command from Pangolin**
|
||||||
|
|
||||||
|
**Run on fry:**
|
||||||
|
```bash
|
||||||
|
docker run -d --name newt --restart unless-stopped \
|
||||||
|
--cap-add NET_ADMIN \
|
||||||
|
-e SITE_TOKEN="<from_pangolin>" \
|
||||||
|
-e PANGOLIN_URL="https://tunnel.obr.sh" \
|
||||||
|
fosrl/newt:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
**Add Resource in Pangolin:**
|
||||||
|
```
|
||||||
|
Name: Gitea (fry)
|
||||||
|
Domain: git.obnh.io
|
||||||
|
Backend: gitea:3000
|
||||||
|
Site: fry-vps
|
||||||
|
```
|
||||||
|
|
||||||
|
**Repeat for proton.obr.sh and photon.obr.sh**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 6: Restrict Public WAN Access (Final Step)
|
||||||
|
|
||||||
|
**ONLY after verifying all services work through Pangolin:**
|
||||||
|
|
||||||
|
**Backup Traefik config:**
|
||||||
|
```bash
|
||||||
|
sudo cp /srv/docker/traefik/traefik_dynamic.yaml \
|
||||||
|
/srv/docker/traefik/traefik_dynamic.yaml.backup-$(date +%Y%m%d)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Edit traefik_dynamic.yaml:**
|
||||||
|
|
||||||
|
**Remove or comment out public routes for:**
|
||||||
|
- video.obnh.io (Jellyfin)
|
||||||
|
- ll.obr.sh (OpenWebUI)
|
||||||
|
- tor.obnh.network (Transmission)
|
||||||
|
|
||||||
|
**Keep public routes for:**
|
||||||
|
- sso.obr.sh (Authentik - must be public for auth)
|
||||||
|
- tunnel.obr.sh (Pangolin - must be public for tunnels)
|
||||||
|
- remote.obr.sh (Guacamole - accessible via Pangolin)
|
||||||
|
- bern.social (Mastodon - public federated)
|
||||||
|
- git.obnh.io, git.proton.obr.sh (Gitea - public with SSO)
|
||||||
|
- dns.obnh.io (Pi-hole - for VPS DNS queries)
|
||||||
|
|
||||||
|
**Reload Traefik:**
|
||||||
|
```bash
|
||||||
|
sudo docker exec traefik kill -SIGHUP 1
|
||||||
|
# Or: docker compose restart (brief downtime)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Test:**
|
||||||
|
- From internet: Jellyfin/OpenWebUI should be unreachable
|
||||||
|
- From Pangolin tunnel: Should work normally
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Automated Integration Scripts
|
||||||
|
|
||||||
|
### Script 1: Add OIDC to Pangolin
|
||||||
|
|
||||||
|
**File:** `/home/olaf/pangolin/scripts/integrate-pangolin-oidc.sh`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# Read credentials
|
||||||
|
source /home/olaf/pangolin/pangolin-oidc-creds.env
|
||||||
|
|
||||||
|
# Update Pangolin config.yml
|
||||||
|
cat >> /srv/docker/pangolin/config/config.yml << EOF
|
||||||
|
|
||||||
|
# OIDC Integration added: $(date -Iseconds)
|
||||||
|
identity_providers:
|
||||||
|
- type: oidc
|
||||||
|
name: Authentik SSO
|
||||||
|
discovery_url: https://sso.obr.sh/application/o/pangolin/.well-known/openid-configuration
|
||||||
|
client_id: ${PANGOLIN_OIDC_CLIENT_ID}
|
||||||
|
client_secret: ${PANGOLIN_OIDC_CLIENT_SECRET}
|
||||||
|
scopes:
|
||||||
|
- openid
|
||||||
|
- profile
|
||||||
|
- email
|
||||||
|
- groups
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Restart Pangolin
|
||||||
|
cd /srv/docker/pangolin
|
||||||
|
sudo docker compose restart pangolin
|
||||||
|
|
||||||
|
echo "✅ Pangolin OIDC integration added"
|
||||||
|
echo "Test at: https://tunnel.obr.sh"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Script 2: Add OIDC to Guacamole
|
||||||
|
|
||||||
|
**File:** `/home/olaf/pangolin/scripts/integrate-guacamole-oidc.sh`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# Read credentials
|
||||||
|
source /home/olaf/pangolin/guacamole-oidc-creds.env
|
||||||
|
|
||||||
|
# Add to environment file
|
||||||
|
cat >> /srv/docker/guacamole/.env << EOF
|
||||||
|
|
||||||
|
# OIDC Integration added: $(date -Iseconds)
|
||||||
|
GUAC_OIDC_CLIENT_ID=${GUAC_OIDC_CLIENT_ID}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Update docker-compose.yml (manual edit needed)
|
||||||
|
echo "⚠️ Manual step required:"
|
||||||
|
echo "Edit /srv/docker/guacamole/docker-compose.yml"
|
||||||
|
echo "Add OPENID_ environment variables"
|
||||||
|
echo "See: /home/olaf/pangolin/ADD-OIDC-INTEGRATIONS.md Phase 2"
|
||||||
|
|
||||||
|
echo "Then run:"
|
||||||
|
echo "cd /srv/docker/guacamole && sudo docker compose up -d"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Verification Checklist
|
||||||
|
|
||||||
|
**After completing all integrations:**
|
||||||
|
|
||||||
|
- [ ] Pangolin SSO login works
|
||||||
|
- [ ] Guacamole SSO login works (MFA if configured)
|
||||||
|
- [ ] Jellyfin web SSO works
|
||||||
|
- [ ] Jellyfin mobile Quick Connect works
|
||||||
|
- [ ] OpenWebUI SSO works with admin role
|
||||||
|
- [ ] Gitea fry SSO works
|
||||||
|
- [ ] Gitea proton SSO works
|
||||||
|
- [ ] Services unreachable from public WAN (if restricted)
|
||||||
|
- [ ] Services accessible via Pangolin tunnel
|
||||||
|
- [ ] LAN access still works for all services
|
||||||
|
- [ ] WAN internet routing still works
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### OIDC Login Fails
|
||||||
|
|
||||||
|
**Check:**
|
||||||
|
- Redirect URI matches exactly in Authentik provider
|
||||||
|
- Client ID and Secret are correct
|
||||||
|
- Discovery URL is accessible: `curl https://sso.obr.sh/application/o/<app>/.well-known/openid-configuration`
|
||||||
|
- Scopes include at minimum: openid, profile, email
|
||||||
|
|
||||||
|
### Services Not Accessible
|
||||||
|
|
||||||
|
**Through Pangolin:**
|
||||||
|
- Check Newt client connected in Pangolin dashboard
|
||||||
|
- Verify resource backend address correct
|
||||||
|
- Check site is online
|
||||||
|
|
||||||
|
**From LAN:**
|
||||||
|
- Should still work (direct access)
|
||||||
|
- Check Traefik dynamic config not removed
|
||||||
|
|
||||||
|
### Mobile Apps
|
||||||
|
|
||||||
|
**Jellyfin:**
|
||||||
|
- Use Quick Connect (web login + 6-digit code)
|
||||||
|
- OR API tokens (Settings → API Keys)
|
||||||
|
|
||||||
|
**Pangolin:**
|
||||||
|
- Install Pangolin mobile app
|
||||||
|
- Connect to tunnel.obr.sh
|
||||||
|
- Login with Authentik credentials
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
**This guide provides:**
|
||||||
|
- Complete OIDC provider creation steps (6 providers)
|
||||||
|
- Platform integration configurations (Pangolin, Guacamole)
|
||||||
|
- Service integration steps (Jellyfin, OpenWebUI, Gitea)
|
||||||
|
- Pangolin site/resource configuration
|
||||||
|
- Newt client deployment
|
||||||
|
- Traefik restriction procedure
|
||||||
|
- Verification checklist
|
||||||
|
- Troubleshooting guidance
|
||||||
|
|
||||||
|
**Use this after completing platform setup wizards.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**File:** `/home/olaf/pangolin/ADD-OIDC-INTEGRATIONS.md`
|
||||||
|
**Ready for:** After admin accounts created on all platforms
|
||||||
204
AUTHENTIK-SETUP-GUIDE.md
Normal file
204
AUTHENTIK-SETUP-GUIDE.md
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
# Authentik Initial Setup Guide
|
||||||
|
|
||||||
|
**Status:** ⚠️ **ACTION REQUIRED**
|
||||||
|
**Created:** 2026-01-20 20:59:00+00:00
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 1: Access Authentik Initial Setup
|
||||||
|
|
||||||
|
**URL:** https://sso.obr.sh/if/flow/initial-setup/
|
||||||
|
|
||||||
|
Navigate to this URL in your web browser. You should see the Authentik initial setup wizard.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 2: Create Admin Account
|
||||||
|
|
||||||
|
**Recommended Settings:**
|
||||||
|
- **Username:** `admin` (or your preferred admin username)
|
||||||
|
- **Email:** `admin@obr.digital`
|
||||||
|
- **Password:** *Choose a strong password (20+ characters recommended)*
|
||||||
|
|
||||||
|
**Important:**
|
||||||
|
- This is the **superuser account** for all Authentik administration
|
||||||
|
- Will be used to create OIDC providers for Pangolin, Guacamole, Jellyfin, etc.
|
||||||
|
- Cannot be recovered without recovery codes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 3: Save Recovery Codes
|
||||||
|
|
||||||
|
After creating the admin account, Authentik will display **recovery codes**.
|
||||||
|
|
||||||
|
**CRITICAL:** Save these codes in a secure location (password manager, encrypted file)
|
||||||
|
|
||||||
|
These codes allow account recovery if you lose access to MFA devices.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 4: Enable MFA (Recommended)
|
||||||
|
|
||||||
|
After initial setup, configure TOTP (Time-based One-Time Password):
|
||||||
|
|
||||||
|
1. Log in to Authentik admin: https://sso.obr.sh/if/admin
|
||||||
|
2. Navigate to your user profile (top right corner)
|
||||||
|
3. Click "Authenticators" or "MFA"
|
||||||
|
4. Add TOTP authenticator (scan QR code with authenticator app)
|
||||||
|
5. **Save backup codes**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 5: Verify Admin Access
|
||||||
|
|
||||||
|
Confirm you can:
|
||||||
|
1. Access Admin Interface: https://sso.obr.sh/if/admin
|
||||||
|
2. See the Authentik dashboard
|
||||||
|
3. Navigate to "Applications" section
|
||||||
|
4. Navigate to "Providers" section
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Next Steps (After Admin Setup Complete)
|
||||||
|
|
||||||
|
Once admin account is created, the following OIDC providers need to be configured:
|
||||||
|
|
||||||
|
### 1. Pangolin OIDC Provider
|
||||||
|
|
||||||
|
**Purpose:** Centralized identity-aware access control for tunneled services
|
||||||
|
|
||||||
|
**Configuration:**
|
||||||
|
- Navigate to: **Admin > Applications > Providers**
|
||||||
|
- Click "Create"
|
||||||
|
- **Type:** OAuth2/OIDC Provider
|
||||||
|
- **Name:** `Pangolin SSO Provider`
|
||||||
|
- **Client Type:** Confidential
|
||||||
|
- **Client ID:** `pangolin` (will be auto-generated, copy this)
|
||||||
|
- **Redirect URIs:** `https://tunnel.obr.sh/api/v1/auth/callback`
|
||||||
|
- **Scopes:** openid, profile, email, groups
|
||||||
|
|
||||||
|
**Required Scopes:**
|
||||||
|
- `openid` - Core identity
|
||||||
|
- `profile` - User profile information
|
||||||
|
- `email` - Email address
|
||||||
|
- `groups` - Group memberships for access control
|
||||||
|
|
||||||
|
**Advanced Settings:**
|
||||||
|
- **Token validity:** 720 minutes (12 hours)
|
||||||
|
- **Signing Key:** `authentik Self-signed Certificate`
|
||||||
|
|
||||||
|
**Save these values:**
|
||||||
|
- Client ID: _________________
|
||||||
|
- Client Secret: _________________
|
||||||
|
|
||||||
|
### 2. Guacamole OIDC Provider
|
||||||
|
|
||||||
|
**Purpose:** SSO for RDP gateway access
|
||||||
|
|
||||||
|
**Configuration:**
|
||||||
|
- **Name:** `Guacamole RDP Gateway`
|
||||||
|
- **Client Type:** Confidential
|
||||||
|
- **Redirect URIs:** `https://remote.obr.sh/guacamole/`
|
||||||
|
- **Scopes:** openid, profile, email
|
||||||
|
- **Token validity:** 300 minutes (5 hours, Guacamole maximum)
|
||||||
|
|
||||||
|
**MFA Policy:**
|
||||||
|
Create and bind MFA policy to require TOTP for Guacamole application:
|
||||||
|
- Navigate to: **Admin > Policies**
|
||||||
|
- Create Expression Policy: "Require TOTP for Guacamole"
|
||||||
|
- Expression: `return ak_is_group_member(request.user, name="guacamole-admins")`
|
||||||
|
- Bind to Guacamole application
|
||||||
|
|
||||||
|
### 3. Jellyfin OIDC Provider
|
||||||
|
|
||||||
|
**Purpose:** SSO for media server (web access only, mobile uses Quick Connect)
|
||||||
|
|
||||||
|
**Configuration:**
|
||||||
|
- **Name:** `Jellyfin Media Server`
|
||||||
|
- **Client Type:** Confidential
|
||||||
|
- **Client Authentication Method:** `client_secret_post` (CRITICAL for Jellyfin plugin)
|
||||||
|
- **Redirect URIs:** `https://video.obnh.io/sso/OID/redirect/Authentik`
|
||||||
|
- **Scopes:** openid, profile, email, groups
|
||||||
|
|
||||||
|
**Create Groups:**
|
||||||
|
- `jellyfin-admins` - Full admin access
|
||||||
|
- `jellyfin-users` - Regular user access
|
||||||
|
|
||||||
|
### 4. OpenWebUI OIDC Provider
|
||||||
|
|
||||||
|
**Purpose:** SSO for AI chat interface
|
||||||
|
|
||||||
|
**Configuration:**
|
||||||
|
- **Name:** `OpenWebUI`
|
||||||
|
- **Client Type:** Confidential
|
||||||
|
- **Redirect URIs:** `https://ll.obr.sh/oauth/oidc/callback`
|
||||||
|
- **Scopes:** openid, profile, email, groups
|
||||||
|
|
||||||
|
**Create Groups:**
|
||||||
|
- `openwebui-admins` - Admin role
|
||||||
|
- `openwebui-users` - Regular users
|
||||||
|
|
||||||
|
**Claims Mapping:**
|
||||||
|
Configure role claim for admin designation:
|
||||||
|
- Claim: `groups`
|
||||||
|
- Mapped to user group memberships
|
||||||
|
|
||||||
|
### 5. Gitea OIDC Providers (2 instances)
|
||||||
|
|
||||||
|
**Purpose:** SSO for self-hosted Git repositories
|
||||||
|
|
||||||
|
**Configuration for gitea-fry:**
|
||||||
|
- **Name:** `Gitea (fry.obr.sh)`
|
||||||
|
- **Redirect URIs:** `https://git.obnh.io/user/oauth2/authentik/callback`
|
||||||
|
- **Scopes:** openid, profile, email
|
||||||
|
|
||||||
|
**Configuration for gitea-proton:**
|
||||||
|
- **Name:** `Gitea (proton.obr.sh)`
|
||||||
|
- **Redirect URIs:** `https://git.proton.obr.sh/user/oauth2/authentik/callback`
|
||||||
|
- **Scopes:** openid, profile, email
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Verification Checklist
|
||||||
|
|
||||||
|
After completing setup:
|
||||||
|
|
||||||
|
- [ ] Admin account created and can login
|
||||||
|
- [ ] Recovery codes saved securely
|
||||||
|
- [ ] MFA (TOTP) configured (optional but recommended)
|
||||||
|
- [ ] Can access Admin Interface
|
||||||
|
- [ ] Ready to create OIDC providers
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
**Cannot access setup page:**
|
||||||
|
- Verify Authentik containers are healthy: `cd /srv/docker/authentik && sudo docker compose ps`
|
||||||
|
- Check Traefik routing: `curl -I https://sso.obr.sh`
|
||||||
|
- Review logs: `sudo docker compose logs server | tail -50`
|
||||||
|
|
||||||
|
**Setup page keeps loading:**
|
||||||
|
- Wait 1-2 minutes for migrations to complete (already done if from Iteration 1)
|
||||||
|
- Refresh browser
|
||||||
|
- Clear browser cache
|
||||||
|
|
||||||
|
**Lost recovery codes:**
|
||||||
|
- If you still have access, generate new codes in admin interface
|
||||||
|
- If locked out, manual database recovery required (contact for assistance)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Security Recommendations
|
||||||
|
|
||||||
|
1. **Use a password manager** - Generate and store strong password
|
||||||
|
2. **Enable MFA immediately** - Don't delay TOTP setup
|
||||||
|
3. **Save recovery codes offline** - Print or store in encrypted file
|
||||||
|
4. **Limit admin access** - Only use admin account for configuration
|
||||||
|
5. **Regular backups** - PostgreSQL database contains all configuration
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Once setup complete, notify the system to proceed with Pangolin OIDC provider creation.**
|
||||||
|
|
||||||
|
**File Location:** `/home/olaf/pangolin/AUTHENTIK-SETUP-GUIDE.md`
|
||||||
75
CLAUDE.md
Normal file
75
CLAUDE.md
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
This repository contains SSO infrastructure configuration and documentation for a self-hosted Pangolin + Authentik + Guacamole deployment on brn (10.50.0.74). It is NOT a code project - it's an infrastructure-as-documentation repository with shell scripts, configuration guides, and deployment artifacts.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
**Three platforms deployed on brn (10.50.0.74):**
|
||||||
|
|
||||||
|
| Platform | URL | Purpose | Docker Path |
|
||||||
|
|----------|-----|---------|-------------|
|
||||||
|
| Authentik | https://sso.obr.sh | Central OIDC identity provider | /srv/docker/authentik |
|
||||||
|
| Pangolin | https://tunnel.obr.sh | WireGuard tunnel manager + identity-aware proxy | /srv/docker/pangolin |
|
||||||
|
| Guacamole | https://remote.obr.sh/guacamole/ | Clientless RDP gateway | /srv/docker/guacamole |
|
||||||
|
|
||||||
|
**Protected services (SSO integration targets):**
|
||||||
|
- Jellyfin (video.obnh.io) - Media server
|
||||||
|
- OpenWebUI (ll.obr.sh) - AI chat interface
|
||||||
|
- Transmission (tor.obnh.network) - Torrent client
|
||||||
|
- Pi-hole (dns.obnh.io) - DNS/ad blocking
|
||||||
|
- Gitea instances on fry.obr.sh and proton.obr.sh
|
||||||
|
|
||||||
|
**Network constraints (CRITICAL - must preserve):**
|
||||||
|
- LAN: 10.50.0.0/24 via br0
|
||||||
|
- WAN: 31.24.10.184/23 via enp131s0
|
||||||
|
- NAT masquerade for LAN → WAN routing
|
||||||
|
|
||||||
|
## Common Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Health check all SSO platforms
|
||||||
|
./scripts/monitor-sso-health.sh
|
||||||
|
|
||||||
|
# Backup all SSO databases and configs
|
||||||
|
./scripts/backup-sso-infrastructure.sh
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
cd /srv/docker/authentik && sudo docker compose logs -f
|
||||||
|
cd /srv/docker/pangolin && sudo docker compose logs -f
|
||||||
|
cd /srv/docker/guacamole && sudo docker compose logs -f
|
||||||
|
|
||||||
|
# Restart a service
|
||||||
|
cd /srv/docker/<service> && sudo docker compose restart
|
||||||
|
```
|
||||||
|
|
||||||
|
## Key Files
|
||||||
|
|
||||||
|
**Scripts:**
|
||||||
|
- `scripts/monitor-sso-health.sh` - Checks HTTP status, container health, network connectivity
|
||||||
|
- `scripts/backup-sso-infrastructure.sh` - Backs up PostgreSQL databases and configs to /srv/backups/
|
||||||
|
|
||||||
|
**Documentation:**
|
||||||
|
- `ADD-OIDC-INTEGRATIONS.md` - Complete OIDC provider setup guide (6 providers)
|
||||||
|
- `DEPLOYMENT-COMPLETE.md` - Deployment summary and next steps
|
||||||
|
- `QUICK-START.md` - 5-minute setup checklist
|
||||||
|
|
||||||
|
**Research artifacts (read-only reference):**
|
||||||
|
- `.tasks/artifacts/` - Platform research, architecture analysis
|
||||||
|
|
||||||
|
## Deployment Notes
|
||||||
|
|
||||||
|
- All Docker stacks use `/srv/docker/<name>/` paths
|
||||||
|
- Secrets stored in `.env` files (chmod 600)
|
||||||
|
- Traefik handles TLS termination and routing
|
||||||
|
- Configuration deployed via Ralph Loop (iterative automation)
|
||||||
|
|
||||||
|
## When Making Changes
|
||||||
|
|
||||||
|
1. Always run `./scripts/monitor-sso-health.sh` before and after changes
|
||||||
|
2. Backup first: `./scripts/backup-sso-infrastructure.sh`
|
||||||
|
3. Never modify network routing rules without verifying LAN/WAN access preserved
|
||||||
|
4. Docker compose changes require `sudo docker compose up -d` to apply
|
||||||
393
MOBILE-CLIENT-SETUP.md
Normal file
393
MOBILE-CLIENT-SETUP.md
Normal file
@@ -0,0 +1,393 @@
|
|||||||
|
# Pangolin Mobile Client Setup Guide
|
||||||
|
|
||||||
|
**Date:** 2026-01-20 22:26:00+00:00
|
||||||
|
**Device:** Android (pixel9pro, pixel6pro)
|
||||||
|
**Current:** Connected to 10.50.0.0/24 via WLAN
|
||||||
|
**Goal:** Access services via Pangolin tunnel when on WAN/mobile network
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
**Before Setting Up Mobile:**
|
||||||
|
- ✅ Pangolin control plane running (https://tunnel.obr.sh)
|
||||||
|
- ✅ Authentik SSO configured with Pangolin
|
||||||
|
- ⏸️ At least one site created in Pangolin (brn-home)
|
||||||
|
- ⏸️ At least one resource configured (e.g., Jellyfin)
|
||||||
|
|
||||||
|
**Without sites/resources, the tunnel works but has nothing to access.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Part 1: Install Pangolin Mobile App
|
||||||
|
|
||||||
|
### Android (Google Play Store)
|
||||||
|
|
||||||
|
**App Name:** Pangolin
|
||||||
|
|
||||||
|
**Install:**
|
||||||
|
1. Open Google Play Store on pixel9pro or pixel6pro
|
||||||
|
2. Search for "Pangolin"
|
||||||
|
3. Install the official Pangolin app (by fosrl)
|
||||||
|
4. Open the app
|
||||||
|
|
||||||
|
**Alternative:** F-Droid (if available)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Part 2: Enroll Mobile Device
|
||||||
|
|
||||||
|
### Option A: QR Code Enrollment (Easiest)
|
||||||
|
|
||||||
|
**On Desktop (accessing Pangolin dashboard):**
|
||||||
|
|
||||||
|
1. Login to Pangolin: https://tunnel.obr.sh
|
||||||
|
- Use your Pangolin admin account
|
||||||
|
- Or "Login with Authentik" if SSO configured
|
||||||
|
|
||||||
|
2. Navigate to: **Clients** or **Devices** (left sidebar)
|
||||||
|
|
||||||
|
3. Click: **Add Client** or **Enroll Device**
|
||||||
|
|
||||||
|
4. Select: **Mobile Device**
|
||||||
|
|
||||||
|
5. **QR Code appears** on screen
|
||||||
|
|
||||||
|
**On Mobile (pixel9pro/pixel6pro):**
|
||||||
|
|
||||||
|
1. Open Pangolin app
|
||||||
|
2. Tap: **Scan QR Code** or **Add Server**
|
||||||
|
3. Point camera at QR code on desktop
|
||||||
|
4. **Automatic enrollment** - device connects
|
||||||
|
5. App may prompt for permissions (VPN, notifications) - ALLOW these
|
||||||
|
|
||||||
|
**Done!** Device enrolled and connected.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Option B: Manual Enrollment
|
||||||
|
|
||||||
|
**On Mobile App:**
|
||||||
|
|
||||||
|
1. Open Pangolin app
|
||||||
|
2. Tap: **Add Server** or **Manual Setup**
|
||||||
|
|
||||||
|
3. Enter server details:
|
||||||
|
```
|
||||||
|
Server URL: https://tunnel.obr.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Tap **Continue**
|
||||||
|
|
||||||
|
5. **Login with Authentik** (if SSO configured):
|
||||||
|
- Browser opens to Authentik login
|
||||||
|
- Enter: akadmin / (password)
|
||||||
|
- Enter MFA code
|
||||||
|
- Approve access
|
||||||
|
- Returns to app
|
||||||
|
|
||||||
|
6. **Device enrolled** and tunnel connected
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Part 3: Access Services Through Tunnel
|
||||||
|
|
||||||
|
**Once connected, the Pangolin app creates a VPN tunnel.**
|
||||||
|
|
||||||
|
### Accessing Jellyfin via Tunnel
|
||||||
|
|
||||||
|
**On Mobile (when NOT on home WiFi):**
|
||||||
|
|
||||||
|
1. **Ensure Pangolin tunnel is connected:**
|
||||||
|
- Open Pangolin app
|
||||||
|
- Should show "Connected" status
|
||||||
|
- May show data transfer stats
|
||||||
|
|
||||||
|
2. **Open browser on mobile** (Chrome, Firefox, etc.)
|
||||||
|
|
||||||
|
3. **Navigate to:** https://video.obnh.io
|
||||||
|
|
||||||
|
4. **Jellyfin loads through the tunnel!**
|
||||||
|
- Traffic: Mobile → WAN → tunnel.obr.sh (brn) → WireGuard tunnel → jellyfin:8096
|
||||||
|
- Encrypted end-to-end
|
||||||
|
- No public exposure
|
||||||
|
|
||||||
|
5. **Login to Jellyfin:**
|
||||||
|
- If SSO configured: Click "Login with Authentik"
|
||||||
|
- If not: Use Jellyfin credentials
|
||||||
|
- Or use Quick Connect (6-digit code from web)
|
||||||
|
|
||||||
|
### Accessing Other Services
|
||||||
|
|
||||||
|
**Through Pangolin tunnel, you can access:**
|
||||||
|
|
||||||
|
**OpenWebUI:** https://ll.obr.sh
|
||||||
|
**Transmission:** https://tor.obnh.network
|
||||||
|
**Pi-hole Admin:** https://dns.obnh.io
|
||||||
|
**Guacamole (RDP in browser!):** https://remote.obr.sh/guacamole/
|
||||||
|
|
||||||
|
**All work the same way:**
|
||||||
|
- Connect Pangolin VPN
|
||||||
|
- Open mobile browser
|
||||||
|
- Navigate to service URL
|
||||||
|
- Authenticate (SSO or service-specific login)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Part 4: Understanding Pangolin Tunnel Behavior
|
||||||
|
|
||||||
|
### When on Home WiFi (10.50.0.0/24):
|
||||||
|
|
||||||
|
**Pangolin tunnel OFF:**
|
||||||
|
- Services accessible directly via LAN
|
||||||
|
- Faster (no tunnel overhead)
|
||||||
|
- More battery efficient
|
||||||
|
|
||||||
|
**Pangolin tunnel ON (optional):**
|
||||||
|
- Still works, routes through tunnel
|
||||||
|
- No advantage when on home network
|
||||||
|
- Can leave on for convenience
|
||||||
|
|
||||||
|
### When on Mobile Network / External WiFi:
|
||||||
|
|
||||||
|
**Pangolin tunnel REQUIRED:**
|
||||||
|
- Services only accessible through tunnel
|
||||||
|
- Encrypted WireGuard connection
|
||||||
|
- Appears as if you're on home LAN to services
|
||||||
|
|
||||||
|
**Without tunnel:**
|
||||||
|
- Services unreachable (after Traefik restrictions applied)
|
||||||
|
- Or requires public WAN exposure (less secure)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Part 5: Pangolin App Features
|
||||||
|
|
||||||
|
### Connection Management
|
||||||
|
|
||||||
|
**Toggle Tunnel:**
|
||||||
|
- Tap connection to enable/disable
|
||||||
|
- Green = connected
|
||||||
|
- Grey = disconnected
|
||||||
|
|
||||||
|
**Auto-Connect:**
|
||||||
|
- Settings → Auto-connect on mobile data
|
||||||
|
- Automatically connects when off home WiFi
|
||||||
|
|
||||||
|
**Kill Switch:**
|
||||||
|
- Settings → Block internet when tunnel down
|
||||||
|
- Prevents leaking traffic if tunnel fails
|
||||||
|
|
||||||
|
### Resource Access
|
||||||
|
|
||||||
|
**Pangolin can show available resources:**
|
||||||
|
- List of services you can access
|
||||||
|
- Quick links to open in browser
|
||||||
|
- Connection status per service
|
||||||
|
|
||||||
|
### VPN Configuration
|
||||||
|
|
||||||
|
**Pangolin uses WireGuard under the hood:**
|
||||||
|
- Android VPN permission required
|
||||||
|
- Shows in notification area when connected
|
||||||
|
- Can view connection stats (data transfer, latency)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Part 6: Mobile App Troubleshooting
|
||||||
|
|
||||||
|
### Cannot Enroll Device
|
||||||
|
|
||||||
|
**Check:**
|
||||||
|
- Pangolin dashboard accessible from mobile (https://tunnel.obr.sh)
|
||||||
|
- Firewall allows UDP 51821 (already configured on brn)
|
||||||
|
- Mobile has internet connectivity
|
||||||
|
|
||||||
|
**Try:**
|
||||||
|
- Use manual enrollment instead of QR
|
||||||
|
- Check Pangolin dashboard logs for connection attempts
|
||||||
|
|
||||||
|
### Tunnel Connects But Services Unreachable
|
||||||
|
|
||||||
|
**Check:**
|
||||||
|
- Sites created in Pangolin dashboard
|
||||||
|
- Resources configured for services
|
||||||
|
- Newt client running on brn (connects services to Pangolin)
|
||||||
|
|
||||||
|
**Deploy Newt on brn:**
|
||||||
|
```bash
|
||||||
|
# Get command from Pangolin dashboard: Sites → brn-home → Connection
|
||||||
|
# Will look like:
|
||||||
|
docker run -d --name newt --cap-add NET_ADMIN \
|
||||||
|
-e SITE_TOKEN="<from_dashboard>" \
|
||||||
|
-e PANGOLIN_URL="https://tunnel.obr.sh" \
|
||||||
|
--network traefik \
|
||||||
|
fosrl/newt:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
### Battery Drain
|
||||||
|
|
||||||
|
**Pangolin tunnel uses some battery:**
|
||||||
|
- Normal: 5-10% extra per day
|
||||||
|
- High drain: Check for constant reconnections
|
||||||
|
|
||||||
|
**Optimize:**
|
||||||
|
- Disable tunnel when on home WiFi
|
||||||
|
- Use WiFi calling if available
|
||||||
|
- Enable battery optimization for Pangolin app (Android settings)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Part 7: Security Considerations
|
||||||
|
|
||||||
|
### When Using Mobile Tunnel
|
||||||
|
|
||||||
|
**Encrypted:**
|
||||||
|
- ✅ WireGuard encryption (end-to-end)
|
||||||
|
- ✅ TLS for HTTPS services
|
||||||
|
- ✅ Double encryption for services
|
||||||
|
|
||||||
|
**Authentication:**
|
||||||
|
- ✅ Authentik SSO if configured
|
||||||
|
- ✅ Service-specific auth (Jellyfin, etc.)
|
||||||
|
- ✅ MFA on Authentik login
|
||||||
|
|
||||||
|
**Safe To Use On:**
|
||||||
|
- Public WiFi (coffee shop, airport)
|
||||||
|
- Hotel networks
|
||||||
|
- Mobile data
|
||||||
|
- Any untrusted network
|
||||||
|
|
||||||
|
### What Gets Tunneled
|
||||||
|
|
||||||
|
**Through Pangolin:**
|
||||||
|
- Only traffic to configured Pangolin resources
|
||||||
|
- Example: video.obnh.io, ll.obr.sh, etc.
|
||||||
|
|
||||||
|
**NOT tunneled:**
|
||||||
|
- General internet traffic (Google, YouTube, etc.)
|
||||||
|
- Other apps on phone
|
||||||
|
- System updates
|
||||||
|
|
||||||
|
**This is NOT a full VPN - it's a tunneled reverse proxy for specific services.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Part 8: Multi-Device Support
|
||||||
|
|
||||||
|
### Setting Up pixel6pro (Second Device)
|
||||||
|
|
||||||
|
**Same process:**
|
||||||
|
1. Install Pangolin app
|
||||||
|
2. Scan QR code from dashboard (generates new enrollment)
|
||||||
|
3. Or use manual enrollment
|
||||||
|
4. Each device gets unique WireGuard keys
|
||||||
|
5. Both can connect simultaneously
|
||||||
|
|
||||||
|
**Device Management:**
|
||||||
|
- View all devices in Pangolin dashboard (Clients section)
|
||||||
|
- Revoke access per device if phone lost/stolen
|
||||||
|
- See last connection time per device
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Part 9: Accessing Guacamole RDP on Mobile
|
||||||
|
|
||||||
|
**This is REALLY cool:**
|
||||||
|
|
||||||
|
**On Mobile (connected to Pangolin tunnel):**
|
||||||
|
|
||||||
|
1. **Open browser:** Chrome or Firefox
|
||||||
|
2. **Navigate to:** https://remote.obr.sh/guacamole/
|
||||||
|
3. **Login** (guacadmin or Authentik SSO)
|
||||||
|
4. **Click:** argon-rdp connection (after you create it)
|
||||||
|
5. **Your Windows 11 desktop IN YOUR MOBILE BROWSER!**
|
||||||
|
- Full RDP session
|
||||||
|
- Touch controls translated to mouse/keyboard
|
||||||
|
- Can copy/paste between mobile and Windows
|
||||||
|
- Can transfer files (if configured)
|
||||||
|
|
||||||
|
**Use Cases:**
|
||||||
|
- Remote desktop from anywhere
|
||||||
|
- Emergency Windows access
|
||||||
|
- Run Windows-only apps from phone
|
||||||
|
- Access files on Windows machine
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Part 10: Jellyfin Mobile App Through Tunnel
|
||||||
|
|
||||||
|
**Jellyfin official app + Pangolin tunnel:**
|
||||||
|
|
||||||
|
**Setup:**
|
||||||
|
1. Install Jellyfin app (Google Play)
|
||||||
|
2. Connect Pangolin tunnel first
|
||||||
|
3. Open Jellyfin app
|
||||||
|
4. Add server: https://video.obnh.io
|
||||||
|
5. **Quick Connect:**
|
||||||
|
- App shows 6-digit code
|
||||||
|
- On desktop browser: Login to Jellyfin → Dashboard → Devices
|
||||||
|
- Enter the 6-digit code
|
||||||
|
- Device authorized
|
||||||
|
6. Jellyfin app now connected through tunnel
|
||||||
|
|
||||||
|
**Benefits:**
|
||||||
|
- Better than browser (native video player)
|
||||||
|
- Downloads for offline viewing
|
||||||
|
- Better performance
|
||||||
|
- Background audio playback
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Reference Card
|
||||||
|
|
||||||
|
**Pangolin Connection:**
|
||||||
|
```
|
||||||
|
Server: https://tunnel.obr.sh
|
||||||
|
Login: Authentik SSO (akadmin)
|
||||||
|
Protocol: WireGuard
|
||||||
|
Port: 51821/UDP
|
||||||
|
```
|
||||||
|
|
||||||
|
**Services Through Tunnel:**
|
||||||
|
```
|
||||||
|
Jellyfin: https://video.obnh.io
|
||||||
|
OpenWebUI: https://ll.obr.sh
|
||||||
|
Transmission: https://tor.obnh.network
|
||||||
|
Pi-hole: https://dns.obnh.io
|
||||||
|
Guacamole: https://remote.obr.sh/guacamole/
|
||||||
|
```
|
||||||
|
|
||||||
|
**When to Connect:**
|
||||||
|
- On mobile data: ✅ Connect
|
||||||
|
- On public WiFi: ✅ Connect
|
||||||
|
- On home WiFi (10.50.0.x): ⏸️ Optional (direct LAN access)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
**1. Complete 2 WebUI steps** (see above - 5 minutes total)
|
||||||
|
|
||||||
|
**2. Provide credentials to automation:**
|
||||||
|
```bash
|
||||||
|
cat /home/olaf/pangolin/oidc-pangolin.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. I will then:**
|
||||||
|
- Create all other OIDC providers via Authentik API
|
||||||
|
- Configure all services programmatically
|
||||||
|
- Create Pangolin sites and resources
|
||||||
|
- Provide final mobile enrollment QR code
|
||||||
|
|
||||||
|
**4. Install Pangolin app and enroll** (2 minutes)
|
||||||
|
|
||||||
|
**5. Test accessing Jellyfin from mobile data** (1 minute)
|
||||||
|
|
||||||
|
**Total setup time:** ~15 minutes start to finish
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**File:** `/home/olaf/pangolin/MOBILE-CLIENT-SETUP.md`
|
||||||
|
|
||||||
|
**Start with:** Complete Step 1 in `/home/olaf/pangolin/WEBUI-ONLY-STEPS.md`
|
||||||
124
QUICK-START.md
Normal file
124
QUICK-START.md
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
# Pangolin SSO Infrastructure - Quick Start
|
||||||
|
|
||||||
|
**Status:** ✅ Deployed and Running
|
||||||
|
**Date:** 2026-01-20
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 TL;DR - What You Have
|
||||||
|
|
||||||
|
**Three platforms deployed on brn (10.50.0.74):**
|
||||||
|
|
||||||
|
| Platform | URL | Status | Action Needed |
|
||||||
|
|----------|-----|--------|---------------|
|
||||||
|
| **Authentik** | https://sso.obr.sh | ✅ Running | Create admin account |
|
||||||
|
| **Pangolin** | https://tunnel.obr.sh | ✅ Running | Use setup token |
|
||||||
|
| **Guacamole** | https://remote.obr.sh/guacamole/ | ✅ Running | Change password |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚡ 5-Minute Quick Start
|
||||||
|
|
||||||
|
### 1. Check Everything is Healthy
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/home/olaf/pangolin/scripts/monitor-sso-health.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected:** ✅ ALL SYSTEMS OPERATIONAL
|
||||||
|
|
||||||
|
### 2. Get Pangolin Setup Token
|
||||||
|
|
||||||
|
The health script shows the token, or run:
|
||||||
|
```bash
|
||||||
|
cd /srv/docker/pangolin && sudo docker compose logs pangolin | grep "Token:"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Complete Setups (One at a Time)
|
||||||
|
|
||||||
|
**Pangolin First:** (https://tunnel.obr.sh)
|
||||||
|
- Enter the setup token
|
||||||
|
- Create admin account
|
||||||
|
- Done in 30 seconds
|
||||||
|
|
||||||
|
**Then Authentik:** (https://sso.obr.sh/if/flow/initial-setup/)
|
||||||
|
- Create admin account
|
||||||
|
- SAVE RECOVERY CODES
|
||||||
|
- Done in 2 minutes
|
||||||
|
|
||||||
|
**Then Guacamole:** (https://remote.obr.sh/guacamole/)
|
||||||
|
- Login: guacadmin / guacadmin
|
||||||
|
- Change password in Settings
|
||||||
|
- Done in 1 minute
|
||||||
|
|
||||||
|
**Total time:** <5 minutes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 Full Documentation
|
||||||
|
|
||||||
|
**For complete integration (OIDC, service SSO, Pangolin sites):**
|
||||||
|
|
||||||
|
Read: `/home/olaf/pangolin/ADD-OIDC-INTEGRATIONS.md`
|
||||||
|
|
||||||
|
**For detailed deployment info:**
|
||||||
|
|
||||||
|
Read: `/home/olaf/pangolin/DEPLOYMENT-COMPLETE.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 Useful Commands
|
||||||
|
|
||||||
|
**Monitor Health:**
|
||||||
|
```bash
|
||||||
|
/home/olaf/pangolin/scripts/monitor-sso-health.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
**Backup Everything:**
|
||||||
|
```bash
|
||||||
|
/home/olaf/pangolin/scripts/backup-sso-infrastructure.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
**View Logs:**
|
||||||
|
```bash
|
||||||
|
cd /srv/docker/authentik && sudo docker compose logs -f server
|
||||||
|
cd /srv/docker/pangolin && sudo docker compose logs -f pangolin
|
||||||
|
cd /srv/docker/guacamole && sudo docker compose logs -f guacamole
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ What's Working
|
||||||
|
|
||||||
|
- LAN access (10.50.0.0/24)
|
||||||
|
- WAN internet routing
|
||||||
|
- All existing services (Jellyfin, OpenWebUI, etc.)
|
||||||
|
- All three new SSO platforms
|
||||||
|
- TLS certificates (automatic)
|
||||||
|
- Docker networks
|
||||||
|
- Firewall rules
|
||||||
|
|
||||||
|
**Zero issues detected.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📞 If You Need Help
|
||||||
|
|
||||||
|
**Check health first:**
|
||||||
|
```bash
|
||||||
|
/home/olaf/pangolin/scripts/monitor-sso-health.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
**Review deployment:**
|
||||||
|
```bash
|
||||||
|
cat /home/olaf/pangolin/DEPLOYMENT-COMPLETE.md
|
||||||
|
```
|
||||||
|
|
||||||
|
**See iteration history:**
|
||||||
|
```bash
|
||||||
|
ls /home/olaf/pangolin/.ralph-loop/
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Infrastructure ready. Complete 3 quick setups to start using SSO.**
|
||||||
226
README.md
Normal file
226
README.md
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
# Pangolin SSO Infrastructure - Deployment Summary
|
||||||
|
|
||||||
|
**Deployed:** 2026-01-20
|
||||||
|
**Method:** Ralph Loop (11 iterations, 40 minutes)
|
||||||
|
**Status:** ✅ **INFRASTRUCTURE COMPLETE**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 What's Been Deployed
|
||||||
|
|
||||||
|
### Three SSO Platforms on brn (10.50.0.74):
|
||||||
|
|
||||||
|
1. **Authentik SSO Platform**
|
||||||
|
- **URL:** https://sso.obr.sh
|
||||||
|
- **Purpose:** Central identity provider for all services
|
||||||
|
- **Status:** Running, needs admin setup
|
||||||
|
- **Docs:** `AUTHENTIK-SETUP-GUIDE.md`
|
||||||
|
|
||||||
|
2. **Pangolin Tunneled Reverse Proxy**
|
||||||
|
- **URL:** https://tunnel.obr.sh
|
||||||
|
- **Purpose:** WireGuard tunnel management + identity-aware access
|
||||||
|
- **Status:** Running, needs admin setup
|
||||||
|
- **Token:** Check with `scripts/monitor-sso-health.sh`
|
||||||
|
|
||||||
|
3. **Apache Guacamole RDP Gateway**
|
||||||
|
- **URL:** https://remote.obr.sh/guacamole/
|
||||||
|
- **Purpose:** Clientless RDP access to Windows machines
|
||||||
|
- **Status:** Running, change default password
|
||||||
|
- **Login:** guacadmin / guacadmin
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Mission Critical Constraints: PRESERVED
|
||||||
|
|
||||||
|
**Verified throughout all 11 iterations:**
|
||||||
|
- ✅ LAN access (10.50.0.0/24): Fully functional
|
||||||
|
- ✅ WAN internet routing: Working normally
|
||||||
|
- ✅ Existing services: Zero disruptions
|
||||||
|
- ✅ Network configuration: Unchanged (except UDP 51821 for Pangolin)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Quick Start
|
||||||
|
|
||||||
|
### Step 1: Verify Everything is Running
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/home/olaf/pangolin/scripts/monitor-sso-health.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected:** All systems operational ✅
|
||||||
|
|
||||||
|
### Step 2: Complete Platform Setups (15 minutes)
|
||||||
|
|
||||||
|
**Pangolin:**
|
||||||
|
```
|
||||||
|
1. Go to: https://tunnel.obr.sh
|
||||||
|
2. Enter setup token (from health monitor script)
|
||||||
|
3. Create admin account
|
||||||
|
```
|
||||||
|
|
||||||
|
**Authentik:**
|
||||||
|
```
|
||||||
|
1. Go to: https://sso.obr.sh/if/flow/initial-setup/
|
||||||
|
2. Create admin account
|
||||||
|
3. SAVE RECOVERY CODES
|
||||||
|
```
|
||||||
|
|
||||||
|
**Guacamole:**
|
||||||
|
```
|
||||||
|
1. Go to: https://remote.obr.sh/guacamole/
|
||||||
|
2. Login: guacadmin / guacadmin
|
||||||
|
3. Settings → Preferences → Change Password
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: Add OIDC Integration (30 minutes)
|
||||||
|
|
||||||
|
**Follow:** `ADD-OIDC-INTEGRATIONS.md`
|
||||||
|
|
||||||
|
Creates 6 OIDC providers in Authentik, integrates with all services.
|
||||||
|
|
||||||
|
### Step 4: Configure Pangolin Sites (20 minutes)
|
||||||
|
|
||||||
|
**Follow:** `ADD-OIDC-INTEGRATIONS.md` Phase 4
|
||||||
|
|
||||||
|
Creates sites and resources for all services.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📁 Important Files
|
||||||
|
|
||||||
|
### Configuration:
|
||||||
|
- `/srv/docker/authentik/` - Authentik stack
|
||||||
|
- `/srv/docker/pangolin/` - Pangolin stack
|
||||||
|
- `/srv/docker/guacamole/` - Guacamole stack
|
||||||
|
|
||||||
|
### Documentation:
|
||||||
|
- `DEPLOYMENT-COMPLETE.md` - Deployment summary
|
||||||
|
- `ADD-OIDC-INTEGRATIONS.md` - Integration guide (500+ lines)
|
||||||
|
- `AUTHENTIK-SETUP-GUIDE.md` - Setup instructions
|
||||||
|
- `RALPH-LOOP-FINAL-REPORT.md` - Complete analysis
|
||||||
|
- `.ralph-loop/` - All iteration results (11 files)
|
||||||
|
|
||||||
|
### Scripts:
|
||||||
|
- `scripts/monitor-sso-health.sh` - Health monitoring
|
||||||
|
- `scripts/backup-sso-infrastructure.sh` - Automated backups
|
||||||
|
- `provide-oidc-credentials.sh` - OIDC credential input helper
|
||||||
|
|
||||||
|
### Research:
|
||||||
|
- `.tasks/artifacts/architecture-validation.md` - Architecture analysis
|
||||||
|
- `.tasks/artifacts/pangolin-research.md` - Pangolin documentation
|
||||||
|
- `.tasks/artifacts/authentik-research.md` - Authentik best practices
|
||||||
|
- `.tasks/artifacts/guacamole-research.md` - Guacamole OIDC details
|
||||||
|
- `.tasks/artifacts/jellyfin-sso-research.md` - Jellyfin SSO plugin
|
||||||
|
- `.tasks/artifacts/openwebui-research.md` - OpenWebUI OIDC
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 Maintenance Commands
|
||||||
|
|
||||||
|
### Check Status
|
||||||
|
```bash
|
||||||
|
./scripts/monitor-sso-health.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Backup Everything
|
||||||
|
```bash
|
||||||
|
./scripts/backup-sso-infrastructure.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### View Logs
|
||||||
|
```bash
|
||||||
|
# Authentik
|
||||||
|
cd /srv/docker/authentik && sudo docker compose logs -f
|
||||||
|
|
||||||
|
# Pangolin
|
||||||
|
cd /srv/docker/pangolin && sudo docker compose logs -f
|
||||||
|
|
||||||
|
# Guacamole
|
||||||
|
cd /srv/docker/guacamole && sudo docker compose logs -f
|
||||||
|
```
|
||||||
|
|
||||||
|
### Restart Services
|
||||||
|
```bash
|
||||||
|
# Individual service
|
||||||
|
cd /srv/docker/<service> && sudo docker compose restart
|
||||||
|
|
||||||
|
# All services
|
||||||
|
cd /srv/docker/authentik && sudo docker compose restart
|
||||||
|
cd /srv/docker/pangolin && sudo docker compose restart
|
||||||
|
cd /srv/docker/guacamole && sudo docker compose restart
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Infrastructure Health
|
||||||
|
|
||||||
|
**Run health check:**
|
||||||
|
```bash
|
||||||
|
/home/olaf/pangolin/scripts/monitor-sso-health.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
**Current Status (Iteration 11):**
|
||||||
|
- All platforms: ✅ Operational
|
||||||
|
- LAN access: ✅ Working
|
||||||
|
- WAN routing: ✅ Working
|
||||||
|
- Containers: 9 healthy
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔐 Security Checklist
|
||||||
|
|
||||||
|
**Completed:**
|
||||||
|
- ✅ TLS certificates (Let's Encrypt automatic)
|
||||||
|
- ✅ Network isolation (Docker internal networks)
|
||||||
|
- ✅ Resource limits applied
|
||||||
|
- ✅ Secrets generated and protected
|
||||||
|
|
||||||
|
**Pending (User Action):**
|
||||||
|
- ⏸️ Change Guacamole default password
|
||||||
|
- ⏸️ Create Authentik admin + enable MFA
|
||||||
|
- ⏸️ Create Pangolin admin
|
||||||
|
- ⏸️ Configure OIDC providers
|
||||||
|
- ⏸️ Add MFA policies
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 Deployment Progress
|
||||||
|
|
||||||
|
**Infrastructure:** 100% ✅
|
||||||
|
**Configuration:** 30% ⏸️ (needs setup wizards)
|
||||||
|
**Integration:** 0% ⏸️ (needs OIDC providers)
|
||||||
|
**Client Deployment:** 0% ⏸️ (needs Newt clients)
|
||||||
|
|
||||||
|
**Overall:** 60% complete
|
||||||
|
|
||||||
|
**Blocker:** User must complete setup wizards to proceed further
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📞 Support
|
||||||
|
|
||||||
|
**If issues occur:**
|
||||||
|
|
||||||
|
1. **Check health:** `./scripts/monitor-sso-health.sh`
|
||||||
|
2. **View logs:** `cd /srv/docker/<service> && sudo docker compose logs`
|
||||||
|
3. **Restart service:** `sudo docker compose restart`
|
||||||
|
4. **Restore from backup:** `./scripts/backup-sso-infrastructure.sh` (creates backups)
|
||||||
|
|
||||||
|
**Documentation:** All guides in `/home/olaf/pangolin/`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🏆 Ralph Loop Achievement
|
||||||
|
|
||||||
|
**11 Iterations**
|
||||||
|
**40 Minutes**
|
||||||
|
**3 Platforms Deployed**
|
||||||
|
**0 Service Disruptions**
|
||||||
|
**100% Constraint Satisfaction**
|
||||||
|
|
||||||
|
**Task Status:** Infrastructure implementation COMPLETE ✅
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**For next steps, see:** `DEPLOYMENT-COMPLETE.md` and `ADD-OIDC-INTEGRATIONS.md`
|
||||||
78
scripts/backup-sso-infrastructure.sh
Executable file
78
scripts/backup-sso-infrastructure.sh
Executable file
@@ -0,0 +1,78 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# SSO Infrastructure Backup Script
|
||||||
|
# Created by Ralph Loop Iteration 10
|
||||||
|
# Backs up all three SSO platforms
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
BACKUP_DIR="/srv/backups/sso-$(date +%Y%m%d-%H%M%S)"
|
||||||
|
mkdir -p "$BACKUP_DIR"
|
||||||
|
|
||||||
|
echo "=========================================="
|
||||||
|
echo "SSO Infrastructure Backup"
|
||||||
|
echo "=========================================="
|
||||||
|
echo "Backup location: $BACKUP_DIR"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Backup Authentik
|
||||||
|
echo "Backing up Authentik..."
|
||||||
|
cd /srv/docker/authentik
|
||||||
|
sudo docker compose exec -T postgresql pg_dump -U authentik authentik | gzip > "$BACKUP_DIR/authentik-db.sql.gz"
|
||||||
|
sudo cp -r ./media "$BACKUP_DIR/authentik-media" 2>/dev/null || echo "No media files"
|
||||||
|
sudo cp .env docker-compose.yml "$BACKUP_DIR/"
|
||||||
|
echo "✅ Authentik backed up"
|
||||||
|
|
||||||
|
# Backup Pangolin
|
||||||
|
echo "Backing up Pangolin..."
|
||||||
|
cd /srv/docker/pangolin
|
||||||
|
sudo docker compose exec -T postgres pg_dump -U pangolin pangolin | gzip > "$BACKUP_DIR/pangolin-db.sql.gz"
|
||||||
|
sudo cp config/config.yml .env docker-compose.yml "$BACKUP_DIR/"
|
||||||
|
echo "✅ Pangolin backed up"
|
||||||
|
|
||||||
|
# Backup Guacamole
|
||||||
|
echo "Backing up Guacamole..."
|
||||||
|
cd /srv/docker/guacamole
|
||||||
|
sudo docker compose exec -T postgres pg_dump -U guacamole guacamole | gzip > "$BACKUP_DIR/guacamole-db.sql.gz"
|
||||||
|
sudo cp initdb/initdb.sql .env docker-compose.yml "$BACKUP_DIR/"
|
||||||
|
echo "✅ Guacamole backed up"
|
||||||
|
|
||||||
|
# Backup Traefik dynamic config
|
||||||
|
echo "Backing up Traefik configuration..."
|
||||||
|
sudo cp /srv/docker/traefik/traefik_dynamic.yaml "$BACKUP_DIR/"
|
||||||
|
echo "✅ Traefik config backed up"
|
||||||
|
|
||||||
|
# Create backup manifest
|
||||||
|
cat > "$BACKUP_DIR/MANIFEST.txt" << EOF
|
||||||
|
SSO Infrastructure Backup
|
||||||
|
Created: $(date -Iseconds)
|
||||||
|
Hostname: $(hostname)
|
||||||
|
|
||||||
|
Contents:
|
||||||
|
- authentik-db.sql.gz - Authentik PostgreSQL database
|
||||||
|
- authentik-media/ - Authentik media files
|
||||||
|
- pangolin-db.sql.gz - Pangolin PostgreSQL database
|
||||||
|
- guacamole-db.sql.gz - Guacamole PostgreSQL database
|
||||||
|
- config.yml - Pangolin configuration
|
||||||
|
- docker-compose.yml files for all services
|
||||||
|
- .env files (CONTAINS SECRETS - PROTECT THIS BACKUP)
|
||||||
|
- traefik_dynamic.yaml - Traefik routing configuration
|
||||||
|
|
||||||
|
Restoration:
|
||||||
|
See: /home/olaf/pangolin/RESTORE-GUIDE.md
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Set permissions
|
||||||
|
chmod 600 "$BACKUP_DIR"/*.env 2>/dev/null || true
|
||||||
|
chmod -R 700 "$BACKUP_DIR"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=========================================="
|
||||||
|
echo "Backup Complete!"
|
||||||
|
echo "=========================================="
|
||||||
|
echo "Location: $BACKUP_DIR"
|
||||||
|
echo "Size: $(du -sh $BACKUP_DIR | cut -f1)"
|
||||||
|
echo ""
|
||||||
|
echo "⚠️ This backup contains secrets (.env files)"
|
||||||
|
echo " Store securely and encrypt if transmitted"
|
||||||
|
echo ""
|
||||||
|
echo "To restore: See /home/olaf/pangolin/RESTORE-GUIDE.md"
|
||||||
59
scripts/monitor-sso-health.sh
Executable file
59
scripts/monitor-sso-health.sh
Executable file
@@ -0,0 +1,59 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# SSO Infrastructure Health Monitor
|
||||||
|
# Created by Ralph Loop Iteration 11
|
||||||
|
|
||||||
|
echo "=========================================="
|
||||||
|
echo "SSO Infrastructure Health Check"
|
||||||
|
echo "Time: $(date -Iseconds)"
|
||||||
|
echo "=========================================="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check Authentik
|
||||||
|
echo "📊 Authentik Status (sso.obr.sh):"
|
||||||
|
cd /srv/docker/authentik
|
||||||
|
sudo docker compose ps --format " {{.Name}}: {{.Status}}" 2>/dev/null
|
||||||
|
AUTHENTIK_HTTP=$(curl -s -o /dev/null -w "%{http_code}" -m 3 -k https://sso.obr.sh 2>/dev/null || echo "FAIL")
|
||||||
|
echo " HTTP Status: $AUTHENTIK_HTTP"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check Pangolin
|
||||||
|
echo "🦎 Pangolin Status (tunnel.obr.sh):"
|
||||||
|
cd /srv/docker/pangolin
|
||||||
|
sudo docker compose ps --format " {{.Name}}: {{.Status}}" 2>/dev/null
|
||||||
|
PANGOLIN_HTTP=$(curl -s -o /dev/null -w "%{http_code}" -m 3 -k https://tunnel.obr.sh 2>/dev/null || echo "FAIL")
|
||||||
|
echo " HTTP Status: $PANGOLIN_HTTP"
|
||||||
|
PANGOLIN_TOKEN=$(sudo docker compose logs pangolin 2>/dev/null | grep "Token:" | tail -1 | awk '{print $2}')
|
||||||
|
if [ -n "$PANGOLIN_TOKEN" ]; then
|
||||||
|
echo " Setup Token: $PANGOLIN_TOKEN"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check Guacamole
|
||||||
|
echo "🖥️ Guacamole Status (remote.obr.sh):"
|
||||||
|
cd /srv/docker/guacamole
|
||||||
|
sudo docker compose ps --format " {{.Name}}: {{.Status}}" 2>/dev/null
|
||||||
|
GUAC_HTTP=$(curl -s -o /dev/null -w "%{http_code}" -m 3 -k https://remote.obr.sh/guacamole/ 2>/dev/null || echo "FAIL")
|
||||||
|
echo " HTTP Status: $GUAC_HTTP"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check Network
|
||||||
|
echo "🌐 Network Status:"
|
||||||
|
echo " LAN (br0): $(ip addr show br0 2>/dev/null | grep 'inet ' | awk '{print $2}' || echo 'ERROR')"
|
||||||
|
echo " WAN (enp131s0): $(ip addr show enp131s0 2>/dev/null | grep 'inet ' | head -1 | awk '{print $2}' || echo 'ERROR')"
|
||||||
|
NAT_RULE=$(sudo nft list table ip nat 2>/dev/null | grep "10.50.0.0/24 masquerade" && echo "✅ ACTIVE" || echo "❌ MISSING")
|
||||||
|
echo " NAT Masquerade (10.50.0.0/24): $NAT_RULE"
|
||||||
|
INTERNET=$(ping -c 1 -W 1 8.8.8.8 >/dev/null 2>&1 && echo "✅ WORKING" || echo "❌ FAILED")
|
||||||
|
echo " Internet Access: $INTERNET"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Overall Status
|
||||||
|
echo "=========================================="
|
||||||
|
if [[ "$AUTHENTIK_HTTP" == "302" || "$AUTHENTIK_HTTP" == "200" ]] && \
|
||||||
|
[[ "$PANGOLIN_HTTP" == "200" ]] && \
|
||||||
|
[[ "$GUAC_HTTP" == "200" ]] && \
|
||||||
|
[[ "$INTERNET" == "✅ WORKING" ]]; then
|
||||||
|
echo "✅ ALL SYSTEMS OPERATIONAL"
|
||||||
|
else
|
||||||
|
echo "⚠️ SOME ISSUES DETECTED - Review above"
|
||||||
|
fi
|
||||||
|
echo "=========================================="
|
||||||
Reference in New Issue
Block a user