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