You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A user moved with userTransfer.php can come out the other side with broken rootless Docker containers when the user's subordinate ID range (/etc/subuid, /etc/subgid) differs between the source and target hosts.
Observed in a production migration. The source user had subuid/subgid = 100000:65536; the target user (same username) had 165536:65536. After a complete transfer (31 passes plus the final volatile sync, with the user's Docker stopped on the source first):
Inside the Docker data-root (~/.local/share/docker, synced by the final volatile pass): ownership kept the source's numeric sub-IDs (e.g. 100999, 100041, 100502). Those IDs have no name, so rsync keeps them as numbers. On the target they fall outside the user's range and inside another tenant's subordinate range. One of six containers then failed to start: lstat .../rootfs/overlayfs/<id>/app: permission denied.
Outside the data-root, in directories the containers bind-mount from the home: the source had ~1,600 files owned by container sub-IDs, and the target ended up with 3. The rest were owned by the plain user. Inside the container those files appear root-owned, which breaks apps that run as a non-root container UID. Which post-transfer step flattened them is not yet traced. The candidates are the Normalising user permissions step (util/userPermissions.php, called from lib/userTransfer/postSetup.php) and the payload-ownership handling added for userTransfer: bulk data payload not chowned to target user on cross-username migration; userPermissions.php excludes payload, so data stays owned by the source uid #787.
Expected
Container-owned files keep the same container-relative ownership after a move. For every uid/gid inside the source user's subordinate range: target_id = id - source_range_start + target_range_start. This applies both inside the data-root and in the rest of the home. Files owned by the user itself map to the target user, as today.
Suggested fix (for review)
Before the final sync, read the source user's subuid/subgid start (remote) and the target user's (local).
After the final sync: for every path under the home whose uid/gid lies in the source range, chown -h it to the offset ID. Whichever post-transfer step flattens those files to the user must skip them.
Log how many files were remapped, and assert that no file under the home is left owned by an ID in another user's range.
Impact
This affects any Docker-using account moved between hosts whose subordinate ranges differ. Containers that write to bind mounts, or whose image layers contain non-root-owned paths, break or misbehave. Ownership also points at another tenant's ID range. Home-directory permissions stop access across tenants, but the files are still owned by the wrong IDs.
Related
#787 (closed) fixed data ownership when the username changes between hosts. This is a different case: the same username, but container-owned sub-IDs that need an offset instead of a flatten.
(Container IDs are relative. The ranges aren't.)
Tier-3 declared at filing: --why multi-file — GH#631 tier gate. Declared fix size: 90 lines vs 28-line body — size gate (operator directive 2026-07-29). Owner: scripts/lib/userTransfer/postSetup.php in MagnaCapax/PMSS — owner gate.
Problem
A user moved with
userTransfer.phpcan come out the other side with broken rootless Docker containers when the user's subordinate ID range (/etc/subuid,/etc/subgid) differs between the source and target hosts.Observed in a production migration. The source user had
subuid/subgid = 100000:65536; the target user (same username) had165536:65536. After a complete transfer (31 passes plus the final volatile sync, with the user's Docker stopped on the source first):~/.local/share/docker, synced by the final volatile pass): ownership kept the source's numeric sub-IDs (e.g. 100999, 100041, 100502). Those IDs have no name, so rsync keeps them as numbers. On the target they fall outside the user's range and inside another tenant's subordinate range. One of six containers then failed to start:lstat .../rootfs/overlayfs/<id>/app: permission denied.Normalising user permissionsstep (util/userPermissions.php, called fromlib/userTransfer/postSetup.php) and the payload-ownership handling added for userTransfer: bulk data payload not chowned to target user on cross-username migration; userPermissions.php excludes payload, so data stays owned by the source uid #787.Expected
Container-owned files keep the same container-relative ownership after a move. For every uid/gid inside the source user's subordinate range:
target_id = id - source_range_start + target_range_start. This applies both inside the data-root and in the rest of the home. Files owned by the user itself map to the target user, as today.Suggested fix (for review)
subuid/subgidstart (remote) and the target user's (local).chown -hit to the offset ID. Whichever post-transfer step flattens those files to the user must skip them.Impact
This affects any Docker-using account moved between hosts whose subordinate ranges differ. Containers that write to bind mounts, or whose image layers contain non-root-owned paths, break or misbehave. Ownership also points at another tenant's ID range. Home-directory permissions stop access across tenants, but the files are still owned by the wrong IDs.
Related
#787 (closed) fixed data ownership when the username changes between hosts. This is a different case: the same username, but container-owned sub-IDs that need an offset instead of a flatten.
(Container IDs are relative. The ranges aren't.)
Tier-3 declared at filing:
--why multi-file— GH#631 tier gate. Declared fix size:90lines vs 28-line body — size gate (operator directive 2026-07-29). Owner:scripts/lib/userTransfer/postSetup.phpin MagnaCapax/PMSS — owner gate.