Remove local fork of archive/tar package#43185
Conversation
|
I am also experimenting with having a separate binary for the chrootarchive reexecs as I described in #42402 (comment) which has both compile-time and runtime assertions to make really, really sure that it can only be built as a static binary without cgo. I have opened a draft PR for your consideration: #43186 |
18f442c to
551d446
Compare
551d446 to
74fe7a2
Compare
74fe7a2 to
b711063
Compare
b711063 to
bd97462
Compare
kolyshkin
left a comment
There was a problem hiding this comment.
LGTM
In view of this, perhaps it might make sense to close golang/go#50102 since we can achieve a similar thing without too much effort. @corhere would you do the honors to describe your solution and propose to close that one?
I think the proposal should be left open. This PR implements the suggestion from golang/go#50102 (comment) and reimplements the loading of (some of) the other filesystem info in userspace, so to speak. The proposed |
|
The problems with this is that it:
If we are ok with these risks then the implementation SGTM |
The only assumption being made is that the stdlib
We need to make sure that the new function is always used in code executed inside a chroot. Or to be more precise, we need to make sure that |
bd97462 to
ddc2000
Compare
|
Looks like gosec linting is complaining. We had quite some "false positives" on that linter, but we should check. We updated the linter to a new version Yesterday; if |
|
@corhere thanks again! We discussed this PR in Yesterday's maintainers meeting with @tonistiigi @samuelkarp @cpuguy83 @tianon and @crazy-max and from that discussion, I think we're good to move forward with this change. Could you rebase the PR (and have a look at the linter failures? #43185 (comment))
It probably doesn't hurt to have a test (just to be sure); that said (as mentioned) there may be other areas that could potentially do a
I was actually trying to recall why we added back the vendored Let me know if you need help! I hope I didn't miss anything (if I did, can you add details, @tonistiigi ?) |
On it. Taking a peek under the hood at the gosec linter, it indiscriminately flags any
Thankfully it was documented at the time! Tl;dr it was a workaround required specifically for static builds on Go 1.10 with |
A copy of Go's archive/tar packge was vendored with a patch applied to mitigate CVE-2019-14271. Vendoring standard library packages is not supported by Go in module-aware mode, which is getting in the way of maintenance. A different approach to mitigate the vulnerability is needed which does not involve vendoring parts of the standard library. glibc implements name service lookups such as users, groups and DNS using a scheme known as Name Service Switch. The services are implemented as modules, shared libraries which glibc dynamically links into the process the first time a function requiring the module is called. This is the crux of the vulnerability: if a process linked against glibc chroots, then calls one of the functions implemented with NSS for the first time, glibc may load NSS modules out of the chrooted filesystem. The API underlying the `docker cp` command is implemented by forking a new process which chroots into the container's rootfs and writes a tar stream of files from the container over standard output. It utilizes the Go standard library's archive/tar package to write the tar stream. It makes use of the tar.FileInfoHeader function to construct a tar.Header value from an fs.FileInfo value. In modern versions of Go on *nix platforms, FileInfoHeader will attempt to resolve the file's UID and GID to their respective user and group names by calling the os/user functions LookupId and LookupGroupId. The cgo implementation of os/user on *nix performs lookups by calling the corresponding libc functions. So when linked against glibc, calls to tar.FileInfoHeader after the process has chrooted into the container's rootfs can have the side effect of loading NSS modules from the container! Without any mitigations, a malicious container image author can trivially get arbitrary code execution by leveraging this vulnerability and escape the chroot (which is not a sandbox) into the host. Mitigate the vulnerability without patching or forking archive/tar by hiding the OS-dependent file info from tar.FileInfoHeader which it needs to perform the lookups. Without that information available it falls back to populating the tar.Header with only the information obtainable directly from the FileInfo value without making any calls into os/user. Fixes moby#42402 Signed-off-by: Cory Snider <[email protected]>
|
🤦 ah, good find! I didn't look there!
If my memory serves me well, the |
ddc2000 to
c98286a
Compare
The recently-upgraded gosec linter has a rule for archive extraction code which may be vulnerable to directory traversal attacks, a.k.a. Zip Slip. Gosec's detection is unfortunately prone to false positives, however: it flags any filepath.Join call with an argument derived from a tar.Header value, irrespective of whether the resultant path is used for filesystem operations or if directory traversal attacks are guarded against. All of the lint errors reported by gosec appear to be false positives. Signed-off-by: Cory Snider <[email protected]>
c98286a to
833139f
Compare
|
@tonistiigi @cpuguy83 PTAL |
|
I'll go ahead and bring this one in. I assume @tonistiigi is good with this (otherwise let me know) |
- What I did
Removed the local fork of
archive/tarwhile retaining all existing mitigations of CVE-2019-14271. Fixes #42402- How I did it
I hid the value containing the file's UID and GID from the
tar.FileInfoHeaderfunction so that it would elide callinguser.LookupId, analogous to the proposal in golang/go#50102.- How to verify it
make dynbinarydockerd's debug logs) and create a container from that imagedocker cpfrom the container and observe that the exploit code has been executed- Description for the changelog
Remove local fork of
archive/tarpackage- A picture of a cute animal (not mandatory but encouraged)