New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
c8d: Handle userns properly #46375
c8d: Handle userns properly #46375
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this break diff
/commit
(all files are chowned and treated as modified)?
EDIT: Ah, it shouldn't because it creates 2 snapshots on top of the base image.
Wondering if we should also adjust calculateSnapshotParentUsage
to account for that?
Hm... With overlayfs this would mean the reported size is 2x if metacopy is not enabled, which is true since chown would copy the files. |
Looking forward to idmapped mounts, which probably would make these kind of things a lot easier. |
return err | ||
} | ||
|
||
if err := remapRootFS(ctx, mounts, rootPair); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does this only use the rootPair and not the full mapping?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RootPair
is the remapped root uid/gid
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comparing to containerd's implementation which increments over the full userns range https://github.com/containerd/containerd/blob/v1.7.5/container_opts_unix.go#L104
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah right!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed thanks
874e755
to
f00f041
Compare
return fmt.Errorf("cannot get underlying data for %s", path) | ||
} | ||
|
||
return os.Lchown(path, int(stat.Uid)+idpair.UID, int(stat.Gid)+idpair.GID) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be clearer to use i.idMapping.ToContainer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok switched to using ToHost
, ToContainer
is the other way around and was giving me errors. With ToHost
the uid/gid is the right one inside the container. Might be a good idea to add a test for this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a test for this
17fc2bb
to
69cdb1c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall SGTM; left some nits though
} | ||
|
||
if err := i.remapRootFS(ctx, mounts); err != nil { | ||
snapshotter.Remove(ctx, usernsID) //nolint: errcheck |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we log this error instead of ignoring it?
69cdb1c
to
072b120
Compare
072b120
to
6c7d022
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, but spotted some minor issues; let me know if you think it's worth fixing those before merging
daemon/snapshotter/mount.go
Outdated
dirID := idtools.Identity{ | ||
UID: idtools.CurrentIdentity().UID, | ||
GID: root.GID, | ||
} | ||
if err := idtools.MkdirAllAndChown(path.Dir(target), 0o710, dirID); err != nil { | ||
return "", err | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you change this to use filepath.Dir()
?
I also thought for a long time that path
is for /
(and filepath
only if you need cross-platform); https://github.com/golang/go/blob/2e7e1d2e8d96ddf2aff00feb645cfa94bc9e4786/src/path/path.go#L5-L6
Package path implements utility routines for manipulating slash-separated paths.
But a second paragraph was added at some point! golang/go@7465bfb
To manipulate operating system paths, use the path/filepath package.
While at it, perhaps also inline that dirID
var (I see the other implementations also have the above issues, but we can look at those separately)
dirID := idtools.Identity{ | |
UID: idtools.CurrentIdentity().UID, | |
GID: root.GID, | |
} | |
if err := idtools.MkdirAllAndChown(path.Dir(target), 0o710, dirID); err != nil { | |
return "", err | |
} | |
if err := idtools.MkdirAllAndChown(filepath.Dir(target), 0o710, idtools.Identity{ | |
UID: idtools.CurrentIdentity().UID, | |
GID: root.GID, | |
}); err != nil { | |
return "", err | |
} |
fid := idtools.Identity{UID: int(stat.Uid), GID: int(stat.Gid)} | ||
ids, err := i.idMapping.ToHost(fid) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps inline this one as well;
fid := idtools.Identity{UID: int(stat.Uid), GID: int(stat.Gid)} | |
ids, err := i.idMapping.ToHost(fid) | |
ids, err := i.idMapping.ToHost(idtools.Identity{UID: int(stat.Uid), GID: int(stat.Gid)} |
daemon/snapshotter/mount.go
Outdated
@@ -67,7 +67,6 @@ type refCountMounter struct { | |||
|
|||
func (m *refCountMounter) Mount(mounts []mount.Mount, containerID string) (target string, retErr error) { | |||
target = filepath.Join(m.home, mountsDir, m.snapshotter, containerID) | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unrelated change? 🙈
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thought the code looked better that way
29b6218
to
2a793cf
Compare
daemon/snapshotter/mount.go
Outdated
if err := idtools.MkdirAllAndChown(filepath.Dir(target), 0o710, idtools.Identity{ | ||
UID: idtools.CurrentIdentity().UID, | ||
GID: root.GID, | ||
} | ||
if err := idtools.MkdirAllAndChown(path.Dir(target), 0o710, dirID); err != nil { | ||
}); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🙈 looks like your touch-up ended in the second commit instead of the first 🙈
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤦 forgot I had two commits in this PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed, thanks
Graph drivers create the parent directory with rootPair().GID:CurrentIdentity().UID owner. This change brings these in line Signed-off-by: Djordje Lukic <djordje.lukic@docker.com>
2a793cf
to
87a8f00
Compare
daemon/containerd/image_snapshot.go
Outdated
if i.idMapping.Empty() { | ||
_, err = snapshotter.Prepare(ctx, id, parentSnapshot) | ||
return err | ||
} | ||
|
||
return i.remapSnapshot(ctx, snapshotter, id, parentSnapshot, lease) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ohman, changes look good, but I'm just looking at this part again, and this could be a potential foot-gun if either a linter, or some "helpful" contribution fixes "unhandled error" here;
if i.idMapping.Empty() { | |
_, err = snapshotter.Prepare(ctx, id, parentSnapshot) | |
return err | |
} | |
return i.remapSnapshot(ctx, snapshotter, id, parentSnapshot, lease) | |
if i.idMapping.Empty() { | |
if _, err := snapshotter.Prepare(ctx, id, parentSnapshot); err != nil { | |
return err | |
} | |
} | |
return i.remapSnapshot(ctx, snapshotter, id, parentSnapshot, lease) |
The above would break this code (which would hopefully be caught in CI.
Do you think it's worth to be defensive here, and to reverse the order?
if i.idMapping.Empty() { | |
_, err = snapshotter.Prepare(ctx, id, parentSnapshot) | |
return err | |
} | |
return i.remapSnapshot(ctx, snapshotter, id, parentSnapshot, lease) | |
if !i.idMapping.Empty() { | |
return i.remapSnapshot(ctx, snapshotter, id, parentSnapshot, lease) | |
} | |
_, err = snapshotter.Prepare(ctx, id, parentSnapshot) | |
return err |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure I can reverse the order 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
If the daemon is run with --userns-remap we need to chown the prepared snapshot Signed-off-by: Djordje Lukic <djordje.lukic@docker.com>
87a8f00
to
0313544
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
- What I did
Remapped the GID/UID when a snapshot is created if the daemon is started with
--userns-remap
. This implementation differs from the graph driver one slightly. With graph drivers the files/directories are chown'ed when the image is downloaded and the contents extracted. Basically, with graph drivers we remap on pull and with containerd we remap on run.Note: with this change builds still fail, my guess is because buildkit still mounts the roots in normal mode, I'll continue looking but I think we can merge this one, I am pretty sure some changes in buildkit are needed:
- How I did it
- How to verify it
TestDaemonUserNamespaceRootSetting
should not fail any more$ make DOCKER_GRAPHDRIVER=overlayfs TEST_FILTER=TestDaemonUserNamespaceRootSetting TEST_INTEGRATION_USE_SNAPSHOTTER=1 test-integration
- Description for the changelog
- A picture of a cute animal (not mandatory but encouraged)