Skip to content

[Bug]: Rclone daemon lacks supplementary groups when accessing ZFS NFSv4 ACL directories #281

Description

@GoingTime

Pre-submission Checklist

  • I have searched existing issues (open and closed) to ensure this is not a duplicate.
  • I am using the latest version of RClone Manager or have checked recent release notes.

Bug Description

Related Information:
https://github.com/orgs/Zarestia-Dev/discussions/278

AI Statement:
The following content is a summary and recommendations provided by an AI after diagnosing and resolving the issue.


1. User Intent

The user wants to browse ZFS dataset subdirectories mounted from the TrueNAS host into the Docker container via the RClone Manager web file manager, and see the list of files and folders inside them. The dataset uses NFSv4 ACLs for access control, and the container runs as a non-root user configured through PUID / PGID.


2. Operation Flow and Actual Problem

Operation Flow

  1. Deploy rclone-manager-headless on TrueNAS via Docker / TrueNAS Apps.
  2. Mount a host ZFS dataset into the container (e.g., /mnt/<dataset>).
  3. Set container environment variables PUID=<UID> and PGID=<GID> to run the process as an unprivileged user.
  4. In the RClone Manager UI, click a subdirectory under the mounted path (e.g., /mnt/<dataset>/<subdir>).

Actual Problem

  • The UI can display the subdirectory itself, but its contents appear empty.
  • The rclone daemon log repeatedly shows permission errors like:
  ERROR : <subdir>: failed to open directory "<subdir>": open /mnt/<dataset>/<subdir>: permission denied
  • However, running the following command directly inside the container works fine:

    docker exec -u <UID> <container> rclone lsjson /mnt/<dataset>/<subdir>
  • Mount propagation, UID/GID mapping, capabilities, and LSM labels have all been ruled out. The issue is isolated to the rclone daemon process itself, not to processes started via docker exec.


3. Root Cause Analysis

3.1 The Only Credential Difference

Comparing two processes running with the same uid=<UID> and gid=<GID>:

Process UID GID Groups (supplementary groups) Directory Access
Process started via docker exec -u <UID> <UID> <GID> <GID> ✅ Success
rclone daemon started by gosu <user> via entrypoint <UID> <GID> empty ❌ Permission denied

3.2 Why docker exec Works but the Daemon Does Not

  • docker exec -u <UID> populates the process's supplementary groups based on /etc/passwd and /etc/group inside the container.
  • The current entrypoint.sh uses gosu <user> ... to drop privileges. gosu only sets uid and gid and does not set supplementary groups, so the rclone daemon's /proc/<pid>/status shows Groups: as empty.

3.3 ZFS NFSv4 ACL Evaluation

The dataset uses acltype=nfsv4. Its ACL contains an explicit ACE granting access to the target group:

{
  "acltype": "NFS4",
  "acl": [
    {"tag": "owner@", "type": "ALLOW", "perms": {"BASIC": "FULL_CONTROL"}},
    {"tag": "group@", "type": "ALLOW", "perms": {"BASIC": "FULL_CONTROL"}},
    {"tag": "GROUP", "type": "ALLOW", "perms": {"BASIC": "MODIFY"}, "id": <GID>}
  ]
}

There is no EVERYONE@ or other allowance. Therefore, the running user <UID> must be a member of group <GID> for the NFSv4 ACL check to pass. Because the daemon's supplementary groups are empty, the kernel cannot match the group ACE, and open() / openat() returns EACCES.

3.4 Reproducible Verification

The discrepancy can be reproduced reliably inside the container:

# Clear supplementary groups → fails (same behavior as the daemon)
setpriv --reuid=<UID> --regid=<GID> --clear-groups ls /mnt/<dataset>/<subdir>

# Keep the target GID as a supplementary group → succeeds
setpriv --reuid=<UID> --regid=<GID> --groups=<GID> ls /mnt/<dataset>/<subdir>

4. Fix Recommendation

It is recommended to change the privilege-dropping mechanism in entrypoint.sh from gosu to setpriv, and explicitly set supplementary groups. To support both simple and complex user scenarios (single ZFS dataset, multiple datasets, multiple ACL groups), the following configurable approach is recommended.

4.1 Recommended Change

Replace this line in entrypoint.sh:

exec gosu <user> /usr/local/bin/rclone-manager-headless "${ARGS[@]}" "$@"

with:

# User and group have already been adjusted via groupmod/usermod to PUID/PGID
USER_UID="${PUID:-1000}"
USER_GID="${PGID:-1000}"

# By default, add PGID as a supplementary group so NFSv4 ACL checks can match
# Users can specify additional groups via PGIDS, e.g., PGIDS=3100,3000,950
SUP_GROUPS="${PGIDS:-$USER_GID}"

# Start with setpriv, preserving the target groups
exec setpriv \
  --reuid="$USER_UID" \
  --regid="$USER_GID" \
  --groups="$SUP_GROUPS" \
  --inh-caps=-all \
  /usr/local/bin/rclone-manager-headless "${ARGS[@]}" "$@"

4.2 Design Rationale

  • Backward-compatible default behavior: If PGIDS is not set, the script automatically includes PGID as a supplementary group, covering the most common single-dataset case.
  • Supports complex ACL scenarios: The new PGIDS environment variable allows users to specify multiple supplementary groups for multi-dataset or multi-group ACL deployments.
  • No additional privilege escalation: --inh-caps=-all clears inherited capabilities, keeping the same security model as gosu.
  • No host-side ACL changes: The fix is entirely inside the container and does not require modifying ZFS ACLs or file ownership.
  • No hard-coded group IDs: The solution uses environment variables, so it works with any PUID / PGID combination.

4.3 Temporary Verification Without Rebuilding the Image

While waiting for an official fix, users can verify inside the container:

# Enter the container
docker exec -it <container> bash

# Start a test process with the target GID as a supplementary group
setpriv --reuid=<UID> --regid=<GID> --groups=<GID> \
  /data/rclone-bin/rclone lsjson /mnt/<dataset>/<subdir>

If this succeeds, the issue is confirmed to be supplementary groups.

4.4 Long-Term Fix Path

  1. Update the base image to include setpriv (usually provided by util-linux) and update entrypoint.sh.
  2. Optionally keep gosu as a fallback via a new environment variable DROPPRIVS_TOOL=gosu|setpriv (defaulting to setpriv).
  3. Document the purpose of PGIDS so that TrueNAS / ZFS users can diagnose similar issues.

5. Potential Side Effects and Risks

Risk Description Mitigation
Wider group permissions After adding the group, the daemon gains all permissions granted to that group in the ACL (e.g., MODIFY). If the user expected read-only access, they must tighten the ACL accordingly. Document the relationship between PGIDS and ACL permissions
Rootless container startup failure The current entrypoint relies on root to run groupmod, usermod, and chown. If a user runs the container with --user as non-root, setpriv will fail. Add a guard like if [ "$(id -u)" -eq 0 ]; then ...; fi to skip group changes and setpriv when not running as root
Capabilities behavior change --inh-caps=-all clears inherited capabilities, which is safer but may affect edge cases requiring capabilities (e.g., binding low ports). No impact for this image's current use case (file access and HTTP services); can remain the default
Missing setpriv in minimal images If the base image does not include util-linux, the setpriv command will be missing. Ensure util-linux is installed in the Dockerfile
Misconfigured multi-group list Users may incorrectly format PGIDS (e.g., wrong separator or group IDs). Add a log line like echo "Using supplementary groups: $SUP_GROUPS" in the entrypoint for debugging

6. Summary

This is not a Docker mount propagation issue, a UID mapping issue, an AppArmor/SELinux issue, or a frontend filtering bug in RClone Manager. It is caused by the entrypoint's privilege-dropping mechanism not propagating supplementary groups, which prevents the ZFS NFSv4 ACL from authorizing the rclone daemon. Replacing gosu with setpriv and exposing a PGIDS environment variable provides a robust, configurable fix for a wide range of user deployments.


### Operating System

Linux

### OS Version

26.0.0-BETA.2

### Architecture / CPU

x86_64 (64-bit Intel/AMD)

### App Version Type

Headless (Web server accessed via browser)

### App Version Number

0.3.1

### RClone Version

_No response_

### Steps to Reproduce

None.

### Expected Behavior

_No response_

### Relevant Logs & Output

```shell

Screenshots / Screen Recordings

No response

Additional Context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions