full diff: https://github.com/opencontainers/selinux/compare/v1.12.0...v1.13.0 Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
23 KiB
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog and this project adheres to Semantic Versioning.
Unreleased
0.6.0 - 2025-11-03
By the Power of Greyskull!
While quite small code-wise, this release marks a very key point in the development of filepath-securejoin.
filepath-securejoin was originally intended (back in 2017) to simply be a
single-purpose library that would take some common code used in container
runtimes (specifically, Docker's FollowSymlinksInScope) and make it more
general-purpose (with the eventual goals of it ending up in the Go stdlib).
Of course, I quickly discovered that this problem was actually far more
complicated to solve when dealing with racing attackers, which lead to me
developing openat2(2) and libpathrs. I had originally planned for
libpathrs to completely replace filepath-securejoin "once it was ready" but in
the interim we needed to fix several race attacks in runc as part of security
advisories. Obviously we couldn't require the usage of a pre-0.1 Rust library
in runc so it was necessary to port bits of libpathrs into filepath-securejoin.
(Ironically the first prototypes of libpathrs were originally written in Go and
then rewritten to Rust, so the code in filepath-securejoin is actually Go code
that was rewritten to Rust then re-rewritten to Go.)
It then became clear that pure-Go libraries will likely not be willing to
require CGo for all of their builds, so it was necessary to accept that
filepath-securejoin will need to stay. As such, in v0.5.0 we provided more
pure-Go implementations of features from libpathrs but moved them into
pathrs-lite subpackage to clarify what purpose these helpers serve.
This release finally closes the loop and makes it so that pathrs-lite can
transparently use libpathrs (via a libpathrs build-tag). This means that
upstream libraries can use the pure Go version if they prefer, but downstreams
(either downstream library users or even downstream distributions) are able to
migrate to libpathrs for all usages of pathrs-lite in an entire Go binary.
I should make it clear that I do not plan to port the rest of libpathrs to Go,
as I do not wish to maintain two copies of the same codebase. pathrs-lite
already provides the core essentials necessary to operate on paths safely for
most modern systems. Users who want additional hardening or more ergonomic APIs
are free to use cyphar.com/go-pathrs (libpathrs's Go bindings).
Breaking
- The deprecated
MkdirAll,MkdirAllHandle,OpenInRoot,OpenatInRootandReopenwrappers have been removed. Please switch to usingpathrs-litedirectly.
Added
pathrs-litenow has support for using libpathrs as a backend. This is opt-in and can be enabled at build time with thelibpathrsbuild tag. The intention is to allow for downstream libraries and other projects to make use of the pure-Gogithub.com/cyphar/filepath-securejoin/pathrs-litepackage and distributors can then opt-in to usinglibpathrsfor the entire binary if they wish.
0.5.1 - 2025-10-31
Spooky scary skeletons send shivers down your spine!
Changed
-
openat2can return-EAGAINif it detects a possible attack in certain scenarios (namely if there was a rename or mount while walking a path with a..component). While this is necessary to avoid a denial-of-service in the kernel, it does require retry loops in userspace.In previous versions,
pathrs-litewould retryopenat232 times before returning an error, but we've received user reports that this limit can be hit on systems with very heavy load. In some synthetic benchmarks (testing the worst-case of an attacker doing renames in a tight loop on every core of a 16-core machine) we managed to get a ~3% failure rate in runc. We have improved this situation in two ways:-
We have now increased this limit to 128, which should be good enough for most use-cases without becoming a denial-of-service vector (the number of syscalls called by the
O_PATHresolver in a typical case is within the same ballpark). The same benchmarks show a failure rate of ~0.12% which (while not zero) is probably sufficient for most users. -
In addition, we now return a
unix.EAGAINerror that is bubbled up and can be detected by callers. This means that callers with stricter requirements to avoid spurious errors can choose to do their own infiniteEAGAINretry loop (though we would strongly recommend users use time-based deadlines in such retry loops to avoid potentially unbounded denials-of-service).
-
0.5.0 - 2025-09-26
Let the past die. Kill it if you have to.
Note
: With this release, some parts of
github.com/cyphar/filepath-securejoinare now licensed under the Mozilla Public License (version 2). Please see COPYING.md as well as the the license header in each file for more details.
Breaking
-
The new API introduced in the 0.3.0 release has been moved to a new subpackage called
pathrs-lite. This was primarily done to better indicate the split between the new and old APIs, as well as indicate to users the purpose of this subpackage (it is a less complete version of libpathrs).We have added some wrappers to the top-level package to ease the transition, but those are deprecated and will be removed in the next minor release of filepath-securejoin. Users should update their import paths.
This new subpackage has also been relicensed under the Mozilla Public License (version 2), please see COPYING.md for more details.
Added
-
Most of the key bits the safe
procfsAPI have now been exported and are available ingithub.com/cyphar/filepath-securejoin/pathrs-lite/procfs. At the moment this primarily consists of a newprocfs.HandleAPI:-
OpenProcRootreturns a new handle to/proc, endeavouring to make it safe if possible (subset=pidto protect against mistaken write attacks and leaks, as well as usingfsopen(2)to avoid racing mount attacks).OpenUnsafeProcRootreturns a handle without attempting to create one withsubset=pid, which makes it more dangerous to leak. Most users should useOpenProcRoot(even if you need to useProcRootas the base of an operation, as filepath-securejoin will internally open a handle when necessary). -
The
(*procfs.Handle).Open*family of methods lets you get a safeO_PATHhandle to subpaths within/procfor certain subpaths.For
OpenThreadSelf, the returnedProcThreadSelfCloserneeds to be called after you completely finish using the handle (this is necessary because Go is multi-threaded andProcThreadSelfreferences/proc/thread-selfwhich may disappear if we do notruntime.LockOSThread--ProcThreadSelfCloseris currently equivalent toruntime.UnlockOSThread).Note that you cannot open any
procfssymlinks (most notably magic-links) using this API. At the moment, filepath-securejoin does not support this feature (but libpathrs does). -
ProcSelfFdReadlinklets you get the in-kernel path representation of a file descriptor (thinkreadlink("/proc/self/fd/...")), except that we verify that there aren't any tricky overmounts that could fool the process.Please be aware that the returned string is simply a snapshot at that particular moment, and an attacker could move the file being pointed to. In addition, complex namespace configurations could result in non-sensical or confusing paths to be returned. The value received from this function should only be used as secondary verification of some security property, not as proof that a particular handle has a particular path.
The procfs handle used internally by the API is the same as the rest of
filepath-securejoin(for privileged programs this is usually a private in-processprocfsinstance created withfsopen(2)).As before, this is intended as a stop-gap before users migrate to libpathrs, which provides a far more extensive safe
procfsAPI and is generally more robust. -
-
Previously, the hardened procfs implementation (used internally within
ReopenandOpen(at)InRoot) only protected against overmount attacks on systems withopenat2(2)(Linux 5.6) or systems withfsopen(2)oropen_tree(2)(Linux 5.2) and programs with privileges to use them (with some caveats about locked mounts that probably affect very few users). For other users, an attacker with the ability to create malicious mounts (on most systems, a sysadmin) could trick you into operating on files you didn't expect. This attack only really makes sense in the context of container runtime implementations.This was considered a reasonable trade-off, as the long-term intention was to get all users to just switch to libpathrs if they wanted to use the safe
procfsAPI (which had more extensive protections, and is what these new protections infilepath-securejoinare based on). However, as the API is now being exported it seems unwise to advertise the API as "safe" if we do not protect against known attacks.The procfs API is now more protected against attackers on systems lacking the aforementioned protections. However, the most comprehensive of these protections effectively rely on
statx(STATX_MNT_ID)(Linux 5.8). On older kernel versions, there is no effective protection (there is some minimal protection against non-procfsfilesystem components but a sufficiently clever attacker can work around those). In addition,STATX_MNT_IDis vulnerable to mount ID reuse attacks by sufficiently motivated and privileged attackers -- this problem is mitigated withSTATX_MNT_ID_UNIQUE(Linux 6.8) but that raises the minimum kernel version for more protection.The fact that these protections are quite limited despite needing a fair bit of extra code to handle was one of the primary reasons we did not initially implement this in
filepath-securejoin(libpathrs supports all of this, of course).
Fixed
- RHEL 8 kernels have backports of
fsopen(2)but in some testing we've found that it has very bad (and very difficult to debug) performance issues, and so we will explicitly refuse to usefsopen(2)if the running kernel version is pre-5.2 and will instead fallback toopen("/proc").
0.4.1 - 2025-01-28
Fixed
- The restrictions added for
rootpaths passed toSecureJoinin 0.4.0 was found to be too strict and caused some regressions when folks tried to update, so this restriction has been relaxed to only return an error if the path contains a..component. We still recommend users usefilepath.Clean(and evenfilepath.EvalSymlinks) on therootpath they are using, but at least you will no longer be punished for "trivial" unclean paths.
0.4.0 - 2025-01-13
Breaking
-
SecureJoin(VFS)will now return an error if the providedrootis not afilepath.Clean'd path.While it is ultimately the responsibility of the caller to ensure the root is a safe path to use, passing a path like
/symlink/..as a root would result in theSecureJoin'd path being placed in/even though/symlink/..might be a different directory, and so we should more strongly discourage such usage.All major users of
securejoin.SecureJoinalready ensure that the paths they provide are safe (and this is ultimately a question of user error), but removing this foot-gun is probably a good idea. Of course, this is necessarily a breaking API change (though we expect no real users to be affected by it).Thanks to Erik Sjölund, who initially reported this issue as a possible security issue.
-
MkdirAllandMkdirHandlenow take anos.FileMode-style mode argument instead of a rawunix.S_*-style mode argument, which may cause compile-time type errors depending on how you usefilepath-securejoin. For most users, there will be no change in behaviour aside from the type change (as the bottom0o777bits are the same in both formats, and most users are probably only using those bits).However, if you were using
unix.S_ISVTXto set the sticky bit withMkdirAll(Handle)you will need to switch toos.ModeStickyotherwise you will get a runtime error with this update. In addition, the error message you will get from passingunix.S_ISUIDandunix.S_ISGIDwill be different as they are treated as invalid bits now (note that previously passing said bits was also an error).
0.3.6 - 2024-12-17
Compatibility
-
The minimum Go version requirement for
filepath-securejoinis now Go 1.18 (we use generics internally).For reference,
filepath-securejoin@v0.3.0somewhat-arbitrarily bumped the Go version requirement to 1.21.While we did make some use of Go 1.21 stdlib features (and in principle Go versions <= 1.21 are no longer even supported by upstream anymore), some downstreams have complained that the version bump has meant that they have to do workarounds when backporting fixes that use the new
filepath-securejoinAPI onto old branches. This is not an ideal situation, but since using this library is probably better for most downstreams than a hand-rolled workaround, we now have compatibility shims that allow us to build on older Go versions. -
Lower minimum version requirement for
golang.org/x/systov0.18.0(we need the wrappers forfsconfig(2)), which should also make backporting patches to older branches easier.
0.3.5 - 2024-12-06
Fixed
MkdirAllwill now no longer return anEEXISTerror if two racing processes are creating the same directory. We will still verify that the path is a directory, but this will avoid spurious errors when multiple threads or programs are trying toMkdirAllthe same path. opencontainers/runc#4543
0.3.4 - 2024-10-09
Fixed
- Previously, some testing mocks we had resulted in us doing
import "testing"in non-_test.gocode, which made some downstreams like Kubernetes unhappy. This has been fixed. (#32)
0.3.3 - 2024-09-30
Fixed
- The mode and owner verification logic in
MkdirAllhas been removed. This was originally intended to protect against some theoretical attacks but upon further consideration these protections don't actually buy us anything and they were causing spurious errors with more complicated filesystem setups. - The "is the created directory empty" logic in
MkdirAllhas also been removed. This was not causing us issues yet, but some pseudofilesystems (such ascgroup) create non-empty directories and so this logic would've been wrong for such cases.
0.3.2 - 2024-09-13
Changed
- Passing the
S_ISUIDorS_ISGIDmodes toMkdirAllInRootwill now return an explicit error saying that those bits are ignored bymkdirat(2). In the past a different error was returned, but since the silent ignoring behaviour is codified in the man pages a more explicit error seems apt. While silently ignoring these bits would be the most compatible option, it could lead to users thinking their code sets these bits when it doesn't. Programs that need to deal with compatibility can mask the bits themselves. (#23, #25)
Fixed
- If a directory has
S_ISGIDset, then all child directories will haveS_ISGIDset when created and a different gid will be used for any inode created under the directory. Previously, the "expected owner and mode" validation insecurejoin.MkdirAlldid not correctly handle this. We now correctly handle this case. (#24, #25)
0.3.1 - 2024-07-23
Changed
-
By allowing
Open(at)InRootto opt-out of the extra work done byMkdirAllto do the necessary "partial lookups",Open(at)InRootnow does less work for both implementations (resulting in a many-fold decrease in the number of operations foropenat2, and a modest improvement for non-openat2) and is far more guaranteed to match the correctopenat2(RESOLVE_IN_ROOT)behaviour. -
We now use
readlinkat(fd, "")where possible. ForOpen(at)InRootthis effectively just means that we no longer risk getting spurious errors during rename races. However, for our hardened procfs handler, this in theory should prevent mount attacks from tricking us when doing magic-link readlinks (even when using the unsafe host/prochandle). UnfortunatelyReopenis still potentially vulnerable to those kinds of somewhat-esoteric attacks.Technically this will only work on post-2.6.39 kernels but it seems incredibly unlikely anyone is using
filepath-securejoinon a pre-2011 kernel.
Fixed
-
Several improvements were made to the errors returned by
Open(at)InRootandMkdirAllwhen dealing with invalid paths under the emulated (ie. non-openat2) implementation. Previously, some paths would return the wrong error (ENOENTwhen the last component was a non-directory), and other paths would be returned as though they were acceptable (trailing-slash components after a non-directory would be ignored byOpen(at)InRoot).These changes were done to match
openat2's behaviour and purely is a consistency fix (most users are going to be usingopenat2anyway).
0.3.0 - 2024-07-11
Added
-
A new set of
*os.File-based APIs have been added. These are adapted from libpathrs and we strongly suggest using them if possible (as they provide far more protection against attacks thanSecureJoin):-
Open(at)InRootresolves a path inside a rootfs and returns an*os.Filehandle to the path. Note that the handle returned is anO_PATHhandle, which cannot be used for reading or writing (as well as some other operations -- see open(2) for more details) -
Reopentakes anO_PATHfile handle and safely re-opens it to upgrade it to a regular handle. This can also be used with non-O_PATHhandles, butO_PATHis the most obvious application. -
MkdirAllis an implementation ofos.MkdirAllthat is safe to use to create a directory tree within a rootfs.
As these are new APIs, they may change in the future. However, they should be safe to start migrating to as we have extensive tests ensuring they behave correctly and are safe against various races and other attacks.
-
0.2.5 - 2024-05-03
Changed
- Some minor changes were made to how lexical components (like
..and.) are handled during path generation inSecureJoin. There is no behaviour change as a result of this fix (the resulting paths are the same).
Fixed
- The error returned when we hit a symlink loop now references the correct path. (#10)
0.2.4 - 2023-09-06
Security
- This release fixes a potential security issue in filepath-securejoin when used on Windows (GHSA-6xv5-86q9-7xr8, which could be used to generate paths outside of the provided rootfs in certain cases), as well as improving the overall behaviour of filepath-securejoin when dealing with Windows paths that contain volume names. Thanks to Paulo Gomes for discovering and fixing these issues.
Fixed
- Switch to GitHub Actions for CI so we can test on Windows as well as Linux and MacOS.
0.2.3 - 2021-06-04
Changed
- Switch to Go 1.13-style
%werror wrapping, letting us drop the dependency ongithub.com/pkg/errors.
0.2.2 - 2018-09-05
Changed
- Use
syscall.ELOOPas the base error for symlink loops, rather than our own (internal) error. This allows callers to more easily useerrors.Isto check for this case.
0.2.1 - 2018-09-05
Fixed
- Use our own
IsNotExistimplementation, which lets us handleENOTDIRproperly withinSecureJoin.
0.2.0 - 2017-07-19
We now have 100% test coverage!
Added
- Add a
SecureJoinVFSAPI that can be used for mocking (as we do in our new tests) or for implementing custom handling of lookup operations (such as for rootless containers, where work is necessary to access directories with weird modes because we don't haveCAP_DAC_READ_SEARCHorCAP_DAC_OVERRIDE).
0.1.0 - 2017-07-19
This is our first release of github.com/cyphar/filepath-securejoin,
containing a full implementation with a coverage of 93.5% (the only missing
cases are the error cases, which are hard to mocktest at the moment).