diff --git a/docs/FILE_PERMISSIONS.md b/docs/FILE_PERMISSIONS.md index 42ccd603..c6ceaafc 100755 --- a/docs/FILE_PERMISSIONS.md +++ b/docs/FILE_PERMISSIONS.md @@ -1,46 +1,78 @@ -# Managing File Permissions for NetAlertX on Nginx with Docker +# Managing File Permissions for NetAlertX on a Read-Only Container > [!TIP] -> If you are facing permission issues, try to start the container without mapping your volumes. If that works, then the issue is permission related. You can try e.g., the following command: -> ``` -> docker run -d --rm --network=host \ -> -e TZ=Europe/Berlin \ -> -e PUID=200 -e PGID=200 \ -> -e PORT=20211 \ -> ghcr.io/jokob-sk/netalertx:latest -> ``` -NetAlertX runs on an Nginx web server. On Alpine Linux, Nginx operates as the `nginx` user (if PUID and GID environment variables are not specified, nginx user UID will be set to 102, and its supplementary group `www-data` ID to 82). Consequently, files accessed or written by the NetAlertX application are owned by `nginx:www-data`. +> NetAlertX runs in a **secure, read-only Alpine-based container** under a dedicated `netalertx` user (UID 20211, GID 20211). All writable paths are either mounted as **persistent volumes** or **`tmpfs` filesystems**. This ensures consistent file ownership and prevents privilege escalation. -Upon starting, NetAlertX changes nginx user UID and www-data GID to specified values (or defaults), and the ownership of files on the host system mapped to `/app/config` and `/app/db` in the container to `nginx:www-data`. This ensures that Nginx can access and write to these files. Since the user in the Docker container is mapped to a user on the host system by ID:GID, the files in `/app/config` and `/app/db` on the host system are owned by a user with the same ID and GID (defaults are ID 102 and GID 82). On different systems, this ID:GID may belong to different users, or there may not be a group with ID 82 at all. +--- -Option to set specific user UID and GID can be useful for host system users needing to access these files (e.g., backup scripts). +## Writable Paths -### Permissions Table for Individual Folders +NetAlertX requires certain paths to be writable at runtime. These paths should be mounted either as **host volumes** or **`tmpfs`** in your `docker-compose.yml` or `docker run` command: -| Folder | User | User ID | Group | Group ID | Permissions | Notes | -|----------------|--------|---------|-----------|----------|-------------|---------------------------------------------------------------------| -| `/app/config` | nginx | PUID (default 102) | www-data | PGID (default 82) | rwxr-xr-x | Ensure `nginx` can read/write; other users can read if in `www-data` | -| `/app/db` | nginx | PUID (default 102) | www-data | PGID (default 82) | rwxr-xr-x | Same as above | +| Path | Purpose | Notes | +| ------------------------------------ | ----------------------------------- | ------------------------------------------------------ | +| `/app/config` | Application configuration | Persistent volume recommended | +| `/app/db` | Database files | Persistent volume recommended | +| `/app/log` | Logs | Can be `tmpfs` for speed or host volume to retain logs | +| `/app/api` | API cache | Use `tmpfs` for faster access | +| `/services/config/nginx/conf.active` | Active nginx configuration override | `tmpfs` recommended or customiozed file mounted | +| `/services/run` | Runtime directories for nginx & PHP | `tmpfs` required | +| `/tmp` | PHP session save directory | `tmpfs` required | +> All these paths will have **UID 20211 / GID 20211** inside the container. Files on the host will appear owned by `20211:20211`. -### Fixing Permission Problems +--- -The container fails to start with "Permission Denied" errors. This typically happens when your data volumes (`netalertx_config`, `netalertx_db`) are "owned" by the `root` user (UID 0) from a previous install, but the secure container must run as the `netalertx` user (UID 20211). +## Fixing Permission Problems + +Sometimes, permission issues arise if your existing host directories were created by a previous container running as root or another UID. The container will fail to start with "Permission Denied" errors. ### Solution -1. Run the container **once** as the `root` user (`--user "0"`) to trigger the built-in fix-it mode: +1. **Run the container once as root** (`--user "0"`) to allow it to correct permissions automatically: - ```bash - docker run -it --rm --name netalertx-fix --user "0" \ - -v netalertx_config:/app/config \ - -v netalertx_db:/app/db \ - netalertx:latest - ``` -2. Wait for the logs to show a **magenta warning** and confirm permissions are being fixed. The container will then hang (this is intentional). -3. Press **Ctrl+C** to stop the fix-it container. -4. Start your container normally (e.g., with `docker-compose up -d` or your standard `docker run` command). +```bash +docker run -it --rm --name netalertx --user "0" \ + -v local/path/config:/app/config \ + -v local/path/db:/app/db \ + ghcr.io/jokob-sk/netalertx:latest +``` + +2. Wait for logs showing **permissions being fixed**. The container will then **hang intentionally**. +3. Press **Ctrl+C** to stop the container. +4. Start the container normally with your `docker-compose.yml` or `docker run` command. + +> The container startup script detects `root` and runs `chown -R 20211:20211` on all volumes, fixing ownership for the secure `netalertx` user. + +--- + +## Example: docker-compose.yml with `tmpfs` + +```yaml +services: + netalertx: + container_name: netalertx + image: "ghcr.io/jokob-sk/netalertx" + network_mode: "host" + cap_add: + - NET_RAW + - NET_ADMIN + - NET_BIND_SERVICE + restart: unless-stopped + volumes: + - local/path/config:/app/config + - local/path/db:/app/db + environment: + - TZ=Europe/Berlin + - PORT=20211 + tmpfs: + - "/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime" + - "/app/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,sync,noatime,nodiratime" + - "/services/config/nginx/conf.active:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime" + - "/services/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime" + - "/tmp:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime" +``` + +> This setup ensures all writable paths are either in `tmpfs` or host-mounted, and the container never writes outside of controlled volumes. -### About This Method -The container’s startup script detects it is running as `root` (UID 0). It automatically runs the `chown` command to fix the permissions on your volume files, setting them to the correct `20211` user. It then hangs (`sleep infinity`) to prevent you from *ever* running the application as `root`, forcing you to restart securely. \ No newline at end of file diff --git a/docs/MIGRATION.md b/docs/MIGRATION.md index 0d0da165..07984177 100755 --- a/docs/MIGRATION.md +++ b/docs/MIGRATION.md @@ -1,6 +1,6 @@ # Migration -When upgrading from older versions of NetAlertX (or PiAlert (by jokob-sk)) the following data and setup migration steps need to be followed. +When upgrading from older versions of NetAlertX (or PiAlert by jokob-sk), follow the migration steps below to ensure your data and configuration are properly transferred. > [!TIP] > It's always important to have a [backup strategy](./BACKUPS.md) in place. @@ -27,7 +27,7 @@ You can migrate data manually, for example by exporting and importing devices us #### STEPS: The application will automatically migrate the database, configuration, and all device information. -A ticker message will appear at the top of the web UI until you update your Docker mount points. +A banner message will appear at the top of the web UI reminding you to update your Docker mount points. 1. Stop the container 2. [Back up your setup](./BACKUPS.md) @@ -37,7 +37,7 @@ A ticker message will appear at the top of the web UI until you update your Dock > [!TIP] -> If you have troubles accessing past backups, config or database files you can copy them into the newly mapped directories, for example by running this command in the container: `cp -r /app/config /home/pi/pialert/config/old_backup_files`. This should create a folder in the `config` directory called `old_backup_files` containing all the files in that location. Another approach is to map the old location and the new one at the same time to copy things over. +> If you have trouble accessing past backups, config or database files you can copy them into the newly mapped directories, for example by running this command in the container: `cp -r /app/config /home/pi/pialert/config/old_backup_files`. This should create a folder in the `config` directory called `old_backup_files` containing all the files in that location. Another approach is to map the old location and the new one at the same time to copy things over. #### New Docker mount locations @@ -58,7 +58,7 @@ The internal application path in the container has changed from `/home/pi/pialer > [!NOTE] -> The application uses symlinks linking the old db and config locations to the new ones, so data loss should not occur. [Backup strategies](./BACKUPS.md) are still recommended to backup your setup. +> The application automatically creates symlinks from the old database and config locations to the new ones, so data loss should not occur. Read the [backup strategies](./BACKUPS.md) guide to backup your setup. #### Examples @@ -92,16 +92,16 @@ services: ```yaml services: - netalertx: # ⚠ This has changed (🟡optional) - container_name: netalertx # ⚠ This has changed (🟡optional) - image: "ghcr.io/jokob-sk/netalertx:25.5.24" # ⚠ This has changed (🟡optional/🔺required in future) + netalertx: # 🆕 This has changed + container_name: netalertx # 🆕 This has changed + image: "ghcr.io/jokob-sk/netalertx:25.5.24" # 🆕 This has changed network_mode: "host" restart: unless-stopped volumes: - - local/path/config:/app/config # ⚠ This has changed (🔺required) - - local/path/db:/app/db # ⚠ This has changed (🔺required) + - local/path/config:/app/config # 🆕 This has changed + - local/path/db:/app/db # 🆕 This has changed # (optional) useful for debugging if you have issues setting up the container - - local/path/logs:/app/log # ⚠ This has changed (🟡optional) + - local/path/logs:/app/log # 🆕 This has changed environment: - TZ=Europe/Berlin - PORT=20211 @@ -138,16 +138,16 @@ services: ```yaml services: - netalertx: # ⚠ This has changed (🟡optional) - container_name: netalertx # ⚠ This has changed (🟡optional) - image: "ghcr.io/jokob-sk/netalertx:25.5.24" # ⚠ This has changed (🟡optional/🔺required in future) + netalertx: # 🆕 This has changed + container_name: netalertx # 🆕 This has changed + image: "ghcr.io/jokob-sk/netalertx:25.5.24" # 🆕 This has changed network_mode: "host" restart: unless-stopped volumes: - - local/path/config/app.conf:/app/config/app.conf # ⚠ This has changed (🔺required) - - local/path/db/app.db:/app/db/app.db # ⚠ This has changed (🔺required) + - local/path/config/app.conf:/app/config/app.conf # 🆕 This has changed + - local/path/db/app.db:/app/db/app.db # 🆕 This has changed # (optional) useful for debugging if you have issues setting up the container - - local/path/logs:/app/log # ⚠ This has changed (🟡optional) + - local/path/logs:/app/log # 🆕 This has changed environment: - TZ=Europe/Berlin - PORT=20211 @@ -156,7 +156,7 @@ services: ### 1.2 Migration from NetAlertX `v25.5.24` -Versions before `v25.10.1` require an intermediate migration through `v25.5.24` to ensure database compatibility. +Versions before `v25.10.1` require an intermediate migration through `v25.5.24` to ensure database compatibility. Skipping this step may cause compatibility issues due to database schema changes introduced after `v25.5.24`. #### STEPS: @@ -180,7 +180,7 @@ Examples of docker files with the tagged version. services: netalertx: container_name: netalertx - image: "ghcr.io/jokob-sk/netalertx:25.5.24" # ⚠ This is important (🔺required) + image: "ghcr.io/jokob-sk/netalertx:25.5.24" # 🆕 This is important network_mode: "host" restart: unless-stopped volumes: @@ -197,7 +197,7 @@ services: services: netalertx: container_name: netalertx - image: "ghcr.io/jokob-sk/netalertx:25.10.1" # ⚠ This is important (🔺required) + image: "ghcr.io/jokob-sk/netalertx:25.10.1" # 🆕 This is important network_mode: "host" restart: unless-stopped volumes: @@ -212,30 +212,19 @@ services: ### 1.3 Migration from NetAlertX `v25.10.1` -> [!WARNING] -> This section is under development. The migration path from `v25.10.1` to future versions (e.g., `v25.11.x` and newer) will be published soon. +Starting from v25.10.1, the container uses a [more secure, read-only runtime environment](./SECURITY_FEATURES.md), which requires all writable paths (e.g., logs, API cache, temporary data) to be mounted as `tmpfs` or permanent writable volumes, with sufficient access [permissions](./FILE_PERMISSIONS.md). #### STEPS: 1. Stop the container 2. [Back up your setup](./BACKUPS.md) -3. Upgrade to `v25.10.1` by pinning the release version (See Examples below) -4. Start the container and verify everything works as expected. -5. Stop the container -6. 🔻 TBC 🔺 Perform a one-off migration to the `20211` user `docker run -it --rm --name netalertx --user "0" -v netalertx_config:/app/config -v netalertx_db:/app/db netalertx:latest` -7. 🔻 TBC 🔺 Stop the container -8. 🔻 TBC 🔺 Switch to the latest `netalertx` image -9. 🔻 TBC 🔺 Start the container and verify everything works as expected. - -##### Example 1: Mapping folders - -###### docker-compose.yml changes +3. Upgrade to `v25.10.1` by pinning the release version (See the example below) ```yaml services: netalertx: container_name: netalertx - image: "ghcr.io/jokob-sk/netalertx:25.10.1" # ⚠ This is important (🔺required) + image: "ghcr.io/jokob-sk/netalertx:25.10.1" # 🆕 This is important network_mode: "host" restart: unless-stopped volumes: @@ -248,12 +237,56 @@ services: - PORT=20211 ``` -🔻 TBC 🔺 +4. Start the container and verify everything works as expected. +5. Stop the container. +6. Perform a one-off migration to the latest `netalertx` image and `20211` user: -```bash +> [!NOTE] +> The example below assumes your `/config` and `/db` folders are stored in `local/path`. +> Replace this path with your actual configuration directory. `netalertx` is the container name, whcih might differ from your setup. + +```sh docker run -it --rm --name netalertx --user "0" \ - -v netalertx_config:/app/config \ - -v netalertx_db:/app/db \ - netalertx:latest + -v local/path/config:/app/config \ + -v local/path/db:/app/db \ + ghcr.io/jokob-sk/netalertx:latest ``` +7. Stop the container +8. Update the `docker-compose.yml` as per example below. + +```yaml +services: + netalertx: + container_name: netalertx + image: "ghcr.io/jokob-sk/netalertx" # 🆕 This is important + network_mode: "host" + cap_add: # 🆕 New line + - NET_RAW # 🆕 New line + - NET_ADMIN # 🆕 New line + - NET_BIND_SERVICE # 🆕 New line + restart: unless-stopped + volumes: + - local/path/config:/app/config + - local/path/db:/app/db + # (optional) useful for debugging if you have issues setting up the container + #- local/path/logs:/app/log + environment: + - TZ=Europe/Berlin + - PORT=20211 + # 🆕 New "tmpfs" section START 🔽 + tmpfs: + # Speed up logging. This can be commented out to retain logs between container restarts + - "/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime" + # Speed up API access as frontend/backend API is very chatty + - "/app/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,sync,noatime,nodiratime" + # Required for customization of the nginx listen addr/port without rebuilding the container + - "/services/config/nginx/conf.active:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime" + # /services/config/nginx/conf.d is required for nginx and php to start + - "/services/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime" + # /tmp is required by php for session save this should be reworked to /services/run/tmp + - "/tmp:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime" + # 🆕 New "tmpfs" section END 🔼 +``` + +9. Start the container and verify everything works as expected. \ No newline at end of file