Repository navigation
Conversation
89c80ea to
5d658c5
Compare
|
The fix looks correct to me, but @thaJeztah's concern is valid: there is one caller in moby that depends on the current behavior. The classic builder's I've opened moby/moby#53859 to fix that on the moby side. Other A few nits for this PR:
|
| rgid, err = toHost(gid, i.GIDMaps) | ||
| ruid, err := toHost(uid, i.UIDMaps) | ||
| if err != nil { | ||
| return ruid, 0, err |
There was a problem hiding this comment.
Better to return -1, -1, err here, consistent with ToContainer (and with what was suggested in #241).
| // ToHost returns the host UID and GID for the container uid, gid. | ||
| // Remapping is only performed if the ids aren't already the remapped root ids | ||
| // | ||
| // Every container id is translated through the id map. The container root | ||
| // (id 0) maps to the host remapped-root base (the ParentID of the map entry | ||
| // covering id 0), because toHost(0) resolves to that base--which is exactly | ||
| // what [IdentityMapping.RootPair] returns, so no special case is needed. | ||
| // An empty (nil) mapping is treated as identity. | ||
| // | ||
| // Callers must pass container-namespace ids. ToHost does not treat an id as | ||
| // "already remapped" based on the host remapped-root value: doing so would | ||
| // incorrectly leave a non-root container uid that happens to equal the host | ||
| // remapped-root base unmapped (and therefore owned by the remapped root inside | ||
| // the container). |
There was a problem hiding this comment.
This does not need to be that long. Something like
| // ToHost returns the host UID and GID for the container uid, gid. | |
| // Remapping is only performed if the ids aren't already the remapped root ids | |
| // | |
| // Every container id is translated through the id map. The container root | |
| // (id 0) maps to the host remapped-root base (the ParentID of the map entry | |
| // covering id 0), because toHost(0) resolves to that base--which is exactly | |
| // what [IdentityMapping.RootPair] returns, so no special case is needed. | |
| // An empty (nil) mapping is treated as identity. | |
| // | |
| // Callers must pass container-namespace ids. ToHost does not treat an id as | |
| // "already remapped" based on the host remapped-root value: doing so would | |
| // incorrectly leave a non-root container uid that happens to equal the host | |
| // remapped-root base unmapped (and therefore owned by the remapped root inside | |
| // the container). | |
| // ToHost returns the host UID and GID for the container uid and gid. | |
| // An empty mapping is treated as identity. |
would do just fine.
Something like this: func TestToHostRemappedRoot(t *testing.T) {
// ToHost expects container IDs. A host ID, such as the remapped root
// one, is not treated as already remapped, and so it is either mapped
// again (if it is in the container range) or results in an error.
idMap := []IDMap{{ID: 0, ParentID: 100000, Count: 65536}}
m := IdentityMapping{UIDMaps: idMap, GIDMaps: idMap}
ruid, rgid := m.RootPair()
uid, gid, err := m.ToHost(ruid, rgid)
if err == nil {
t.Fatalf("expected an error, got uid %d, gid %d", uid, gid)
}
} |
…se unmapped ToHost translated container ids to host ids, but special-cased the container root by skipping the id-map lookup whenever the container uid matched the host remapped-root base (RootPair). That comparison was wrong: the input is a container-namespace id, so it must never be compared against a host value. When the subuid/subgid base is below 65536 (e.g. 'rootless:1000:65536', used to align dind-rootless container uids), a non-root container uid that equals the base (e.g. 1000) was incorrectly left unmapped and therefore appeared as root inside the container. Drop the special case entirely and always translate every id through toHost. toHost(0) already resolves to the host remapped-root base (which is exactly what RootPair returns), so the container root is handled correctly without any guard. An empty (nil) mapping is treated as identity. Add a regression test covering standard, low-base, and empty mappings. Signed-off-by: okhowang(王沛文) <okhowang@tencent.com>
5d658c5 to
66f3976
Compare
kolyshkin
left a comment
There was a problem hiding this comment.
AFAIK this one is ready; PTAL @vvoland @thaJeztah
|
(of course, user release notes should come with an explanation about this one) |
|
Any suggestions for the release notes? |
| uid, gid, err := m.ToHost(ruid, rgid) | ||
| if err == nil { | ||
| t.Fatalf("expected an error, got uid %d, gid %d", uid, gid) | ||
| } |
There was a problem hiding this comment.
Should we assert the -1 values returned here?
There was a problem hiding this comment.
Yes, makes sense to add something like
if uid != -1 || gid != -1 {
t.Errorf("expected -1, -1 on error, got %d, %d", uid, gid)
}There was a problem hiding this comment.
...or not -- the caller is usually supposed to ignore values if a function returns an error.
| // ToHost returns the host UID and GID for the container uid and gid. | ||
| // An empty mapping is treated as identity. | ||
| func (i IdentityMapping) ToHost(uid, gid int) (int, int, error) { |
There was a problem hiding this comment.
Perhaps the -1 is worth calling out. Of course, a caller shouldn't consume returned values if there's an error, but just to emphasise that they really shouldn't? 😅
There was a problem hiding this comment.
I think we should assert it (in the test, see your previous comment) but not document it, as it's minor implementation detail, can be changed, and is not always true (when git mapping fails we we return ruid, -1, err now). So I would leave the doc as is.
I can make a release and write it, but something like this should do: |
Maybe also say that the only user of such weird feature was moby, and add a link to moby/moby#53859 |
fixes #241
ToHost translated container ids to host ids, but special-cased the
container root by skipping the id-map lookup whenever the container uid
matched the host remapped-root base (RootPair). That comparison was
wrong: the input is a container-namespace id, so it must never be
compared against a host value.
When the subuid/subgid base is below 65536 (e.g. 'rootless:1000:65536',
used to align dind-rootless container uids), a non-root container uid
that equals the base (e.g. 1000) was incorrectly left unmapped and
therefore appeared as root inside the container.
Drop the special case entirely and always translate every id through
toHost. toHost(0) already resolves to the host remapped-root base (which
is exactly what RootPair returns), so the container root is handled
correctly without any guard. An empty (nil) mapping is treated as
identity.
Add a regression test covering standard, low-base, and empty mappings.