Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

[userns-remap] Bug: ToHost incorrectly maps non-root container UID to root when it collides with RootPair

オープン 初心者向け
#241 コメント 3 件 リアクション 0 件 担当者 0 名 GitHub で見る

@okhowang がすでに取り組んでいます。

2026年8月4日 から。

  • #242 @okhowang による — オープン

評価

難易度
2/5
見積もり時間
1〜3時間
初心者へのやさしさ
76/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
静か
技術スタック
go

調査の方向性

moby/sys/user/idtools.go の IdentityMapping.ToHost() から始め、RootPair()、UIDMaps、GIDMaps がどのように使用されているかを調べます。1000 から始まる subuid 範囲で userns-remap の再現を確認します。コンテナの UID/GID 1000 が適切な非 root のホスト ID にマッピングされ、コンテナの root は引き続き remap された root ID にマッピングされれば完了です。

索引モデルが issue の本文から書いたものです。

説明

Summary

When userns-remap is enabled, the ToHost method in moby/sys/user/idtools.go incorrectly skips UID/GID remapping for non-root users whose container UID happens to equal the host-side root UID returned by RootPair(). This causes files owned by such users to be chown'd to the remapped root UID on the host, and thus appear as root:root inside the container.

Affected Component

github.com/moby/sys/user — IdentityMapping.ToHost() method

Steps to Reproduce

  1. Configure Docker with userns-remap and /etc/subuid:

    rootless:1000:65536
    
  2. Pull an image that contains a non-root user with UID=1000 (e.g., mcr.microsoft.com/playwright:v1.55.1-jammy which creates a pwuser with UID 1000 via adduser).

  3. Run the image:

    docker run -it --rm mcr.microsoft.com/playwright:v1.55.1-jammy ls -l /home
    

Expected Behavior

drwxr-x--- 2 pwuser pwuser 57 Sep 23  2025 pwuser

Actual Behavior

drwxr-x--- 2 root root 73 Sep 23  2025 pwuser

Root Cause

In ToHost() (line 109-126 of idtools.go):

func (i IdentityMapping) ToHost(uid, gid int) (int, int, error) {
    var err error
    ruid, rgid := i.RootPair()

    if uid != ruid {
        ruid, err = toHost(uid, i.UIDMaps)
        if err != nil {
            return ruid, rgid, err
        }
    }

    if gid != rgid {
        rgid, err = toHost(gid, i.GIDMaps)
    }
    return ruid, rgid, err
}

The method assumes that if uid == RootPair().UID, then uid must be the container root (UID 0) that has already been mapped, and skips the toHost() call as an optimization. However, this assumption is incorrect when the subuid ParentID coincides with a non-root container UID.

For example, with subuid = rootless:1000:65536:

  • RootPair() returns (1000, 1000) (host-side root)
  • Container root (UID 0) maps to host UID 1000
  • A non-root user in the image with UID 1000 should map to host UID 2000

But ToHost(1000, 1000) sees uid == ruid (1000 == 1000), skips the toHost() call, and returns (1000, 1000) — the remapped root UID. The file gets chown'd to 1000:1000 on the host, which is the same as the remapped root, and appears as root:root inside the container.

This bug is especially common because:

  • UID=1000 is the default first non-root user on many Linux distributions (Ubuntu/Debian)
  • subuid ranges often start at 1000 when configured for rootless Docker
  • Many Docker images create a user with UID=1000

Suggested Fix

The ToHost method should always perform the toHost() mapping, and only skip it for the actual container root (UID 0) — not for any arbitrary UID that happens to collide with RootPair().

Remove the optimization entirely. The performance impact is negligible:

func (i IdentityMapping) ToHost(uid, gid int) (int, int, error) {
    ruid, err := toHost(uid, i.UIDMaps)
    if err != nil {
        return -1, -1, err
    }
    rgid, err := toHost(gid, i.GIDMaps)
    return ruid, rgid, err
}
主要言語
Go
スター
102
フォーク
62
平均マージ
6分
マージ済み PR(30日)
2

環境構築

このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

moby/sys のほかの issue

moby/sys の issue をすべて見る

似ている issue

Go の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。