From 8a52e6894fd26af18506a840ab117f4ff134117b Mon Sep 17 00:00:00 2001 From: aki Date: Fri, 25 Apr 2025 17:33:09 +0800 Subject: [PATCH] feat!: Add Authelia for authentication and Redis for session storage - Introduced Redis service for session management with health checks. - Added Authelia service for user authentication with necessary environment variables. - Configured Traefik to use Authelia as middleware for various services. - Created Authelia configuration file with session, storage, and access control settings. - Added user database for Authelia with an example admin user. --- .env.example | 8 +- README.md | 408 +++++++++++++++++++++--------------- authelia/configuration.yml | 83 ++++++++ authelia/users_database.yml | 39 ++++ docker-compose.yml | 80 ++++++- 5 files changed, 438 insertions(+), 180 deletions(-) create mode 100644 authelia/configuration.yml create mode 100644 authelia/users_database.yml diff --git a/.env.example b/.env.example index b03ff47..c736e17 100644 --- a/.env.example +++ b/.env.example @@ -67,13 +67,9 @@ AUTHELIA_SESSION_SECRET= # Example: your_strong_session_secret AUTHELIA_STORAGE_ENCRYPTION_KEY= # Example: your_strong_storage_encryption_key AUTHELIA_REDIS_PASSWORD= # Example: your_strong_redis_password -# Google OIDC Provider Settings (Get from Google Cloud Console - https://console.cloud.google.com/apis/credentials) -AUTHELIA_GOOGLE_OIDC_CLIENT_ID= # Example: your-google-client-id.apps.googleusercontent.com -AUTHELIA_GOOGLE_OIDC_CLIENT_SECRET= # Example: GOCSPX-your-google-client-secret - # Authelia Session Configuration -AUTHELIA_SESSION_DOMAIN=${TAILSCALE_HOSTNAME}.${TAILSCALE_TAILNET_DOMAIN} -AUTHELIA_DEFAULT_REDIRECT_URL=https://${TAILSCALE_HOSTNAME}.${TAILSCALE_TAILNET_DOMAIN}/home +AUTHELIA_SESSION_DOMAIN=${HOSTNAME} +AUTHELIA_DEFAULT_REDIRECT_URL=https://${HOSTNAME}/home # --- API Keys & Integration Tokens (Optional - Mainly for Homepage Widgets) --- # Find API keys within each application's settings (usually Settings > General or Security) diff --git a/README.md b/README.md index 678c1d6..6447e72 100644 --- a/README.md +++ b/README.md @@ -4,228 +4,308 @@ This project provides a comprehensive, self-hosted media and utility server setu The core idea is to manage media libraries (movies, TV shows, music), automate downloads securely, provide easy access via a dashboard, and enable remote access through Tailscale. +## Table of Contents + +- [Docker Compose NAS](#docker-compose-nas) + - [Table of Contents](#table-of-contents) + - [Architecture Overview](#architecture-overview) + - [Features](#features) + - [Prerequisites](#prerequisites) + - [Quick Start Guide](#quick-start-guide) + - [Configuration (`.env` Variables)](#configuration-env-variables) + - [Core System \& Paths](#core-system--paths) + - [Networking \& Access (Tailscale)](#networking--access-tailscale) + - [Authentication (Authelia)](#authentication-authelia) + - [Service Credentials](#service-credentials) + - [Homepage Customization \& Widgets](#homepage-customization--widgets) + - [Optional Features \& Services](#optional-features--services) + - [Detailed Setup \& Usage](#detailed-setup--usage) + - [Authelia User Management](#authelia-user-management) + - [(Optional) VPN Configuration](#optional-vpn-configuration) + - [(Optional) Traefik DNS Challenge](#optional-traefik-dns-challenge) + - [Service Access](#service-access) + - [Optional Services](#optional-services) + - [Troubleshooting](#troubleshooting) + - [SELinux Socket Permissions (Docker)](#selinux-socket-permissions-docker) + - [Tailscale Issues](#tailscale-issues) + - [File Permissions](#file-permissions) + - [Advanced Topics](#advanced-topics) + +## Architecture Overview + +This stack uses a combination of key services for routing, access, and security: + +* **[Tailscale](https://tailscale.com):** Provides a secure overlay network (WireGuard-based VPN) connecting your devices. It allows access to the NAS services from anywhere without opening firewall ports and handles HTTPS termination via its built-in `tailscale serve` or `tailscale funnel` features. All other services run within Tailscale's network namespace. +* **[Traefik](https://traefik.io):** Acts as a reverse proxy *within* the Tailscale network. It discovers services via Docker labels and routes incoming requests (from Tailscale) to the appropriate container based on paths (e.g., `/sonarr`, `/radarr`). +* **[Authelia](https://www.authelia.com):** Serves as the authentication gateway. Traefik forwards requests to Authelia for verification. If a user isn't logged in, they are redirected to the Authelia portal (`/`). Once authenticated, Authelia sets a session cookie (stored in Redis), and Traefik allows access to the requested service. + ## Features -This stack includes: +This stack includes the following services, categorized for clarity: -* **Reverse Proxy & Service Discovery:** [Traefik](https://traefik.io) automatically routes traffic to services. -* **Media Management:** - * [Sonarr](https://sonarr.tv): TV show management. - * [Radarr](https://radarr.video): Movie management. - * [Lidarr](https://lidarr.audio) (Optional): Music management. - * [Bazarr](https://www.bazarr.media/): Subtitle management. -* **Indexers & Downloads:** - * [Prowlarr](https://github.com/Prowlarr/Prowlarr): Indexer management for *arr apps. - * [qBittorrent](https://www.qbittorrent.org): Bittorrent client (can be configured to run through a VPN). - * [SABnzbd](https://sabnzbd.org/) (Optional): Usenet download client. -* **Media Server:** [Jellyfin](https://jellyfin.org) organizes and streams your media. -* **Request Management:** [Jellyseerr](https://github.com/FallenBagel/jellyseerr) allows users (including Jellyfin users) to request media. -* **Dashboard:** [Homepage](https://gethomepage.dev) provides a central dashboard to access all services. -* **Remote Access:** [Tailscale](https://tailscale.com) provides secure access to your services from anywhere without opening firewall ports. It handles HTTPS termination. -* **Utilities:** - * [Watchtower](https://containrrr.dev/watchtower/): Automatically updates running containers to the latest image. - * [Autoheal](https://github.com/willfarrell/docker-autoheal/): Monitors and restarts unhealthy containers. - * [Unpackerr](https://unpackerr.zip): Automatically extracts downloaded archives. -* **Other Optional Services:** AdGuard Home, Calibre-Web, Decluttarr, Tandoor Recipes, Joplin Server, Home Assistant, Immich Photos (enable via profiles). +**Core Infrastructure:** +* **Reverse Proxy:** [Traefik](https://traefik.io) - Manages internal routing and service discovery. +* **Secure Remote Access:** [Tailscale](https://tailscale.com) - Provides VPN access and HTTPS. +* **Authentication:** [Authelia](https://www.authelia.com) & [Redis](https://redis.io) - Single sign-on portal and session management. +* **Dashboard:** [Homepage](https://gethomepage.dev) - Centralized access point (at `/home`). + +**Media Management (\*Arr Suite):** +* [Sonarr](https://sonarr.tv): TV show management. +* [Radarr](https://radarr.video): Movie management. +* [Lidarr](https://lidarr.audio) (Optional): Music management. +* [Bazarr](https://www.bazarr.media/): Subtitle management. +* [Prowlarr](https://github.com/Prowlarr/Prowlarr): Indexer management for *arr apps. + +**Download Clients:** +* [qBittorrent](https://www.qbittorrent.org): Bittorrent client. +* [SABnzbd](https://sabnzbd.org/) (Optional): Usenet download client. + +**Media Serving & Requests:** +* [Jellyfin](https://jellyfin.org): Media server for streaming. +* [Jellyseerr](https://github.com/FallenBagel/jellyseerr): Media request management. + +**Utilities:** +* [Watchtower](https://containrrr.dev/watchtower/): Automatic container updates. +* [Autoheal](https://github.com/willfarrell/docker-autoheal/): Automatic container restarts on failure. +* [Unpackerr](https://unpackerr.zip): Automated archive extraction. + +**Optional Services (Enabled via Profiles):** +* [AdGuard Home](https://adguard.com/en/adguard-home/overview.html): Network-wide ad blocking. +* [Calibre-Web](https://github.com/janeczku/calibre-web): E-book library management. +* [Decluttarr](https://github.com/manimatter/decluttarr): Automated download cleanup. +* [Tandoor Recipes](https://docs.tandoor.dev/): Recipe management. +* [Joplin Server](https://joplinapp.org/): Note-taking synchronization server. +* [Home Assistant](https://www.home-assistant.io/): Home automation platform. +* [Immich](https://immich.app/): Self-hosted photo and video backup. +* [FlareSolverr](https://github.com/FlareSolverr/FlareSolverr): Bypasses Cloudflare challenges (e.g., for Prowlarr). ## Prerequisites -* **Linux Host:** Any recent Linux distribution capable of running Docker. Tested on Ubuntu Server 22.04. -* **Docker Engine:** Install the latest version of Docker Engine. [Official Installation Guide](https://docs.docker.com/engine/install/). -* **Docker Compose V2:** Ensure you have Docker Compose V2 (usually installed as a Docker plugin, invoked via `docker compose`). [Official Installation Guide](https://docs.docker.com/compose/install/). -* **User Permissions:** You'll need a user account that can run `docker` commands (usually by adding the user to the `docker` group) or run `docker compose` via `sudo`. -* **SELinux (If Enabled):** If your host uses SELinux (e.g., Fedora, CentOS, RHEL), you might need additional host configuration. See the [Troubleshooting](#selinux-socket-permissions) section. +* **Linux Host:** A system capable of running Docker (e.g., Ubuntu, Debian, Fedora). +* **Docker & Docker Compose:** Latest versions installed. See [Docker Engine Install](https://docs.docker.com/engine/install/) and [Docker Compose Install](https://docs.docker.com/compose/install/). +* **User Permissions:** Ability to run `docker` commands (user in `docker` group or use `sudo`). +* **Basic Linux Knowledge:** Familiarity with command line, text editors, and file permissions. +* **Tailscale Account:** Required for remote access. [Sign up here](https://tailscale.com/login). +* **(Optional) SELinux:** If enabled, see [Troubleshooting](#selinux-socket-permissions). -## Quick Start +## Quick Start Guide -1. **Clone the Repository:** +This guide gets the core stack running quickly. **Read the full [Configuration](#configuration-env-variables) section afterwards for details.** + +1. **Clone Repository:** ```bash git clone https://github.com/AdrienPoupa/docker-compose-nas.git cd docker-compose-nas ``` -2. **Create Configuration File:** - Copy the example environment file: +2. **Create `.env` File:** ```bash cp .env.example .env ``` -3. **Edit `.env` File:** - Open the `.env` file with a text editor and configure it according to your system and preferences. **This is the most crucial step.** See the detailed [Configuration (`.env` File)](#configuration-env-file) section below for explanations of each variable. Minimally, you **must** set `USER_ID`, `GROUP_ID`, `TIMEZONE`, `HOSTNAME`, and `TAILSCALE_AUTHKEY`. +3. **Critical `.env` Edits:** Open `.env` and set **at minimum**: + * `USER_ID` & `GROUP_ID`: Run `id -u` and `id -g` on your host to find these. + * `TIMEZONE`: Your local timezone (e.g., `Asia/Manila`). + * `TAILSCALE_AUTHKEY`: Generate an auth key in your [Tailscale Admin Console](https://login.tailscale.com/admin/settings/keys). + * `TAILSCALE_TAILNET_DOMAIN`: Your unique Tailnet domain (e.g., `your-name.ts.net`). + * `AUTHELIA_JWT_SECRET`, `AUTHELIA_SESSION_SECRET`, `AUTHELIA_STORAGE_ENCRYPTION_KEY`, `AUTHELIA_REDIS_PASSWORD`: **Generate unique, strong secrets** for these (e.g., using `openssl rand -hex 32`). **Do not use the examples!** -4. **Start the Stack:** - Run Docker Compose (use `sudo` if your user isn't in the `docker` group): +4. **Set Authelia Admin Password:** + * Generate a password hash (replace `'your_secure_password'`): + ```bash + docker run --rm authelia/authelia:latest authelia hash-password 'your_secure_password' + ``` + * Copy the **entire output hash** (starting with `$argon2id...`). + * Open `authelia/users_database.yml` and replace the example `password:` value under `admin:` with your generated hash. + +5. **Start the Stack:** ```bash + # Make helper script executable (if needed) + chmod +x ./update-config.sh + # Start containers docker compose up -d ``` - This will pull the necessary images and start all the core services in the background. + *(Wait for containers to download and start)* -5. **Run Initial Configuration Script:** - This script helps configure base URLs and API keys within the running *arr applications based on your `.env` file. +6. **(Optional) Run Config Script:** This attempts to set API keys/URLs in *arr apps based on `.env`. May require manual adjustments in apps later. ```bash ./update-config.sh ``` - *(Note: You might need to make it executable first: `chmod +x ./update-config.sh`)* -6. **Access Services:** Once Tailscale is connected, you should be able to access your services via `https://..ts.net/` or `https:///`. If you set up DNS for your `HOSTNAME`, you can use `https:///`. The main dashboard is at `/`. +7. **Access:** Navigate to `https://./`. You should be redirected to the Authelia login. Use `admin` and the password you set in step 4. After login, you'll land on the Homepage dashboard at `/home`. -## Configuration (`.env` File) +## Configuration (`.env` Variables) -This file controls all the essential settings for your Docker Compose stack. Copy `.env.example` to `.env` and edit the values. +This file controls essential settings. Copy `.env.example` to `.env` and modify the values. **Bold variables** are critical for initial setup. ---- +#### Core System & Paths -### **Core Settings (Required)** +| Variable | Description | Default | +| :----------------------- | :---------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------- | +| **`USER_ID`** | Linux user ID for container permissions. Find with `id -u`. | `1000` | +| **`GROUP_ID`** | Linux group ID for container permissions. Find with `id -g`. | `1000` | +| **`TIMEZONE`** | Your local timezone (e.g., `America/New_York`, `Asia/Manila`). [List](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). | `America/New_York` | +| `CONFIG_ROOT` | Host base directory for service configurations. `.` = project subdirs. | `.` | +| `DATA_ROOT` | Host directory for media libraries (movies, TV, music, etc.). | `/mnt/data` | +| `DOWNLOAD_ROOT` | Host directory for downloads (in progress/completed). **Must be on same filesystem as `DATA_ROOT` for hardlinks.** | `/mnt/data/torrents` | +| `IMMICH_UPLOAD_LOCATION` | Host path for Immich uploads (if `immich` profile enabled). | `/mnt/data/photos` | -These are fundamental for basic operation and permissions. +#### Networking & Access (Tailscale) -* `USER_ID`: The Linux user ID that the containers will run as. Find yours with `id -u`. - * *Default:* `1000` -* `GROUP_ID`: The Linux group ID that the containers will run as. Find yours with `id -g`. - * *Default:* `1000` - * **Note:** Using the correct IDs is crucial for file permissions, especially for accessing media files on the host. -* `TIMEZONE`: Your local timezone (e.g., `America/New_York`, `Europe/London`, `Asia/Manila`). Find yours from [this list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). - * *Default:* `America/New_York` -* `HOSTNAME`: **(Deprecated - Now derived)** The primary hostname used by Traefik for routing. This is now automatically constructed from `TAILSCALE_HOSTNAME` and `TAILSCALE_TAILNET_DOMAIN`. You generally don't need to set this directly unless overriding the default behavior. - * *Default:* `${TAILSCALE_HOSTNAME}.${TAILSCALE_TAILNET_DOMAIN}` +| Variable | Description | Default | +| :-------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------- | +| **`TAILSCALE_AUTHKEY`** | **Required.** Auth key from [Tailscale Admin Console](https://login.tailscale.com/admin/settings/keys). Use reusable or ephemeral. | *(None)* | +| **`TAILSCALE_TAILNET_DOMAIN`**| **Required.** Your unique Tailnet domain (e.g., `your-name.ts.net`). | `your-tailnet.ts.net` | +| `TAILSCALE_HOSTNAME` | Desired hostname for this NAS within Tailscale. | `tailscale-nas` | +| `TAILSCALE_TAGS` | Optional tags for the Tailscale node (e.g., `tag:nas`). | `tag:nas` | +| `ENABLE_FUNNEL_HTTPS` | Use Tailscale Funnel (`true` = public access via Tailscale domain) or Serve (`false` = Tailnet-only access, recommended). | `false` | +| `HOSTNAME` | Primary hostname used by Traefik/Authelia. Defaults to Tailscale FQDN. Can be overridden if using custom DNS pointing to Tailscale IP. | `${TAILSCALE_HOSTNAME}.${TAILSCALE_TAILNET_DOMAIN}` | ---- +#### Authentication (Authelia) -### **Host Paths (Required)** +| Variable | Description | Default | +| :---------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------- | +| **`AUTHELIA_JWT_SECRET`** | **Required.** Random secret for signing JWTs. **Generate your own!** | *(None - Example in file)* | +| **`AUTHELIA_SESSION_SECRET`** | **Required.** Random secret for session cookies. **Generate your own!** | *(None - Example in file)* | +| **`AUTHELIA_STORAGE_ENCRYPTION_KEY`** | **Required.** Random secret for encrypting data at rest. **Generate your own!** | *(None - Example in file)* | +| **`AUTHELIA_REDIS_PASSWORD`** | **Required.** Password for the Redis database. **Generate your own!** | *(None - Example in file)* | +| `AUTHELIA_SESSION_DOMAIN` | Domain for session cookies. Should match `HOSTNAME`. | `${HOSTNAME}` | +| `AUTHELIA_DEFAULT_REDIRECT_URL` | Where users land after login. | `https://${HOSTNAME}/home` | -Define where container data and configuration are stored on your host machine. +#### Service Credentials -* `CONFIG_ROOT`: The base directory on your host where configuration files for each service will be stored. Using `.` stores them in subdirectories within the project folder. - * *Default:* `.` -* `DATA_ROOT`: The main directory on your host containing your media libraries (movies, TV shows, music, books). - * *Default:* `/mnt/data` -* `DOWNLOAD_ROOT`: The directory on your host where the download client (qBittorrent/SABnzbd) will store downloads in progress and completed files *before* they are imported by *arr apps. - * *Default:* `/mnt/data/torrents` - * **Hardlink Note:** For efficient storage (avoiding duplicate files), it's highly recommended that `DOWNLOAD_ROOT` is on the **same filesystem** as `DATA_ROOT` (e.g., `/mnt/data/torrents` is inside `/mnt/data`). This allows instant moves via hardlinks instead of slow copies. +| Variable | Description | Default | +| :----------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| `QBITTORRENT_USERNAME` | Username for qBittorrent Web UI. | `admin` | +| `QBITTORRENT_PASSWORD` | Password for qBittorrent Web UI. **Change default!** (May need to use temp password from logs on first run, then change in UI & `.env`). | `adminadmin` | +| `CALIBRE_USERNAME` | Username for Calibre-Web (if `calibre-web` profile enabled). | `admin` | +| `CALIBRE_PASSWORD` | Password for Calibre-Web (if `calibre-web` profile enabled). | `admin123` | +| `IMMICH_DB_PASSWORD` | Password for Immich's internal database (if `immich` profile enabled). | `postgres` | +| `ADGUARD_USERNAME` | Username for AdGuard Home (if `adguardhome` profile enabled). | *(None)* | +| `ADGUARD_PASSWORD` | Password for AdGuard Home (if `adguardhome` profile enabled). | *(None)* | ---- +#### Homepage Customization & Widgets -### **Tailscale Access (Required)** +| Variable | Description | Default | +| :------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------- | +| `HOMEPAGE_VAR_TITLE` | Title shown on the dashboard. | `Docker-Compose NAS` | +| `HOMEPAGE_VAR_SEARCH_PROVIDER` | Default search engine. [Options](https://gethomepage.dev/en/widgets/search/). | `google` | +| `HOMEPAGE_VAR_HEADER_STYLE` | Dashboard header style. [Options](https://gethomepage.dev/en/configs/settings/#header-style). | `boxed` | +| `HOMEPAGE_VAR_WEATHER_CITY` | City for weather widget. | *(None)* | +| `HOMEPAGE_VAR_WEATHER_LAT` | Latitude for weather widget. | *(None)* | +| `HOMEPAGE_VAR_WEATHER_LONG` | Longitude for weather widget. | *(None)* | +| `HOMEPAGE_VAR_WEATHER_UNIT` | Weather units (`metric` or `imperial`). | `metric` | +| `SONARR_API_KEY` | API Keys for various services, primarily used for Homepage widgets. Find keys in each app's settings. | *(None)* | +| `RADARR_API_KEY` | " | *(None)* | +| `LIDARR_API_KEY` | " (if `lidarr` profile enabled) | *(None)* | +| `BAZARR_API_KEY` | " | *(None)* | +| `PROWLARR_API_KEY` | " | *(None)* | +| `JELLYFIN_API_KEY` | " | *(None)* | +| `JELLYSEERR_API_KEY` | " | *(None)* | +| `SABNZBD_API_KEY` | " (if `sabnzbd` profile enabled) | *(None)* | +| `IMMICH_API_KEY` | " (if `immich` profile enabled) | *(None)* | +| `HOMEASSISTANT_ACCESS_TOKEN` | " (if `homeassistant` profile enabled) | *(None)* | -Controls secure remote access via Tailscale. +#### Optional Features & Services -* `TAILSCALE_AUTHKEY`: **Required.** An authentication key from your Tailscale account. Generate one in the Tailscale Admin Console under Settings > Keys. You can use a reusable key or an ephemeral key (recommended for containers). - * *Default:* (None - **Must be set**) -* `TAILSCALE_HOSTNAME`: The desired hostname for this NAS within your Tailscale network. - * *Default:* `tailscale-nas` -* `TAILSCALE_TAILNET_DOMAIN`: **Required.** The domain of your Tailnet, including your Tailnet's unique name - * *Default:* `your-tailnet.ts.net` (**Must be set**) -* `TAILSCALE_TAGS`: Optional tags to apply to the Tailscale node (e.g., `tag:nas`). - * *Default:* `tag:nas` -* `ENABLE_FUNNEL_HTTPS`: Controls Tailscale's public accessibility. - * `true`: Enables Tailscale Funnel, making services accessible publicly via the Tailscale domain (`..ts.net`). Use with caution. - * `false`: Uses Tailscale Serve, making services accessible *only* to devices logged into your Tailnet. (Recommended) - * *Default:* `false` +| Variable | Description | Default | +| :---------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------- | +| `COMPOSE_PROFILES` | Comma-separated list of optional service profiles to enable (e.g., `lidarr,sabnzbd`). See [Optional Services](#optional-services). | *(None)* | +| `COMPOSE_PATH_SEPARATOR` | Path separator for `COMPOSE_FILE` (use `;` for Windows). | `:` | +| `COMPOSE_FILE` | Colon-separated list of compose files to use. Allows extending base config. | `docker-compose.yml:...` (See `.env.example`) | +| `DECLUTTARR_TEST_RUN` | Run Decluttarr in test mode (`True`/`False`)? (if `decluttarr` profile enabled). | `True` | +| `DECLUTTARR_...` | Other Decluttarr settings (see `.env.example`). | *(Varies)* | +| `PIA_USER` / `PIA_PASS` | Credentials for PIA VPN (if using default VPN setup for qBittorrent). | *(None)* | +| `PIA_LOCATION` | PIA server location (if using default VPN). [List](https://serverlist.piaservers.net/vpninfo/servers/v6). | `ca` | +| `PIA_LOCAL_NETWORK` | Your local network CIDR (e.g., `192.168.1.0/24`) to allow local access to VPN'd containers. | `192.168.0.0/16` | +| `DNS_CHALLENGE` | Enable Traefik DNS challenge for Let's Encrypt (`true`/`false`). **Not needed if using Tailscale for HTTPS.** | `true` | +| `DNS_CHALLENGE_PROVIDER` | Your DNS provider (e.g., `cloudflare`). [Providers](https://doc.traefik.io/traefik/https/acme/#providers). | `cloudflare` | +| `LETS_ENCRYPT_EMAIL` | Email for Let's Encrypt (if using DNS challenge). | *(None)* | +| `LETS_ENCRYPT_CA_SERVER` | Let's Encrypt server URL (if using DNS challenge). | `https://acme-v02.api.letsencrypt.org/directory` | +| `CLOUDFLARE_...` / `PROVIDER_...` | DNS provider API credentials (if using DNS challenge). | *(None)* | +| `HOMEASSISTANT_HOSTNAME` | Specific hostname for Home Assistant (if `homeassistant` profile enabled). | *(None)* | +| `IMMICH_HOSTNAME` | Specific hostname for Immich (if `immich` profile enabled). | *(None)* | +| `ADGUARD_HOSTNAME` | Specific hostname for AdGuard Home (if `adguardhome` profile enabled). | *(None)* | ---- +## Detailed Setup & Usage -### **Homepage Widgets (Optional)** +### Authelia User Management -API keys needed *only* if you want to display real-time information from these services on the Homepage dashboard. Find the API keys within each application's settings (usually under Settings > General or Settings > Security). +Authelia uses the `authelia/users_database.yml` file to manage users. -* `SONARR_API_KEY` -* `RADARR_API_KEY` -* `LIDARR_API_KEY` (If Lidarr profile is enabled) -* `BAZARR_API_KEY` -* `PROWLARR_API_KEY` -* `JELLYFIN_API_KEY` -* `JELLYSEERR_API_KEY` -* `SABNZBD_API_KEY` (If SABnzbd profile is enabled) -* `ADGUARD_USERNAME` / `ADGUARD_PASSWORD` (If AdGuard Home profile is enabled) -* `CALIBRE_USERNAME` / `CALIBRE_PASSWORD` (If Calibre-Web profile is enabled) +* **Setting the Initial Admin Password:** + 1. As mentioned in the Quick Start, you **must** set a strong password for the default `admin` user. + 2. Generate a hash using Docker (replace `'your_secure_password'`): + ```bash + docker run --rm authelia/authelia:latest authelia hash-password 'your_secure_password' + ``` + 3. Copy the **entire output hash** (starting with `$argon2id...`). + 4. Open `authelia/users_database.yml` and replace the example `password:` value under `admin:` with your generated hash. + 5. Ensure the `admin` user belongs to the `admins` and `users` groups as shown in the example. ---- +* **Adding More Users:** + 1. Generate a password hash for the new user as shown above. + 2. Edit `authelia/users_database.yml`. + 3. Add a new entry under `users:`, following the format of the `admin` user: + ```yaml + users: + admin: + # ... (admin details) ... + newuser: + displayname: "New User Name" + password: "paste_generated_hash_here" + email: newuser@example.com + groups: + - users # Add to 'admins' group if needed + ``` + 4. Save the file. Authelia should pick up the changes automatically (or restart the Authelia container: `docker compose restart authelia`). -### **Homepage Customization (Optional)** +* **Enabling User Registration (Optional):** + 1. Edit `authelia/configuration.yml`. + 2. Find the commented-out `registration:` section near the bottom. + 3. Uncomment it and set `enable: true`: + ```yaml + # registration: + # enable: false # Set to true to enable registration form + ``` + becomes: + ```yaml + registration: + enable: true + ``` + 4. Save the file and restart Authelia (`docker compose restart authelia`). + 5. A "Register" link will now appear on the Authelia login page. -Control the appearance and behavior of the Homepage dashboard. +* **Approving Registered Users:** + 1. When a user registers (if enabled), their details are added to `authelia/users_database.yml` but marked as `disabled: true`. + 2. To approve them, edit `authelia/users_database.yml`. + 3. Find the new user's entry. + 4. Change `disabled: true` to `disabled: false` (or simply remove the `disabled: true` line). + 5. Save the file. The user should now be able to log in. -* `HOMEPAGE_VAR_TITLE`: Title shown on the dashboard. - * *Default:* `Docker-Compose NAS` -* `HOMEPAGE_VAR_SEARCH_PROVIDER`: Default search engine. [See options](https://gethomepage.dev/en/widgets/search/). - * *Default:* `google` -* `HOMEPAGE_VAR_HEADER_STYLE`: Dashboard header style. [See options](https://gethomepage.dev/en/configs/settings/#header-style). - * *Default:* `boxed` -* `HOMEPAGE_VAR_WEATHER_CITY`, `_LAT`, `_LONG`, `_UNIT`: Configure the weather widget. +### (Optional) VPN Configuration ---- +*(Details about configuring the PIA VPN or other VPN setups could go here if needed.)* -### **Download Client Settings** +### (Optional) Traefik DNS Challenge -Credentials for included download clients. - -* `QBITTORRENT_USERNAME`: Username for qBittorrent Web UI. - * *Default:* `admin` -* `QBITTORRENT_PASSWORD`: Password for qBittorrent Web UI. - * *Default:* `adminadmin` - * **Note:** On first run, qBittorrent might generate a temporary password shown in its logs (`docker compose logs qbittorrent`). Log in with that, change the password in qBittorrent settings, and update this `.env` variable accordingly. - ---- - -### **VPN Configuration (Example: PIA - Optional)** - -These variables are specific to the example `thrnz/docker-wireguard-pia` VPN container used for qBittorrent in the default setup. If you use a different VPN provider or container, you'll need different variables. **If you don't use the VPN, you can ignore these.** - -* `PIA_USER`: Private Internet Access username. -* `PIA_PASS`: Private Internet Access password. -* `PIA_LOCATION`: PIA server location code (e.g., `ca_montreal`, `us_east`). [See list](https://serverlist.piaservers.net/vpninfo/servers/v6). - * *Default:* `ca` -* `PIA_LOCAL_NETWORK`: Your local network CIDR (e.g., `192.168.1.0/24`). Allows local access to the qBittorrent UI when the VPN is active. - * *Default:* `192.168.0.0/16` - ---- - -### **Traefik DNS Challenge (Optional)** - -These settings are for enabling automatic HTTPS certificate generation via Let's Encrypt using the DNS-01 challenge method. **This is generally NOT needed** because Tailscale handles HTTPS termination by default in this setup. Only configure this if you have a specific reason to manage your own certificates via Traefik (e.g., accessing services without Tailscale). - -* `DNS_CHALLENGE`: Set to `true` to enable DNS challenge. - * *Default:* `true` (Consider setting to `false` if using Tailscale for HTTPS) -* `DNS_CHALLENGE_PROVIDER`: Your DNS provider supported by Traefik/Lego (e.g., `cloudflare`, `godaddy`). [See providers](https://doc.traefik.io/traefik/https/acme/#providers). - * *Default:* `cloudflare` -* `LETS_ENCRYPT_EMAIL`: Your email address for Let's Encrypt notifications. -* `LETS_ENCRYPT_CA_SERVER`: Let's Encrypt server URL (use staging for testing). - * *Default:* `https://acme-v02.api.letsencrypt.org/directory` (Production) -* Provider-Specific Variables (e.g., `CLOUDFLARE_EMAIL`, `CLOUDFLARE_DNS_API_TOKEN`, `CLOUDFLARE_ZONE_API_TOKEN`): Credentials required by your chosen `DNS_CHALLENGE_PROVIDER`. Refer to Traefik documentation. - ---- - -### **Compose Profiles & Files (Advanced)** - -* `COMPOSE_PROFILES`: Comma-separated list of optional service profiles to enable (e.g., `lidarr,sabnzbd,adguardhome`). See [Optional Services](#optional-services). -* `COMPOSE_FILE`: Colon-separated list of compose files to use. Allows extending the base configuration. - * *Default:* `docker-compose.yml` +*(Details about setting up DNS provider credentials for Let's Encrypt could go here if needed.)* ## Service Access -With the default Tailscale setup, services are securely accessible via HTTPS using your Tailscale node's name or IP, followed by the service path. Replace `` with your Tailscale device name (e.g., `tailscale-nas.your-tailnet.ts.net`) or its Tailscale IP address. +With the default Tailscale setup and Authelia enabled, services are securely accessible via HTTPS using your Tailscale node's name or IP. Access requires authentication via Authelia. -* **Homepage:** `https:///home` -* **Sonarr:** `https:///sonarr` -* **Radarr:** `https:///radarr` -* **Lidarr:** `https:///lidarr` (If profile enabled) -* **Bazarr:** `https:///bazarr` -* **Jellyseerr:** `https:///jellyseerr` -* **Prowlarr:** `https:///prowlarr` +* **Login Portal:** `https:///` (Redirects unauthenticated users here) +* **Homepage Dashboard:** `https:///home` (Accessible after login) +* **Sonarr:** `https:///sonarr` (Requires login) +* **Radarr:** `https:///radarr` (Requires login) * **qBittorrent:** `https:///qbittorrent` -* **SABnzbd:** `https:///sabnzbd` (If profile enabled) * **Jellyfin:** `https:///jellyfin` -* **Calibre-Web:** `https:///calibre` (If profile enabled) -* **AdGuard Home:** `http://:3000` (If profile enabled, access via IP/port initially) -* **Tandoor Recipes:** `https:///recipes` (If profile enabled) -* **Joplin Server:** `https:///joplin` (If profile enabled) -* **Home Assistant:** `http://:8123` (If profile enabled, access via IP/port initially) -* **Immich:** `http://:2283` (If profile enabled, access via IP/port initially) +* ...and so on. -**Note:** -* `` refers to the full Tailscale name (e.g., `tailscale-nas.your-tailnet.ts.net`). -* `` refers to the Tailscale IP address of the NAS. -* Some services (AdGuard, HA, Immich) might require initial setup via their direct IP and port before Tailscale/Traefik routing is fully effective or configured within the application. Authentication for most services will be handled by Authelia (configured later). +Replace `` with your Tailscale device name (e.g., `tailscale-nas.your-tailnet.ts.net`) or its Tailscale IP address. + +If you configure DNS for your `HOSTNAME` variable to point to the Tailscale IP, you can use `https:///`. ## Optional Services diff --git a/authelia/configuration.yml b/authelia/configuration.yml new file mode 100644 index 0000000..155c6ae --- /dev/null +++ b/authelia/configuration.yml @@ -0,0 +1,83 @@ +# Authelia Configuration File +# Documentation: https://www.authelia.com/configuration/ + +# Default configuration options affecting multiple sections. +default_redirection_url: ${AUTHELIA_DEFAULT_REDIRECT_URL} + +# Server settings (listening address, paths, etc.) +server: + host: 0.0.0.0 + port: 9091 + +# Logging configuration +log: + level: info + format: text # or json + +# Session configuration +session: + name: authelia_session + secret: ${AUTHELIA_SESSION_SECRET} + expiration: 1h # Adjust as needed + inactivity: 5m # Adjust as needed + domain: ${AUTHELIA_SESSION_DOMAIN} # Set from .env + redis: + host: redis + port: 6379 + password: ${AUTHELIA_REDIS_PASSWORD} + database_index: 0 + +# 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 # Example using SQLite for simple storage needs + +# Authentication backend (using file-based user database) +authentication_backend: + file: + path: /config/users_database.yml + password: + algorithm: argon2id # Recommended hashing algorithm + iterations: 1 + memory: 1024 # MiB + parallelism: 8 + salt_length: 16 + key_length: 32 + +# Access control rules +access_control: + default_policy: deny # Deny access by default + rules: + # Rule to allow authenticated users access to the domain + - domain: ${AUTHELIA_SESSION_DOMAIN} + policy: one_factor # Requires username/password + +# Notifier (Optional, for password resets, etc. - configure if needed) +# notifier: +# smtp: +# address: smtp.example.com:587 +# username: user@example.com +# password: password +# sender: Authelia +# subject: "[Authelia] {title}" +# startup_check_address: test@authelia.com + +# JWT configuration (used for forwardAuth) +jwt_secret: ${AUTHELIA_JWT_SECRET} + +# Identity Providers (None configured for this setup) +identity_providers: + oidc: null # Explicitly disable OIDC + +# Enable registration (requires manual approval by editing users_database.yml) +# Set 'enable: true' to allow users to register. +# They will be added to users_database.yml commented out or with disabled: true. +# registration: +# enable: false # Set to true to enable registration form diff --git a/authelia/users_database.yml b/authelia/users_database.yml new file mode 100644 index 0000000..461f16d --- /dev/null +++ b/authelia/users_database.yml @@ -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 diff --git a/docker-compose.yml b/docker-compose.yml index 2f3263f..67e76b3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -25,6 +25,51 @@ services: test: ["CMD", "traefik", "healthcheck", "--ping"] interval: 30s retries: 10 + redis: + image: redis:alpine + container_name: redis + restart: always + environment: + - REDIS_PASSWORD=${AUTHELIA_REDIS_PASSWORD} + command: ["redis-server", "--requirepass", "$$REDIS_PASSWORD"] # Use $$ to escape $ for compose + volumes: + - ${CONFIG_ROOT:-.}/redis:/data:Z + healthcheck: + test: ["CMD", "redis-cli", "-a", "$$REDIS_PASSWORD", "ping"] # Use $$ to escape $ for compose + interval: 5s + timeout: 3s + retries: 5 + authelia: + image: authelia/authelia:latest + container_name: authelia + restart: always + volumes: + - ${CONFIG_ROOT:-.}/authelia:/config:Z + environment: + - AUTHELIA_JWT_SECRET=${AUTHELIA_JWT_SECRET} + - AUTHELIA_SESSION_SECRET=${AUTHELIA_SESSION_SECRET} + - AUTHELIA_STORAGE_ENCRYPTION_KEY=${AUTHELIA_STORAGE_ENCRYPTION_KEY} + - AUTHELIA_REDIS_PASSWORD=${AUTHELIA_REDIS_PASSWORD} + - AUTHELIA_SESSION_DOMAIN=${AUTHELIA_SESSION_DOMAIN} + - AUTHELIA_DEFAULT_REDIRECT_URL=${AUTHELIA_DEFAULT_REDIRECT_URL} + - TZ=${TIMEZONE} + labels: + - traefik.enable=true + # Rule for Authelia portal itself (handles internal paths like /api, /logout etc.) + - traefik.http.routers.authelia.rule=Host(`${HOSTNAME}`) + - traefik.http.routers.authelia.entrypoints=web + - traefik.http.routers.authelia.priority=100 # High priority to catch root path + - traefik.http.services.authelia.loadbalancer.server.port=9091 + # Define the forwardAuth middleware + - traefik.http.middlewares.authelia-auth.forwardAuth.address=http://authelia:9091/api/verify?rd=https://${HOSTNAME}/ + - traefik.http.middlewares.authelia-auth.forwardAuth.trustForwardHeader=true + - traefik.http.middlewares.authelia-auth.forwardAuth.authResponseHeaders=Remote-User,Remote-Groups,Remote-Name,Remote-Email + # Homepage labels for Authelia itself (optional, but can be nice) + - homepage.group=Security + - homepage.name=Authelia + - homepage.icon=authelia.png # You might need to add this icon + - homepage.href=https://${HOSTNAME}/ # Link to the login portal + - homepage.description=Authentication Portal sonarr: image: lscr.io/linuxserver/sonarr container_name: sonarr @@ -44,6 +89,7 @@ services: - traefik.enable=true - traefik.http.routers.sonarr.rule=PathPrefix(`/sonarr`) - traefik.http.routers.sonarr.entrypoints=web + - traefik.http.routers.sonarr.middlewares=authelia-auth@docker - traefik.http.services.sonarr.loadbalancer.server.port=8989 - homepage.group=Media - homepage.name=Sonarr @@ -73,6 +119,7 @@ services: - traefik.enable=true - traefik.http.routers.radarr.rule=PathPrefix(`/radarr`) - traefik.http.routers.radarr.entrypoints=web + - traefik.http.routers.radarr.middlewares=authelia-auth@docker - traefik.http.services.radarr.loadbalancer.server.port=7878 - homepage.group=Media - homepage.name=Radarr @@ -102,6 +149,7 @@ services: - traefik.enable=true - traefik.http.routers.lidarr.rule=PathPrefix(`/lidarr`) - traefik.http.routers.lidarr.entrypoints=web + - traefik.http.routers.lidarr.middlewares=authelia-auth@docker - traefik.http.services.lidarr.loadbalancer.server.port=8686 - homepage.group=Media - homepage.name=Lidarr @@ -131,8 +179,9 @@ services: retries: 10 labels: - traefik.enable=true - - traefik.http.routers.bazarr.rule=Host(`${TAILSCALE_HOSTNAME}.${TAILSCALE_TAILNET_DOMAIN}`) && PathPrefix(`/bazarr`) + - traefik.http.routers.bazarr.rule=PathPrefix(`/bazarr`) - traefik.http.routers.bazarr.entrypoints=web + - traefik.http.routers.bazarr.middlewares=authelia-auth@docker - traefik.http.services.bazarr.loadbalancer.server.port=6767 - homepage.group=Download - homepage.name=Bazarr @@ -168,7 +217,7 @@ services: - traefik.http.routers.jellyseerr.rule=PathPrefix(`/jellyseerr`) - traefik.http.routers.jellyseerr.entrypoints=web - traefik.http.services.jellyseerr.loadbalancer.server.port=5055 - - traefik.http.routers.jellyseerr.middlewares=jellyseerr-stripprefix,jellyseerr-rewrite,jellyseerr-rewriteHeaders + - traefik.http.routers.jellyseerr.middlewares=jellyseerr-stripprefix,jellyseerr-rewrite,jellyseerr-rewriteHeaders,authelia-auth@docker - 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].regex=^/(.+)$ @@ -241,6 +290,7 @@ services: - traefik.enable=true - traefik.http.routers.prowlarr.rule=PathPrefix(`/prowlarr`) - traefik.http.routers.prowlarr.entrypoints=web + - traefik.http.routers.prowlarr.middlewares=authelia-auth@docker - traefik.http.services.prowlarr.loadbalancer.server.port=9696 - homepage.group=Download - homepage.name=Prowlarr @@ -264,6 +314,7 @@ services: - traefik.enable=true - traefik.http.routers.flaresolverr.rule=PathPrefix(`/flaresolverr`) - traefik.http.routers.flaresolverr.entrypoints=web + - traefik.http.routers.flaresolverr.middlewares=authelia-auth@docker - traefik.http.services.flaresolverr.loadbalancer.server.port=8191 profiles: - flaresolverr @@ -289,10 +340,10 @@ services: retries: 10 labels: - traefik.enable=true - - traefik.http.routers.qbittorrent.rule=Host(`${TAILSCALE_HOSTNAME}.${TAILSCALE_TAILNET_DOMAIN}`) && PathPrefix(`/qbittorrent`) + - traefik.http.routers.qbittorrent.rule=PathPrefix(`/qbittorrent`) - traefik.http.routers.qbittorrent.entrypoints=web - traefik.http.services.qbittorrent.loadbalancer.server.port=8080 - - traefik.http.routers.qbittorrent.middlewares=qbittorrent-strip-slash,qbittorrent-stripprefix + - traefik.http.routers.qbittorrent.middlewares=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 # https://community.traefik.io/t/middleware-to-add-the-if-needed/1895/19 @@ -340,6 +391,7 @@ services: - traefik.enable=true - traefik.http.routers.sabnzbd.rule=PathPrefix(`/sabnzbd`) # Simplified rule - traefik.http.routers.sabnzbd.entrypoints=web + - traefik.http.routers.sabnzbd.middlewares=authelia-auth@docker - traefik.http.services.sabnzbd.loadbalancer.server.port=8080 - homepage.group=Download - homepage.name=Sabnzbd @@ -375,6 +427,7 @@ services: - traefik.enable=true - traefik.http.routers.jellyfin.rule=PathPrefix(`/jellyfin`) - traefik.http.routers.jellyfin.entrypoints=web + - traefik.http.routers.jellyfin.middlewares=authelia-auth@docker - traefik.http.services.jellyfin.loadbalancer.server.port=8096 - homepage.group=Media - homepage.name=Jellyfin @@ -403,8 +456,8 @@ services: - 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-stripprefixregex.stripPrefixRegex.regex=/calibre - - traefik.http.routers.calibre.middlewares=calibre-headers,calibre-stripprefixregex - - traefik.http.routers.calibre.rule=Host(`${TAILSCALE_HOSTNAME}.${TAILSCALE_TAILNET_DOMAIN}`) && PathPrefix(`/calibre`) + - traefik.http.routers.calibre.middlewares=calibre-headers,calibre-stripprefixregex,authelia-auth@docker + - traefik.http.routers.calibre.rule=PathPrefix(`/calibre`) - traefik.http.routers.calibre.entrypoints=web - traefik.http.services.calibre.loadbalancer.server.port=8083 - homepage.group=Media @@ -478,11 +531,18 @@ services: [sh, -c, "cp -n /app/config/tpl/*.yaml /app/config && node server.js"] labels: - traefik.enable=true - # Change path to /home and use specific Tailscale host - - traefik.http.routers.homepage.rule=Host(`${TAILSCALE_HOSTNAME}.${TAILSCALE_TAILNET_DOMAIN}`) && PathPrefix(`/home`) + # Rule for homepage, now at /home, needs auth + - traefik.http.routers.homepage.rule=Host(`${HOSTNAME}`) && PathPrefix(`/home`) - traefik.http.routers.homepage.entrypoints=web - # Authelia middleware will be added in a later commit - - traefik.http.services.homepage.loadbalancer.server.port=3000 + - traefik.http.routers.homepage.priority=10 # Lower priority than Authelia's root rule + - traefik.http.middlewares.homepage-stripprefix.stripPrefix.prefixes=/home + - traefik.http.routers.homepage.middlewares=homepage-stripprefix,authelia-auth@docker + # Homepage's own labels for discovery (unchanged) + - homepage.group=Dashboard + - homepage.name=Homepage + - homepage.icon=homepage.png + - homepage.href=/home # Update link to new path + - homepage.description=Service Dashboard watchtower: image: ghcr.io/containrrr/watchtower:latest container_name: watchtower