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:
2026-01-21 06:15:04 +00:00
commit b428721b07
17 changed files with 5749 additions and 0 deletions

19
.gitignore vendored Normal file
View 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

View 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

View 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/)

View 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

View 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

View 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

View 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.

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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`

View 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
View 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 "=========================================="