Compare commits
5 Commits
31db372b43
...
50c930eeaf
| Author | SHA1 | Date | |
|---|---|---|---|
| 50c930eeaf | |||
| 8dde15b8ef | |||
| 6100d8dc69 | |||
| a92f0fcb28 | |||
| bd4604cdcc |
@@ -20,7 +20,8 @@ NixOS cluster configuration using flakes. Homelab infrastructure with Nomad/Cons
|
||||
├── docs/
|
||||
│ ├── CLUSTER_REVAMP.md # Master plan for architecture changes
|
||||
│ ├── MIGRATION_TODO.md # Tracking checklist for migration
|
||||
│ └── NFS_FAILOVER.md # NFS failover procedures
|
||||
│ ├── NFS_FAILOVER.md # NFS failover procedures
|
||||
│ └── AUTH_SETUP.md # Authentication (Pocket ID + Traefik OIDC)
|
||||
└── services/ # Nomad job specs (.hcl files)
|
||||
```
|
||||
|
||||
@@ -76,6 +77,12 @@ NixOS cluster configuration using flakes. Homelab infrastructure with Nomad/Cons
|
||||
- SOPS for secrets, files in `secrets/`
|
||||
- Keys managed per-host
|
||||
|
||||
**Authentication**:
|
||||
- Pocket ID (OIDC provider) at `pocket-id.v.paler.net`
|
||||
- Traefik uses `traefik-oidc-auth` plugin for SSO
|
||||
- Services add `middlewares=oidc-auth@file` tag to protect
|
||||
- See `docs/AUTH_SETUP.md` for details
|
||||
|
||||
## Migration Status
|
||||
|
||||
**Phase 3 & 4**: COMPLETE! GlusterFS removed, all services on NFS
|
||||
|
||||
55
docs/AUTH_SETUP.md
Normal file
55
docs/AUTH_SETUP.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# Authentication Setup
|
||||
|
||||
SSO for homelab services using OIDC.
|
||||
|
||||
## Architecture
|
||||
|
||||
**Pocket ID** (`pocket-id.v.paler.net`) - Lightweight OIDC provider, data in `/data/services/pocket-id`
|
||||
|
||||
**Traefik** - Uses `traefik-oidc-auth` plugin (v0.16.0) to protect services
|
||||
- Plugin downloaded from GitHub at startup, cached in `/data/services/traefik/plugins-storage`
|
||||
- Middleware config in `/data/services/traefik/rules/middlewares.yml`
|
||||
- Protected services add tag: `traefik.http.routers.<name>.middlewares=oidc-auth@file`
|
||||
|
||||
## Flow
|
||||
|
||||
1. User hits protected service → Traefik intercepts
|
||||
2. Redirects to Pocket ID for login
|
||||
3. Pocket ID returns OIDC token
|
||||
4. Traefik validates and forwards with `X-Oidc-Username` header
|
||||
|
||||
## Protected Services
|
||||
|
||||
Use `oidc-auth@file` middleware (grep codebase for full list):
|
||||
- Wikis (TiddlyWiki instances)
|
||||
- Media stack (Radarr, Sonarr, Plex, etc.)
|
||||
- Infrastructure (Traefik dashboard, Loki, Jupyter, Unifi)
|
||||
|
||||
## Key Files
|
||||
|
||||
- `services/pocket-id.hcl` - OIDC provider
|
||||
- `services/traefik.hcl` - Plugin declaration
|
||||
- `/data/services/traefik/rules/middlewares.yml` - Middleware definitions (oidc-auth, simple-auth fallback)
|
||||
|
||||
## Cold Start Notes
|
||||
|
||||
- Traefik needs internet to download plugin on first start
|
||||
- Pocket ID needs `/data/services` NFS mounted
|
||||
- Pocket ID down = all protected services inaccessible
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Infinite redirects**: Check `TRUST_PROXY=true` on Pocket ID
|
||||
|
||||
**Plugin not loading**: Clear cache in `/data/services/traefik/plugins-storage/`, restart Traefik
|
||||
|
||||
**401 after login**: Verify client ID/secret in middlewares.yml matches Pocket ID client config
|
||||
|
||||
## Migration History
|
||||
|
||||
- Previous: Authentik with forwardAuth (removed Nov 2024)
|
||||
- Current: Pocket ID + traefik-oidc-auth (simpler, lighter)
|
||||
|
||||
---
|
||||
|
||||
*Manage users/clients via Pocket ID UI. Basic auth fallback available via `simple-auth` middleware.*
|
||||
@@ -24,4 +24,20 @@
|
||||
externalInterface = "enp1s0";
|
||||
internalInterfaces = [ "tailscale0" ];
|
||||
};
|
||||
|
||||
# Security hardening: Enable firewall (override global setting)
|
||||
networking.firewall = {
|
||||
enable = lib.mkForce true;
|
||||
allowedTCPPorts = [ 80 443 ]; # Public web traffic only
|
||||
allowedUDPPorts = [ 41641 ]; # Tailscale
|
||||
trustedInterfaces = [ "tailscale0" ]; # Full access via VPN
|
||||
};
|
||||
|
||||
# Security hardening: Restrict SSH to Tailscale only + key-based auth
|
||||
services.openssh = {
|
||||
listenAddresses = [
|
||||
{ addr = "100.75.147.49"; port = 22; } # Tailscale IP only
|
||||
];
|
||||
settings.PasswordAuthentication = false; # Keys only
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,9 +7,12 @@ job "media" {
|
||||
|
||||
group "servers" {
|
||||
network {
|
||||
port "radarr" { to = 7878 }
|
||||
port "sonarr" { to = 8989 }
|
||||
port "radarr" { static = 7878 }
|
||||
port "sonarr" { static = 8989 }
|
||||
port "bazarr" { to = 6767 }
|
||||
port "prowlarr" { static = 9696 }
|
||||
port "jellyseerr" { static = 5055 }
|
||||
port "flaresolverr" { static = 8191 }
|
||||
port "pms" { static = 32400 }
|
||||
port "qbt_ui" { static = 8080 }
|
||||
port "qbt_torrent" { static = 51413 }
|
||||
@@ -34,7 +37,7 @@ job "media" {
|
||||
}
|
||||
|
||||
resources {
|
||||
cpu = 200
|
||||
cpu = 1000
|
||||
}
|
||||
|
||||
service {
|
||||
@@ -68,7 +71,8 @@ job "media" {
|
||||
}
|
||||
|
||||
resources {
|
||||
cpu = 200
|
||||
cpu = 1000
|
||||
memory = 500
|
||||
}
|
||||
|
||||
service {
|
||||
@@ -83,15 +87,49 @@ job "media" {
|
||||
}
|
||||
}
|
||||
|
||||
task "bazarr" {
|
||||
# task "bazarr" {
|
||||
# driver = "docker"
|
||||
#
|
||||
# config {
|
||||
# image = "ghcr.io/hotio/bazarr:latest"
|
||||
# ports = [ "bazarr" ]
|
||||
# volumes = [
|
||||
# "/data/services/media/bazarr:/config",
|
||||
# "/data/media/media:/data/media",
|
||||
# ]
|
||||
# }
|
||||
#
|
||||
# env {
|
||||
# PUID = 1000
|
||||
# PGID = 1000
|
||||
# TZ = "Europe/Lisbon"
|
||||
# }
|
||||
#
|
||||
# resources {
|
||||
# cpu = 200
|
||||
# memory = 500
|
||||
# }
|
||||
#
|
||||
# service {
|
||||
# name = "bazarr"
|
||||
# port = "bazarr"
|
||||
#
|
||||
# tags = [
|
||||
# "traefik.enable=true",
|
||||
# "traefik.http.routers.bazarr.entryPoints=websecure",
|
||||
# "traefik.http.routers.bazarr.middlewares=oidc-auth@file",
|
||||
# ]
|
||||
# }
|
||||
# }
|
||||
|
||||
task "prowlarr" {
|
||||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "ghcr.io/hotio/bazarr:latest"
|
||||
ports = [ "bazarr" ]
|
||||
image = "ghcr.io/hotio/prowlarr:latest"
|
||||
ports = [ "prowlarr" ]
|
||||
volumes = [
|
||||
"/data/services/media/bazarr:/config",
|
||||
"/data/media/media:/data/media",
|
||||
"/data/services/media/prowlarr:/config",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -106,17 +144,93 @@ job "media" {
|
||||
}
|
||||
|
||||
service {
|
||||
name = "bazarr"
|
||||
port = "bazarr"
|
||||
name = "prowlarr"
|
||||
port = "prowlarr"
|
||||
|
||||
tags = [
|
||||
"traefik.enable=true",
|
||||
"traefik.http.routers.bazarr.entryPoints=websecure",
|
||||
"traefik.http.routers.bazarr.middlewares=oidc-auth@file",
|
||||
"traefik.http.routers.prowlarr.entryPoints=websecure",
|
||||
"traefik.http.routers.prowlarr.middlewares=oidc-auth@file",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
task "jellyseerr" {
|
||||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "fallenbagel/jellyseerr:latest"
|
||||
ports = [ "jellyseerr" ]
|
||||
volumes = [
|
||||
"/data/services/media/jellyseerr:/app/config",
|
||||
]
|
||||
}
|
||||
|
||||
env {
|
||||
TZ = "Europe/Lisbon"
|
||||
}
|
||||
|
||||
resources {
|
||||
cpu = 200
|
||||
}
|
||||
|
||||
service {
|
||||
name = "jellyseerr"
|
||||
port = "jellyseerr"
|
||||
|
||||
tags = [
|
||||
"traefik.enable=true",
|
||||
"traefik.http.routers.jellyseerr.entryPoints=websecure",
|
||||
"traefik.http.routers.jellyseerr.middlewares=oidc-auth@file",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
task "flaresolverr" {
|
||||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "ghcr.io/flaresolverr/flaresolverr:latest"
|
||||
ports = [ "flaresolverr" ]
|
||||
}
|
||||
|
||||
env {
|
||||
LOG_LEVEL = "info"
|
||||
TZ = "Europe/Lisbon"
|
||||
}
|
||||
|
||||
resources {
|
||||
cpu = 500
|
||||
memory = 1024
|
||||
}
|
||||
|
||||
service {
|
||||
name = "flaresolverr"
|
||||
port = "flaresolverr"
|
||||
}
|
||||
}
|
||||
|
||||
task "recyclarr" {
|
||||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "ghcr.io/recyclarr/recyclarr:latest"
|
||||
volumes = [
|
||||
"/data/services/media/recyclarr:/config",
|
||||
]
|
||||
}
|
||||
|
||||
env {
|
||||
TZ = "Europe/Lisbon"
|
||||
CRON_SCHEDULE = "0 0 * * *" # Daily at midnight
|
||||
}
|
||||
|
||||
resources {
|
||||
cpu = 100
|
||||
memory = 256
|
||||
}
|
||||
}
|
||||
|
||||
task "plex" {
|
||||
driver = "docker"
|
||||
|
||||
@@ -177,7 +291,7 @@ job "media" {
|
||||
|
||||
resources {
|
||||
cpu = 2000
|
||||
memory = 1000
|
||||
memory = 1500
|
||||
}
|
||||
|
||||
service {
|
||||
|
||||
Reference in New Issue
Block a user