feat!: Use Authelia authentication to protect endpoints
- Uses Authelia as an authentication middleware and access control, with sensible policy - Redis as Authelia's backend for session data - Add https-proto middleware - Add/update example files
This commit is contained in:
parent
cf78372b71
commit
67ff6d585c
27
.env.example
27
.env.example
@ -36,9 +36,10 @@ TAILSCALE_TAGS=tag:nas
|
|||||||
# Enable Tailscale Funnel (public access) for HTTPS? Set to 'true' or 'false'. 'false' uses Serve (Tailnet only, recommended).
|
# Enable Tailscale Funnel (public access) for HTTPS? Set to 'true' or 'false'. 'false' uses Serve (Tailnet only, recommended).
|
||||||
ENABLE_FUNNEL_HTTPS=false
|
ENABLE_FUNNEL_HTTPS=false
|
||||||
|
|
||||||
# --- Primary Hostname ---
|
# --- Primary Application Hostname ---
|
||||||
# Primary hostname used by Traefik for routing. Derived from Tailscale settings by default.
|
# Primary hostname used by Traefik/Authelia. Derived from Tailscale settings by default.
|
||||||
HOSTNAME=${TAILSCALE_HOSTNAME}.${TAILSCALE_TAILNET_DOMAIN}
|
# Renamed from HOSTNAME to avoid collision with host system environment variable.
|
||||||
|
APP_HOSTNAME=${TAILSCALE_HOSTNAME}.${TAILSCALE_TAILNET_DOMAIN}
|
||||||
|
|
||||||
# --- Application Credentials ---
|
# --- Application Credentials ---
|
||||||
# qBittorrent Web UI Credentials (change default!)
|
# qBittorrent Web UI Credentials (change default!)
|
||||||
@ -62,18 +63,16 @@ HOMEPAGE_VAR_WEATHER_UNIT=metric
|
|||||||
|
|
||||||
# --- Authelia Settings ---
|
# --- Authelia Settings ---
|
||||||
# Generate strong random secrets for these using tools like `openssl rand -hex 32`
|
# Generate strong random secrets for these using tools like `openssl rand -hex 32`
|
||||||
AUTHELIA_JWT_SECRET= # Example: your_strong_jwt_secret
|
# These are all REQUIRED for Authelia to function properly
|
||||||
AUTHELIA_SESSION_SECRET= # Example: your_strong_session_secret
|
AUTHELIA_JWT_SECRET= # Secret used for JWT tokens (password reset, etc)
|
||||||
AUTHELIA_STORAGE_ENCRYPTION_KEY= # Example: your_strong_storage_encryption_key
|
AUTHELIA_SESSION_SECRET= # Secret for encrypting session cookies
|
||||||
AUTHELIA_REDIS_PASSWORD= # Example: your_strong_redis_password
|
AUTHELIA_STORAGE_ENCRYPTION_KEY= # Secret for encrypting stored data
|
||||||
|
AUTHELIA_REDIS_PASSWORD= # Password for Redis session storage
|
||||||
|
|
||||||
# Google OIDC Provider Settings (Get from Google Cloud Console - https://console.cloud.google.com/apis/credentials)
|
# Note: The following variables are no longer needed with Authelia 4.38+ and the updated configuration
|
||||||
AUTHELIA_GOOGLE_OIDC_CLIENT_ID= # Example: your-google-client-id.apps.googleusercontent.com
|
# They are preserved for backward compatibility but will be automatically mapped to the new structure
|
||||||
AUTHELIA_GOOGLE_OIDC_CLIENT_SECRET= # Example: GOCSPX-your-google-client-secret
|
# AUTHELIA_SESSION_DOMAIN=${APP_HOSTNAME}
|
||||||
|
# AUTHELIA_DEFAULT_REDIRECT_URL=https://${APP_HOSTNAME}/home
|
||||||
# Authelia Session Configuration
|
|
||||||
AUTHELIA_SESSION_DOMAIN=${TAILSCALE_HOSTNAME}.${TAILSCALE_TAILNET_DOMAIN}
|
|
||||||
AUTHELIA_DEFAULT_REDIRECT_URL=https://${TAILSCALE_HOSTNAME}.${TAILSCALE_TAILNET_DOMAIN}/home
|
|
||||||
|
|
||||||
# --- API Keys & Integration Tokens (Optional - Mainly for Homepage Widgets) ---
|
# --- API Keys & Integration Tokens (Optional - Mainly for Homepage Widgets) ---
|
||||||
# Find API keys within each application's settings (usually Settings > General or Security)
|
# Find API keys within each application's settings (usually Settings > General or Security)
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,6 +1,9 @@
|
|||||||
*.env
|
*.env
|
||||||
|
*.bak
|
||||||
.idea
|
.idea
|
||||||
docker-compose.override.yml
|
docker-compose.override.yml
|
||||||
|
/authelia/*.yml
|
||||||
|
!/authelia/*.example.yml
|
||||||
/homepage/logs
|
/homepage/logs
|
||||||
/homepage/*.yaml
|
/homepage/*.yaml
|
||||||
/homepage/*.css
|
/homepage/*.css
|
||||||
|
|||||||
140
authelia/configuration.example.yml
Normal file
140
authelia/configuration.example.yml
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
# Authelia Configuration File v4.38+
|
||||||
|
# Documentation: https://www.authelia.com/configuration/
|
||||||
|
|
||||||
|
# Server settings
|
||||||
|
server:
|
||||||
|
address: 'tcp://0.0.0.0:9091'
|
||||||
|
|
||||||
|
# Logging configuration
|
||||||
|
log:
|
||||||
|
level: info
|
||||||
|
format: text
|
||||||
|
|
||||||
|
# Session configuration for v4.38+
|
||||||
|
session:
|
||||||
|
name: authelia_session
|
||||||
|
secret: ${AUTHELIA_SESSION_SECRET}
|
||||||
|
expiration: 1h
|
||||||
|
inactivity: 5m
|
||||||
|
redis:
|
||||||
|
host: redis
|
||||||
|
port: 6379
|
||||||
|
password: ${AUTHELIA_SESSION_REDIS_PASSWORD}
|
||||||
|
database_index: 0
|
||||||
|
cookies:
|
||||||
|
# Using your specific Tailscale domain (e.g. example.ts.net) not just ts.net
|
||||||
|
- domain: 'your-tailnet.ts.net'
|
||||||
|
authelia_url: 'https://tailscale-nas.your-tailnet.ts.net'
|
||||||
|
default_redirection_url: 'https://tailscale-nas.your-tailnet.ts.net/home'
|
||||||
|
same_site: lax
|
||||||
|
|
||||||
|
# Regulation (brute force protection)
|
||||||
|
regulation:
|
||||||
|
max_retries: 3
|
||||||
|
find_time: 2m
|
||||||
|
ban_time: 5m
|
||||||
|
|
||||||
|
# Storage (for user preferences, etc. - encrypted using storage key)
|
||||||
|
storage:
|
||||||
|
encryption_key: ${AUTHELIA_STORAGE_ENCRYPTION_KEY}
|
||||||
|
local:
|
||||||
|
path: /config/db.sqlite3
|
||||||
|
|
||||||
|
# Authentication backend (using file-based user database)
|
||||||
|
authentication_backend:
|
||||||
|
file:
|
||||||
|
path: /config/users_database.yml
|
||||||
|
password:
|
||||||
|
algorithm: argon2id
|
||||||
|
iterations: 1
|
||||||
|
memory: 1024
|
||||||
|
parallelism: 8
|
||||||
|
salt_length: 16
|
||||||
|
key_length: 32
|
||||||
|
|
||||||
|
# Access control rules
|
||||||
|
access_control:
|
||||||
|
default_policy: deny # Deny access by default
|
||||||
|
rules:
|
||||||
|
# Rules are processed in order. First match wins.
|
||||||
|
# It's recommended to put more specific rules first.
|
||||||
|
|
||||||
|
# 1. Bypass rules (No authentication required)
|
||||||
|
# Allow access to Authelia's own endpoints
|
||||||
|
- domain: '*.your-tailnet.ts.net'
|
||||||
|
path_regex: '^/auth.*' # Match /auth and anything after it
|
||||||
|
policy: bypass
|
||||||
|
# Allow access to the root path (will be redirected by Traefik later)
|
||||||
|
- domain: '*.your-tailnet.ts.net'
|
||||||
|
path: '/'
|
||||||
|
policy: bypass
|
||||||
|
# Allow access to API endpoints (as requested, review security implications)
|
||||||
|
- domain: '*.your-tailnet.ts.net'
|
||||||
|
path_regex: '^/api.*' # Match /api and anything after it
|
||||||
|
policy: bypass
|
||||||
|
|
||||||
|
# 2. One-Factor Authentication Rules (Requires login)
|
||||||
|
# Add rules for each service you want to protect.
|
||||||
|
# The domain should match your Tailscale domain.
|
||||||
|
# The path should match the Traefik PathPrefix for the service.
|
||||||
|
- domain: '*.your-tailnet.ts.net'
|
||||||
|
path_regex: '^/sonarr.*'
|
||||||
|
policy: one_factor
|
||||||
|
- domain: '*.your-tailnet.ts.net'
|
||||||
|
path_regex: '^/radarr.*'
|
||||||
|
policy: one_factor
|
||||||
|
- domain: '*.your-tailnet.ts.net'
|
||||||
|
path_regex: '^/lidarr.*'
|
||||||
|
policy: one_factor
|
||||||
|
- domain: '*.your-tailnet.ts.net'
|
||||||
|
path_regex: '^/bazarr.*'
|
||||||
|
policy: one_factor
|
||||||
|
- domain: '*.your-tailnet.ts.net'
|
||||||
|
path_regex: '^/qbittorrent.*'
|
||||||
|
policy: one_factor
|
||||||
|
- domain: '*.your-tailnet.ts.net'
|
||||||
|
path_regex: '^/sabnzbd.*'
|
||||||
|
policy: one_factor
|
||||||
|
- domain: '*.your-tailnet.ts.net'
|
||||||
|
path_regex: '^/calibre.*'
|
||||||
|
policy: one_factor
|
||||||
|
- domain: '*.your-tailnet.ts.net'
|
||||||
|
path_regex: '^/home.*' # Protect the homepage
|
||||||
|
policy: one_factor
|
||||||
|
- domain: '*.your-tailnet.ts.net'
|
||||||
|
path_regex: '^/jellyseerr.*'
|
||||||
|
policy: one_factor
|
||||||
|
- domain: '*.your-tailnet.ts.net'
|
||||||
|
path_regex: '^/prowlarr.*'
|
||||||
|
policy: one_factor
|
||||||
|
- domain: '*.your-tailnet.ts.net'
|
||||||
|
path_regex: '^/flaresolverr.*'
|
||||||
|
policy: one_factor
|
||||||
|
# Add other services here following the pattern:
|
||||||
|
# - domain: '*.your-tailnet.ts.net'
|
||||||
|
# path_regex: '^/<service_path>.*'
|
||||||
|
# policy: one_factor
|
||||||
|
|
||||||
|
# 3. Default rule for the domain (optional, if you want a catch-all)
|
||||||
|
# This rule will apply if no path-specific rule above matches.
|
||||||
|
# You might want to deny or require one_factor for unmatched paths.
|
||||||
|
# Example: Deny any other path on the domain
|
||||||
|
# - domain: '*.your-tailnet.ts.net'
|
||||||
|
# policy: deny
|
||||||
|
# Example: Require login for any other path
|
||||||
|
# - domain: '*.your-tailnet.ts.net'
|
||||||
|
# policy: one_factor
|
||||||
|
|
||||||
|
# Notifier configuration
|
||||||
|
notifier:
|
||||||
|
filesystem:
|
||||||
|
filename: /config/notification.txt
|
||||||
|
|
||||||
|
# Identity Validation (includes JWT secret for password reset)
|
||||||
|
identity_validation:
|
||||||
|
reset_password:
|
||||||
|
jwt_secret: ${AUTHELIA_JWT_SECRET}
|
||||||
|
|
||||||
|
# Identity Providers
|
||||||
|
identity_providers:
|
||||||
|
oidc: null
|
||||||
39
authelia/users_database.example.yml
Normal file
39
authelia/users_database.example.yml
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# Authelia User Database
|
||||||
|
# Documentation: https://www.authelia.com/configuration/security/authentication/file/
|
||||||
|
|
||||||
|
# To add users:
|
||||||
|
# 1. Generate a password hash:
|
||||||
|
# docker run authelia/authelia:latest authelia hash-password 'your_strong_password'
|
||||||
|
# 2. Add the user entry below.
|
||||||
|
#
|
||||||
|
# To approve registered users (if registration is enabled in configuration.yml):
|
||||||
|
# 1. New users will appear here, possibly commented out or with 'disabled: true'.
|
||||||
|
# 2. Uncomment the user or set 'disabled: false' to grant access.
|
||||||
|
|
||||||
|
users:
|
||||||
|
# First user is typically considered the admin in access rules
|
||||||
|
admin:
|
||||||
|
displayname: "Admin User"
|
||||||
|
# Replace this hash with one generated for your desired password!
|
||||||
|
password: "$argon2id$v=19$m=102400,t=1,p=8$PBf/L9l3s7LwN6jX/B3tVg$9+q3kL8VAbpWj9Gv9Z6uA5bA4zT1fB2fH3aD5c6b7e8" # Example hash for 'password'
|
||||||
|
email: admin@example.com
|
||||||
|
groups:
|
||||||
|
- admins
|
||||||
|
- users
|
||||||
|
|
||||||
|
# Example of a regular user
|
||||||
|
# user1:
|
||||||
|
# displayname: "Regular User"
|
||||||
|
# password: "..." # Generate hash
|
||||||
|
# email: user1@example.com
|
||||||
|
# groups:
|
||||||
|
# - users
|
||||||
|
|
||||||
|
# Example of a registered user waiting for approval (if registration enabled)
|
||||||
|
# newuser:
|
||||||
|
# disabled: true
|
||||||
|
# displayname: "New User"
|
||||||
|
# password: "..." # Hash generated during registration
|
||||||
|
# email: newuser@example.com
|
||||||
|
# groups:
|
||||||
|
# - users
|
||||||
@ -13,18 +13,57 @@ services:
|
|||||||
- --experimental.plugins.rewrite-body.version=v1.2.0
|
- --experimental.plugins.rewrite-body.version=v1.2.0
|
||||||
- --experimental.plugins.rewriteHeaders.modulename=github.com/XciD/traefik-plugin-rewrite-headers
|
- --experimental.plugins.rewriteHeaders.modulename=github.com/XciD/traefik-plugin-rewrite-headers
|
||||||
- --experimental.plugins.rewriteHeaders.version=v0.0.3
|
- --experimental.plugins.rewriteHeaders.version=v0.0.3
|
||||||
network_mode: service:tailscale # Add this line
|
- --providers.docker.network=docker-compose-nas
|
||||||
# ports: # Remove this section
|
- --providers.docker.endpoint=unix:///var/run/docker.sock
|
||||||
# - "80:80"
|
network_mode: service:tailscale
|
||||||
# - "443:443"
|
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
# extra_hosts: # Remove this section
|
|
||||||
# - host.docker.internal:172.17.0.1
|
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "traefik", "healthcheck", "--ping"]
|
test: ["CMD", "traefik", "healthcheck", "--ping"]
|
||||||
interval: 30s
|
interval: 30s
|
||||||
retries: 10
|
retries: 10
|
||||||
|
redis:
|
||||||
|
image: redis:alpine
|
||||||
|
container_name: redis
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
- REDIS_PASSWORD=${AUTHELIA_REDIS_PASSWORD}
|
||||||
|
command: ["redis-server", "--requirepass", "${AUTHELIA_REDIS_PASSWORD}"]
|
||||||
|
volumes:
|
||||||
|
- ${CONFIG_ROOT:-.}/redis:/data:Z
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "redis-cli", "-a", "${AUTHELIA_REDIS_PASSWORD}", "ping"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 5
|
||||||
|
authelia:
|
||||||
|
image: authelia/authelia:latest
|
||||||
|
container_name: authelia
|
||||||
|
restart: always
|
||||||
|
user: ${USER_ID}:${GROUP_ID}
|
||||||
|
volumes:
|
||||||
|
- ${CONFIG_ROOT:-.}/authelia:/config:Z
|
||||||
|
environment:
|
||||||
|
- AUTHELIA_SESSION_SECRET=${AUTHELIA_SESSION_SECRET}
|
||||||
|
- AUTHELIA_STORAGE_ENCRYPTION_KEY=${AUTHELIA_STORAGE_ENCRYPTION_KEY}
|
||||||
|
- AUTHELIA_SESSION_REDIS_PASSWORD=${AUTHELIA_REDIS_PASSWORD}
|
||||||
|
- AUTHELIA_IDENTITY_VALIDATION_RESET_PASSWORD_JWT_SECRET=${AUTHELIA_JWT_SECRET}
|
||||||
|
- TZ=${TIMEZONE}
|
||||||
|
labels:
|
||||||
|
- traefik.enable=true
|
||||||
|
- traefik.http.routers.authelia.rule=Host(`${APP_HOSTNAME}`) && PathPrefix(`/auth`) # Changed rule
|
||||||
|
- traefik.http.routers.authelia.entrypoints=web
|
||||||
|
# - traefik.http.routers.authelia.priority=100 # Removed priority
|
||||||
|
- traefik.http.services.authelia.loadbalancer.server.port=9091
|
||||||
|
- traefik.http.middlewares.authelia-auth.forwardAuth.address=http://authelia:9091/api/verify # Simplified forwardAuth address
|
||||||
|
- traefik.http.routers.authelia.middlewares=https-proto@docker
|
||||||
|
- traefik.http.middlewares.authelia-auth.forwardAuth.trustForwardHeader=true
|
||||||
|
- traefik.http.middlewares.authelia-auth.forwardAuth.authResponseHeaders=Remote-User,Remote-Groups,Remote-Name,Remote-Email
|
||||||
|
- homepage.group=Security
|
||||||
|
- homepage.name=Authelia
|
||||||
|
- homepage.icon=authelia.png
|
||||||
|
- homepage.href=/auth # Updated href
|
||||||
|
- homepage.description=Authentication Portal
|
||||||
sonarr:
|
sonarr:
|
||||||
image: lscr.io/linuxserver/sonarr
|
image: lscr.io/linuxserver/sonarr
|
||||||
container_name: sonarr
|
container_name: sonarr
|
||||||
@ -42,8 +81,9 @@ services:
|
|||||||
retries: 10
|
retries: 10
|
||||||
labels:
|
labels:
|
||||||
- traefik.enable=true
|
- traefik.enable=true
|
||||||
- traefik.http.routers.sonarr.rule=PathPrefix(`/sonarr`)
|
- traefik.http.routers.sonarr.rule=Host(`${APP_HOSTNAME}`) && PathPrefix(`/sonarr`) # Added Host check
|
||||||
- traefik.http.routers.sonarr.entrypoints=web
|
- traefik.http.routers.sonarr.entrypoints=web
|
||||||
|
- traefik.http.routers.sonarr.middlewares=https-proto@docker,authelia-auth@docker
|
||||||
- traefik.http.services.sonarr.loadbalancer.server.port=8989
|
- traefik.http.services.sonarr.loadbalancer.server.port=8989
|
||||||
- homepage.group=Media
|
- homepage.group=Media
|
||||||
- homepage.name=Sonarr
|
- homepage.name=Sonarr
|
||||||
@ -71,8 +111,9 @@ services:
|
|||||||
retries: 10
|
retries: 10
|
||||||
labels:
|
labels:
|
||||||
- traefik.enable=true
|
- traefik.enable=true
|
||||||
- traefik.http.routers.radarr.rule=PathPrefix(`/radarr`)
|
- traefik.http.routers.radarr.rule=Host(`${APP_HOSTNAME}`) && PathPrefix(`/radarr`) # Added Host check
|
||||||
- traefik.http.routers.radarr.entrypoints=web
|
- traefik.http.routers.radarr.entrypoints=web
|
||||||
|
- traefik.http.routers.radarr.middlewares=https-proto@docker,authelia-auth@docker
|
||||||
- traefik.http.services.radarr.loadbalancer.server.port=7878
|
- traefik.http.services.radarr.loadbalancer.server.port=7878
|
||||||
- homepage.group=Media
|
- homepage.group=Media
|
||||||
- homepage.name=Radarr
|
- homepage.name=Radarr
|
||||||
@ -100,8 +141,9 @@ services:
|
|||||||
retries: 10
|
retries: 10
|
||||||
labels:
|
labels:
|
||||||
- traefik.enable=true
|
- traefik.enable=true
|
||||||
- traefik.http.routers.lidarr.rule=PathPrefix(`/lidarr`)
|
- traefik.http.routers.lidarr.rule=Host(`${APP_HOSTNAME}`) && PathPrefix(`/lidarr`) # Added Host check
|
||||||
- traefik.http.routers.lidarr.entrypoints=web
|
- traefik.http.routers.lidarr.entrypoints=web
|
||||||
|
- traefik.http.routers.lidarr.middlewares=https-proto@docker,authelia-auth@docker
|
||||||
- traefik.http.services.lidarr.loadbalancer.server.port=8686
|
- traefik.http.services.lidarr.loadbalancer.server.port=8686
|
||||||
- homepage.group=Media
|
- homepage.group=Media
|
||||||
- homepage.name=Lidarr
|
- homepage.name=Lidarr
|
||||||
@ -131,8 +173,9 @@ services:
|
|||||||
retries: 10
|
retries: 10
|
||||||
labels:
|
labels:
|
||||||
- traefik.enable=true
|
- traefik.enable=true
|
||||||
- traefik.http.routers.bazarr.rule=Host(`${TAILSCALE_HOSTNAME}.${TAILSCALE_TAILNET_DOMAIN}`) && PathPrefix(`/bazarr`)
|
- traefik.http.routers.bazarr.rule=Host(`${APP_HOSTNAME}`) && PathPrefix(`/bazarr`) # Added Host check
|
||||||
- traefik.http.routers.bazarr.entrypoints=web
|
- traefik.http.routers.bazarr.entrypoints=web
|
||||||
|
- traefik.http.routers.bazarr.middlewares=https-proto@docker,authelia-auth@docker
|
||||||
- traefik.http.services.bazarr.loadbalancer.server.port=6767
|
- traefik.http.services.bazarr.loadbalancer.server.port=6767
|
||||||
- homepage.group=Download
|
- homepage.group=Download
|
||||||
- homepage.name=Bazarr
|
- homepage.name=Bazarr
|
||||||
@ -165,10 +208,10 @@ services:
|
|||||||
retries: 10
|
retries: 10
|
||||||
labels:
|
labels:
|
||||||
- traefik.enable=true
|
- traefik.enable=true
|
||||||
- traefik.http.routers.jellyseerr.rule=PathPrefix(`/jellyseerr`)
|
- traefik.http.routers.jellyseerr.rule=Host(`${APP_HOSTNAME}`) && PathPrefix(`/jellyseerr`) # Added Host check
|
||||||
- traefik.http.routers.jellyseerr.entrypoints=web
|
- traefik.http.routers.jellyseerr.entrypoints=web
|
||||||
- traefik.http.services.jellyseerr.loadbalancer.server.port=5055
|
- traefik.http.services.jellyseerr.loadbalancer.server.port=5055
|
||||||
- traefik.http.routers.jellyseerr.middlewares=jellyseerr-stripprefix,jellyseerr-rewrite,jellyseerr-rewriteHeaders
|
- traefik.http.routers.jellyseerr.middlewares=https-proto@docker,jellyseerr-stripprefix,jellyseerr-rewrite,jellyseerr-rewriteHeaders,authelia-auth@docker
|
||||||
- traefik.http.middlewares.jellyseerr-stripprefix.stripPrefix.prefixes=/jellyseerr
|
- traefik.http.middlewares.jellyseerr-stripprefix.stripPrefix.prefixes=/jellyseerr
|
||||||
- traefik.http.middlewares.jellyseerr-rewriteHeaders.plugin.rewriteHeaders.rewrites[0].header=location
|
- traefik.http.middlewares.jellyseerr-rewriteHeaders.plugin.rewriteHeaders.rewrites[0].header=location
|
||||||
- traefik.http.middlewares.jellyseerr-rewriteHeaders.plugin.rewriteHeaders.rewrites[0].regex=^/(.+)$
|
- traefik.http.middlewares.jellyseerr-rewriteHeaders.plugin.rewriteHeaders.rewrites[0].regex=^/(.+)$
|
||||||
@ -239,8 +282,9 @@ services:
|
|||||||
retries: 10
|
retries: 10
|
||||||
labels:
|
labels:
|
||||||
- traefik.enable=true
|
- traefik.enable=true
|
||||||
- traefik.http.routers.prowlarr.rule=PathPrefix(`/prowlarr`)
|
- traefik.http.routers.prowlarr.rule=Host(`${APP_HOSTNAME}`) && PathPrefix(`/prowlarr`) # Added Host check
|
||||||
- traefik.http.routers.prowlarr.entrypoints=web
|
- traefik.http.routers.prowlarr.entrypoints=web
|
||||||
|
- traefik.http.routers.prowlarr.middlewares=https-proto@docker,authelia-auth@docker
|
||||||
- traefik.http.services.prowlarr.loadbalancer.server.port=9696
|
- traefik.http.services.prowlarr.loadbalancer.server.port=9696
|
||||||
- homepage.group=Download
|
- homepage.group=Download
|
||||||
- homepage.name=Prowlarr
|
- homepage.name=Prowlarr
|
||||||
@ -262,8 +306,9 @@ services:
|
|||||||
- TZ=${TIMEZONE}
|
- TZ=${TIMEZONE}
|
||||||
labels:
|
labels:
|
||||||
- traefik.enable=true
|
- traefik.enable=true
|
||||||
- traefik.http.routers.flaresolverr.rule=PathPrefix(`/flaresolverr`)
|
- traefik.http.routers.flaresolverr.rule=Host(`${APP_HOSTNAME}`) && PathPrefix(`/flaresolverr`) # Added Host check
|
||||||
- traefik.http.routers.flaresolverr.entrypoints=web
|
- traefik.http.routers.flaresolverr.entrypoints=web
|
||||||
|
- traefik.http.routers.flaresolverr.middlewares=https-proto@docker,authelia-auth@docker
|
||||||
- traefik.http.services.flaresolverr.loadbalancer.server.port=8191
|
- traefik.http.services.flaresolverr.loadbalancer.server.port=8191
|
||||||
profiles:
|
profiles:
|
||||||
- flaresolverr
|
- flaresolverr
|
||||||
@ -281,25 +326,20 @@ services:
|
|||||||
- ${DOWNLOAD_ROOT}:/data/torrents:Z
|
- ${DOWNLOAD_ROOT}:/data/torrents:Z
|
||||||
restart: always
|
restart: always
|
||||||
healthcheck:
|
healthcheck:
|
||||||
# Container may fail if the PIA's token expired, so mark as unhealthy when there is no internet connection
|
|
||||||
# see: https://github.com/qdm12/gluetun/issues/641#issuecomment-933856220
|
|
||||||
test:
|
test:
|
||||||
["CMD", "curl", "--fail", "http://127.0.0.1:8080", "https://google.com"]
|
["CMD", "curl", "--fail", "http://127.0.0.1:8080", "https://google.com"]
|
||||||
interval: 30s
|
interval: 30s
|
||||||
retries: 10
|
retries: 10
|
||||||
labels:
|
labels:
|
||||||
- traefik.enable=true
|
- traefik.enable=true
|
||||||
- traefik.http.routers.qbittorrent.rule=Host(`${TAILSCALE_HOSTNAME}.${TAILSCALE_TAILNET_DOMAIN}`) && PathPrefix(`/qbittorrent`)
|
- traefik.http.routers.qbittorrent.rule=Host(`${APP_HOSTNAME}`) && PathPrefix(`/qbittorrent`) # Added Host check
|
||||||
- traefik.http.routers.qbittorrent.entrypoints=web
|
- traefik.http.routers.qbittorrent.entrypoints=web
|
||||||
- traefik.http.services.qbittorrent.loadbalancer.server.port=8080
|
- traefik.http.services.qbittorrent.loadbalancer.server.port=8080
|
||||||
- traefik.http.routers.qbittorrent.middlewares=qbittorrent-strip-slash,qbittorrent-stripprefix
|
- traefik.http.routers.qbittorrent.middlewares=https-proto@docker,qbittorrent-strip-slash,qbittorrent-stripprefix,authelia-auth@docker
|
||||||
# https://github.com/qbittorrent/qBittorrent/issues/5693#issuecomment-552146296
|
|
||||||
- traefik.http.middlewares.qbittorrent-stripprefix.stripPrefix.prefixes=/qbittorrent
|
- traefik.http.middlewares.qbittorrent-stripprefix.stripPrefix.prefixes=/qbittorrent
|
||||||
# https://community.traefik.io/t/middleware-to-add-the-if-needed/1895/19
|
|
||||||
- traefik.http.middlewares.qbittorrent-strip-slash.redirectregex.regex=(^.*\/qbittorrent$$)
|
- traefik.http.middlewares.qbittorrent-strip-slash.redirectregex.regex=(^.*\/qbittorrent$$)
|
||||||
- traefik.http.middlewares.qbittorrent-strip-slash.redirectregex.replacement=$$1/
|
- traefik.http.middlewares.qbittorrent-strip-slash.redirectregex.replacement=$$1/
|
||||||
- traefik.http.middlewares.qbittorrent-strip-slash.redirectregex.permanent=false
|
- traefik.http.middlewares.qbittorrent-strip-slash.redirectregex.permanent=false
|
||||||
#- com.centurylinklabs.watchtower.depends-on=/vpn
|
|
||||||
- homepage.group=Download
|
- homepage.group=Download
|
||||||
- homepage.name=qBittorrent
|
- homepage.name=qBittorrent
|
||||||
- homepage.icon=qbittorrent.png
|
- homepage.icon=qbittorrent.png
|
||||||
@ -338,8 +378,9 @@ services:
|
|||||||
restart: always
|
restart: always
|
||||||
labels:
|
labels:
|
||||||
- traefik.enable=true
|
- traefik.enable=true
|
||||||
- traefik.http.routers.sabnzbd.rule=PathPrefix(`/sabnzbd`) # Simplified rule
|
- traefik.http.routers.sabnzbd.rule=Host(`${APP_HOSTNAME}`) && PathPrefix(`/sabnzbd`) # Added Host check
|
||||||
- traefik.http.routers.sabnzbd.entrypoints=web
|
- traefik.http.routers.sabnzbd.entrypoints=web
|
||||||
|
- traefik.http.routers.sabnzbd.middlewares=https-proto@docker,authelia-auth@docker
|
||||||
- traefik.http.services.sabnzbd.loadbalancer.server.port=8080
|
- traefik.http.services.sabnzbd.loadbalancer.server.port=8080
|
||||||
- homepage.group=Download
|
- homepage.group=Download
|
||||||
- homepage.name=Sabnzbd
|
- homepage.name=Sabnzbd
|
||||||
@ -359,7 +400,7 @@ services:
|
|||||||
- PUID=${USER_ID}
|
- PUID=${USER_ID}
|
||||||
- PGID=${GROUP_ID}
|
- PGID=${GROUP_ID}
|
||||||
- TZ=${TIMEZONE}
|
- TZ=${TIMEZONE}
|
||||||
- JELLYFIN_PublishedServerUrl=${TAILSCALE_HOSTNAME}.${TAILSCALE_TAILNET_DOMAIN}/jellyfin
|
- JELLYFIN_PublishedServerUrl=https://${TAILSCALE_HOSTNAME}.${TAILSCALE_TAILNET_DOMAIN}/jellyfin
|
||||||
volumes:
|
volumes:
|
||||||
- ${CONFIG_ROOT:-.}/jellyfin:/config:Z
|
- ${CONFIG_ROOT:-.}/jellyfin:/config:Z
|
||||||
- ${DATA_ROOT}:/data:Z
|
- ${DATA_ROOT}:/data:Z
|
||||||
@ -373,8 +414,9 @@ services:
|
|||||||
retries: 10
|
retries: 10
|
||||||
labels:
|
labels:
|
||||||
- traefik.enable=true
|
- traefik.enable=true
|
||||||
- traefik.http.routers.jellyfin.rule=PathPrefix(`/jellyfin`)
|
- traefik.http.routers.jellyfin.rule=Host(`${APP_HOSTNAME}`) && PathPrefix(`/jellyfin`) # Added Host check
|
||||||
- traefik.http.routers.jellyfin.entrypoints=web
|
- traefik.http.routers.jellyfin.entrypoints=web
|
||||||
|
- traefik.http.routers.jellyfin.middlewares=https-proto@docker # Only HTTPS, no auth
|
||||||
- traefik.http.services.jellyfin.loadbalancer.server.port=8096
|
- traefik.http.services.jellyfin.loadbalancer.server.port=8096
|
||||||
- homepage.group=Media
|
- homepage.group=Media
|
||||||
- homepage.name=Jellyfin
|
- homepage.name=Jellyfin
|
||||||
@ -403,8 +445,8 @@ services:
|
|||||||
- traefik.http.middlewares.calibre-headers.headers.customRequestHeaders.X-Scheme=https
|
- traefik.http.middlewares.calibre-headers.headers.customRequestHeaders.X-Scheme=https
|
||||||
- traefik.http.middlewares.calibre-headers.headers.customRequestHeaders.X-Script-Name=/calibre
|
- traefik.http.middlewares.calibre-headers.headers.customRequestHeaders.X-Script-Name=/calibre
|
||||||
- traefik.http.middlewares.calibre-stripprefixregex.stripPrefixRegex.regex=/calibre
|
- traefik.http.middlewares.calibre-stripprefixregex.stripPrefixRegex.regex=/calibre
|
||||||
- traefik.http.routers.calibre.middlewares=calibre-headers,calibre-stripprefixregex
|
- traefik.http.routers.calibre.middlewares=https-proto@docker,calibre-headers,calibre-stripprefixregex,authelia-auth@docker
|
||||||
- traefik.http.routers.calibre.rule=Host(`${TAILSCALE_HOSTNAME}.${TAILSCALE_TAILNET_DOMAIN}`) && PathPrefix(`/calibre`)
|
- traefik.http.routers.calibre.rule=Host(`${APP_HOSTNAME}`) && PathPrefix(`/calibre`) # Added Host check
|
||||||
- traefik.http.routers.calibre.entrypoints=web
|
- traefik.http.routers.calibre.entrypoints=web
|
||||||
- traefik.http.services.calibre.loadbalancer.server.port=8083
|
- traefik.http.services.calibre.loadbalancer.server.port=8083
|
||||||
- homepage.group=Media
|
- homepage.group=Media
|
||||||
@ -467,7 +509,6 @@ services:
|
|||||||
- HOMEPAGE_VAR_WEATHER_LONG=${HOMEPAGE_VAR_WEATHER_LONG}
|
- HOMEPAGE_VAR_WEATHER_LONG=${HOMEPAGE_VAR_WEATHER_LONG}
|
||||||
- HOMEPAGE_VAR_WEATHER_TIME=${TIMEZONE}
|
- HOMEPAGE_VAR_WEATHER_TIME=${TIMEZONE}
|
||||||
- HOMEPAGE_VAR_WEATHER_UNIT=${HOMEPAGE_VAR_WEATHER_UNIT}
|
- HOMEPAGE_VAR_WEATHER_UNIT=${HOMEPAGE_VAR_WEATHER_UNIT}
|
||||||
# Explicitly allow the hostname constructed from Tailscale variables
|
|
||||||
- HOMEPAGE_ALLOWED_HOSTS=${TAILSCALE_HOSTNAME}.${TAILSCALE_TAILNET_DOMAIN}
|
- HOMEPAGE_ALLOWED_HOSTS=${TAILSCALE_HOSTNAME}.${TAILSCALE_TAILNET_DOMAIN}
|
||||||
volumes:
|
volumes:
|
||||||
- ${CONFIG_ROOT:-.}/homepage:/app/config:Z
|
- ${CONFIG_ROOT:-.}/homepage:/app/config:Z
|
||||||
@ -478,11 +519,17 @@ services:
|
|||||||
[sh, -c, "cp -n /app/config/tpl/*.yaml /app/config && node server.js"]
|
[sh, -c, "cp -n /app/config/tpl/*.yaml /app/config && node server.js"]
|
||||||
labels:
|
labels:
|
||||||
- traefik.enable=true
|
- traefik.enable=true
|
||||||
# Change path to /home and use specific Tailscale host
|
- traefik.http.routers.homepage.rule=Host(`${APP_HOSTNAME}`) && PathPrefix(`/`) # Changed rule to root
|
||||||
- traefik.http.routers.homepage.rule=Host(`${TAILSCALE_HOSTNAME}.${TAILSCALE_TAILNET_DOMAIN}`) && PathPrefix(`/home`)
|
|
||||||
- traefik.http.routers.homepage.entrypoints=web
|
- traefik.http.routers.homepage.entrypoints=web
|
||||||
# Authelia middleware will be added in a later commit
|
# - traefik.http.routers.homepage.priority=10 # Removed priority
|
||||||
- traefik.http.services.homepage.loadbalancer.server.port=3000
|
# Global middleware for setting HTTPS header
|
||||||
|
- traefik.http.middlewares.https-proto.headers.customrequestheaders.X-Forwarded-Proto=https
|
||||||
|
- traefik.http.routers.homepage.middlewares=https-proto@docker,authelia-auth@docker
|
||||||
|
- homepage.group=Dashboard
|
||||||
|
- homepage.name=Homepage
|
||||||
|
- homepage.icon=homepage.png
|
||||||
|
- homepage.href=/ # Updated href
|
||||||
|
- homepage.description=Service Dashboard
|
||||||
watchtower:
|
watchtower:
|
||||||
image: ghcr.io/containrrr/watchtower:latest
|
image: ghcr.io/containrrr/watchtower:latest
|
||||||
container_name: watchtower
|
container_name: watchtower
|
||||||
@ -502,23 +549,22 @@ services:
|
|||||||
tailscale:
|
tailscale:
|
||||||
image: tailscale/tailscale:latest
|
image: tailscale/tailscale:latest
|
||||||
container_name: tailscale
|
container_name: tailscale
|
||||||
hostname: ${TAILSCALE_HOSTNAME:-tailscale-nas} # Hostname for Tailscale access
|
hostname: ${TAILSCALE_HOSTNAME:-tailscale-nas}
|
||||||
environment:
|
environment:
|
||||||
TS_AUTHKEY: ${TAILSCALE_AUTHKEY} # Needs to be set in .env
|
TS_AUTHKEY: ${TAILSCALE_AUTHKEY}
|
||||||
TS_EXTRA_ARGS: "--advertise-tags=${TAILSCALE_TAGS:-tag:nas}" # Keep tags if desired
|
TS_EXTRA_ARGS: "--advertise-tags=${TAILSCALE_TAGS:-tag:nas}"
|
||||||
TS_STATE_DIR: "/var/lib/tailscale"
|
TS_STATE_DIR: "/var/lib/tailscale"
|
||||||
TS_USERSPACE: "false"
|
TS_USERSPACE: "false"
|
||||||
# Switch to enable Funnel (public access) or Serve (Tailnet only)
|
|
||||||
ENABLE_FUNNEL_HTTPS: ${ENABLE_FUNNEL_HTTPS:-false}
|
ENABLE_FUNNEL_HTTPS: ${ENABLE_FUNNEL_HTTPS:-false}
|
||||||
volumes:
|
volumes:
|
||||||
- ${CONFIG_ROOT:-.}/tailscale/state:/var/lib/tailscale:Z # Persist state
|
- ${CONFIG_ROOT:-.}/tailscale/state:/var/lib/tailscale:Z
|
||||||
- /var/run/docker.sock:/var/run/docker.sock # Optional, keep if needed
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
devices:
|
devices:
|
||||||
- /dev/net/tun:/dev/net/tun
|
- /dev/net/tun:/dev/net/tun
|
||||||
cap_add:
|
cap_add:
|
||||||
- NET_ADMIN
|
- NET_ADMIN
|
||||||
- NET_RAW
|
- NET_RAW
|
||||||
extra_hosts: # Add this section
|
extra_hosts:
|
||||||
- host.docker.internal:172.17.0.1
|
- host.docker.internal:172.17.0.1
|
||||||
restart: always
|
restart: always
|
||||||
command:
|
command:
|
||||||
@ -542,8 +588,6 @@ services:
|
|||||||
done
|
done
|
||||||
echo " Tailscaled is running."
|
echo " Tailscaled is running."
|
||||||
|
|
||||||
# --- Start Tailscale Funnel/Serve ---
|
|
||||||
# Check the ENABLE_FUNNEL_HTTPS variable
|
|
||||||
if [ "${ENABLE_FUNNEL_HTTPS}" = "true" ]; then
|
if [ "${ENABLE_FUNNEL_HTTPS}" = "true" ]; then
|
||||||
echo "ENABLE_FUNNEL_HTTPS is true. Setting up Funnel -> http://localhost:80..."
|
echo "ENABLE_FUNNEL_HTTPS is true. Setting up Funnel -> http://localhost:80..."
|
||||||
tailscale funnel --bg http://localhost:80
|
tailscale funnel --bg http://localhost:80
|
||||||
@ -553,10 +597,9 @@ services:
|
|||||||
tailscale serve --bg http://localhost:80
|
tailscale serve --bg http://localhost:80
|
||||||
echo "Tailscale Serve configured."
|
echo "Tailscale Serve configured."
|
||||||
fi
|
fi
|
||||||
# --- End Tailscale Funnel/Serve ---
|
|
||||||
|
|
||||||
echo "Tailscale forwarding configured. Container will remain running."
|
echo "Tailscale forwarding configured. Container will remain running."
|
||||||
wait # Wait indefinitely for background processes
|
wait
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
default:
|
default:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user